Implement Rhombus 2D primitive. (#13501)
# Objective - Create a new 2D primitive, Rhombus, also knows as "Diamond Shape" - Simplify the creation and handling of isometric projections - Extend Bevy's arsenal of 2D primitives ## Testing - New unit tests created in bevy_math/ primitives and bev_math/ bounding - Tested translations, rotations, wireframe, bounding sphere, aabb and creation parameters --------- Co-authored-by: Luís Figueiredo <luispcfigueiredo@tecnico.ulisboa.pt>
This commit is contained in:
parent
037f37e4d6
commit
7d843e0c08
@ -7,7 +7,7 @@ use super::helpers::*;
|
|||||||
use bevy_color::Color;
|
use bevy_color::Color;
|
||||||
use bevy_math::primitives::{
|
use bevy_math::primitives::{
|
||||||
Annulus, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon,
|
Annulus, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon,
|
||||||
Polyline2d, Primitive2d, Rectangle, RegularPolygon, Segment2d, Triangle2d,
|
Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d,
|
||||||
};
|
};
|
||||||
use bevy_math::{Dir2, Mat2, Vec2};
|
use bevy_math::{Dir2, Mat2, Vec2};
|
||||||
|
|
||||||
@ -138,6 +138,38 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rhombus 2d
|
||||||
|
|
||||||
|
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Rhombus> for Gizmos<'w, 's, Config, Clear>
|
||||||
|
where
|
||||||
|
Config: GizmoConfigGroup,
|
||||||
|
Clear: 'static + Send + Sync,
|
||||||
|
{
|
||||||
|
type Output<'a> = () where Self: 'a;
|
||||||
|
|
||||||
|
fn primitive_2d(
|
||||||
|
&mut self,
|
||||||
|
primitive: Rhombus,
|
||||||
|
position: Vec2,
|
||||||
|
angle: f32,
|
||||||
|
color: impl Into<Color>,
|
||||||
|
) -> Self::Output<'_> {
|
||||||
|
if !self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [a, b, c, d] =
|
||||||
|
[(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), (0.0, -1.0)].map(|(sign_x, sign_y)| {
|
||||||
|
Vec2::new(
|
||||||
|
primitive.half_diagonals.x * sign_x,
|
||||||
|
primitive.half_diagonals.y * sign_y,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let positions = [a, b, c, d, a].map(rotate_then_translate_2d(angle, position));
|
||||||
|
self.linestrip_2d(positions, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// capsule 2d
|
// capsule 2d
|
||||||
|
|
||||||
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Capsule2d> for Gizmos<'w, 's, Config, Clear>
|
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Capsule2d> for Gizmos<'w, 's, Config, Clear>
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
primitives::{
|
primitives::{
|
||||||
Arc2d, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, CircularSegment,
|
Arc2d, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, CircularSegment,
|
||||||
Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Segment2d,
|
Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus,
|
||||||
Triangle2d,
|
Segment2d, Triangle2d,
|
||||||
},
|
},
|
||||||
Dir2, Mat2, Rotation2d, Vec2,
|
Dir2, Mat2, Rotation2d, Vec2,
|
||||||
};
|
};
|
||||||
@ -183,6 +183,33 @@ impl Bounded2d for Ellipse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Bounded2d for Rhombus {
|
||||||
|
fn aabb_2d(&self, translation: Vec2, rotation: impl Into<Rotation2d>) -> Aabb2d {
|
||||||
|
let rotation_mat = rotation.into();
|
||||||
|
|
||||||
|
let [rotated_x_half_diagonal, rotated_y_half_diagonal] = [
|
||||||
|
rotation_mat * Vec2::new(self.half_diagonals.x, 0.0),
|
||||||
|
rotation_mat * Vec2::new(0.0, self.half_diagonals.y),
|
||||||
|
];
|
||||||
|
let aabb_half_extent = rotated_x_half_diagonal
|
||||||
|
.abs()
|
||||||
|
.max(rotated_y_half_diagonal.abs());
|
||||||
|
|
||||||
|
Aabb2d {
|
||||||
|
min: -aabb_half_extent + translation,
|
||||||
|
max: aabb_half_extent + translation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_circle(
|
||||||
|
&self,
|
||||||
|
translation: Vec2,
|
||||||
|
_rotation: impl Into<Rotation2d>,
|
||||||
|
) -> BoundingCircle {
|
||||||
|
BoundingCircle::new(translation, self.circumradius())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Bounded2d for Plane2d {
|
impl Bounded2d for Plane2d {
|
||||||
fn aabb_2d(&self, translation: Vec2, rotation: impl Into<Rotation2d>) -> Aabb2d {
|
fn aabb_2d(&self, translation: Vec2, rotation: impl Into<Rotation2d>) -> Aabb2d {
|
||||||
let rotation: Rotation2d = rotation.into();
|
let rotation: Rotation2d = rotation.into();
|
||||||
@ -448,7 +475,7 @@ mod tests {
|
|||||||
bounding::Bounded2d,
|
bounding::Bounded2d,
|
||||||
primitives::{
|
primitives::{
|
||||||
Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, Plane2d,
|
Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, Plane2d,
|
||||||
Polygon, Polyline2d, Rectangle, RegularPolygon, Segment2d, Triangle2d,
|
Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d,
|
||||||
},
|
},
|
||||||
Dir2,
|
Dir2,
|
||||||
};
|
};
|
||||||
@ -769,6 +796,31 @@ mod tests {
|
|||||||
assert_eq!(bounding_circle.radius(), 1.0);
|
assert_eq!(bounding_circle.radius(), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rhombus() {
|
||||||
|
let rhombus = Rhombus::new(2.0, 1.0);
|
||||||
|
let translation = Vec2::new(2.0, 1.0);
|
||||||
|
|
||||||
|
let aabb = rhombus.aabb_2d(translation, std::f32::consts::FRAC_PI_4);
|
||||||
|
assert_eq!(aabb.min, Vec2::new(1.2928932, 0.29289323));
|
||||||
|
assert_eq!(aabb.max, Vec2::new(2.7071068, 1.7071068));
|
||||||
|
|
||||||
|
let bounding_circle = rhombus.bounding_circle(translation, std::f32::consts::FRAC_PI_4);
|
||||||
|
assert_eq!(bounding_circle.center, translation);
|
||||||
|
assert_eq!(bounding_circle.radius(), 1.0);
|
||||||
|
|
||||||
|
let rhombus = Rhombus::new(0.0, 0.0);
|
||||||
|
let translation = Vec2::new(0.0, 0.0);
|
||||||
|
|
||||||
|
let aabb = rhombus.aabb_2d(translation, std::f32::consts::FRAC_PI_4);
|
||||||
|
assert_eq!(aabb.min, Vec2::new(0.0, 0.0));
|
||||||
|
assert_eq!(aabb.max, Vec2::new(0.0, 0.0));
|
||||||
|
|
||||||
|
let bounding_circle = rhombus.bounding_circle(translation, std::f32::consts::FRAC_PI_4);
|
||||||
|
assert_eq!(bounding_circle.center, translation);
|
||||||
|
assert_eq!(bounding_circle.radius(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plane() {
|
fn plane() {
|
||||||
let translation = Vec2::new(2.0, 1.0);
|
let translation = Vec2::new(2.0, 1.0);
|
||||||
|
|||||||
@ -929,6 +929,132 @@ impl Measured2d for Annulus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A rhombus primitive, also known as a diamond shape.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[doc(alias = "Diamond")]
|
||||||
|
pub struct Rhombus {
|
||||||
|
/// Size of the horizontal and vertical diagonals of the rhombus
|
||||||
|
pub half_diagonals: Vec2,
|
||||||
|
}
|
||||||
|
impl Primitive2d for Rhombus {}
|
||||||
|
|
||||||
|
impl Default for Rhombus {
|
||||||
|
/// Returns the default [`Rhombus`] with a half-horizontal and half-vertical diagonal of `0.5`.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
half_diagonals: Vec2::splat(0.5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rhombus {
|
||||||
|
/// Create a new `Rhombus` from a vertical and horizontal diagonal sizes.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `Rhombus` from a side length with all inner angles equal.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_side(side: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
half_diagonals: Vec2::splat(side.hypot(side) / 2.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `Rhombus` from a given inradius with all inner angles equal.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_inradius(inradius: f32) -> Self {
|
||||||
|
let half_diagonal = inradius * 2.0 / std::f32::consts::SQRT_2;
|
||||||
|
Self {
|
||||||
|
half_diagonals: Vec2::new(half_diagonal, half_diagonal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the length of each side of the rhombus
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn side(&self) -> f32 {
|
||||||
|
self.half_diagonals.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the radius of the circumcircle on which all vertices
|
||||||
|
/// of the rhombus lie
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn circumradius(&self) -> f32 {
|
||||||
|
self.half_diagonals.x.max(self.half_diagonals.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the radius of the largest circle that can
|
||||||
|
/// be drawn within the rhombus
|
||||||
|
#[inline(always)]
|
||||||
|
#[doc(alias = "apothem")]
|
||||||
|
pub fn inradius(&self) -> f32 {
|
||||||
|
let side = self.side();
|
||||||
|
if side == 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
(self.half_diagonals.x * self.half_diagonals.y) / side
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the point on the rhombus that is closest to the given `point`.
|
||||||
|
///
|
||||||
|
/// If the point is outside the rhombus, the returned point will be on the perimeter of the rhombus.
|
||||||
|
/// Otherwise, it will be inside the rhombus and returned as is.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn closest_point(&self, point: Vec2) -> Vec2 {
|
||||||
|
// Fold the problem into the positive quadrant
|
||||||
|
let point_abs = point.abs();
|
||||||
|
let half_diagonals = self.half_diagonals.abs(); // to ensure correct sign
|
||||||
|
|
||||||
|
// The unnormalised normal vector perpendicular to the side of the rhombus
|
||||||
|
let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
|
||||||
|
let normal_magnitude_squared = normal.length_squared();
|
||||||
|
if normal_magnitude_squared == 0.0 {
|
||||||
|
return Vec2::ZERO; // A null Rhombus has only one point anyway.
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last term corresponds to normal.dot(rhombus_vertex)
|
||||||
|
let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
|
||||||
|
|
||||||
|
// The point is already inside so we simply return it.
|
||||||
|
if distance_unnormalised <= 0.0 {
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp the point to the edge
|
||||||
|
let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
|
||||||
|
|
||||||
|
// Clamp the point back to the positive quadrant
|
||||||
|
// if it's outside, it needs to be clamped to either vertex
|
||||||
|
if result.x <= 0.0 {
|
||||||
|
result = Vec2::new(0.0, half_diagonals.y);
|
||||||
|
} else if result.y <= 0.0 {
|
||||||
|
result = Vec2::new(half_diagonals.x, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we restore the signs of the original vector
|
||||||
|
result.copysign(point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Measured2d for Rhombus {
|
||||||
|
/// Get the area of the rhombus
|
||||||
|
#[inline(always)]
|
||||||
|
fn area(&self) -> f32 {
|
||||||
|
2.0 * self.half_diagonals.x * self.half_diagonals.y
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the perimeter of the rhombus
|
||||||
|
#[inline(always)]
|
||||||
|
fn perimeter(&self) -> f32 {
|
||||||
|
4.0 * self.side()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An unbounded plane in 2D space. It forms a separating surface through the origin,
|
/// An unbounded plane in 2D space. It forms a separating surface through the origin,
|
||||||
/// stretching infinitely far
|
/// stretching infinitely far
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
@ -1601,6 +1727,25 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rhombus_closest_point() {
|
||||||
|
let rhombus = Rhombus::new(2.0, 1.0);
|
||||||
|
assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
|
||||||
|
assert_eq!(
|
||||||
|
rhombus.closest_point(Vec2::NEG_ONE * 0.2),
|
||||||
|
Vec2::NEG_ONE * 0.2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
rhombus.closest_point(Vec2::new(-0.55, 0.35)),
|
||||||
|
Vec2::new(-0.5, 0.25)
|
||||||
|
);
|
||||||
|
|
||||||
|
let rhombus = Rhombus::new(0.0, 0.0);
|
||||||
|
assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
|
||||||
|
assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
|
||||||
|
assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn circle_math() {
|
fn circle_math() {
|
||||||
let circle = Circle { radius: 3.0 };
|
let circle = Circle { radius: 3.0 };
|
||||||
@ -1618,6 +1763,28 @@ mod tests {
|
|||||||
assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
|
assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rhombus_math() {
|
||||||
|
let rhombus = Rhombus::new(3.0, 4.0);
|
||||||
|
assert_eq!(rhombus.area(), 6.0, "incorrect area");
|
||||||
|
assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
|
||||||
|
assert_eq!(rhombus.side(), 2.5, "incorrect side");
|
||||||
|
assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
|
||||||
|
assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
|
||||||
|
let rhombus = Rhombus::new(0.0, 0.0);
|
||||||
|
assert_eq!(rhombus.area(), 0.0, "incorrect area");
|
||||||
|
assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
|
||||||
|
assert_eq!(rhombus.side(), 0.0, "incorrect side");
|
||||||
|
assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
|
||||||
|
assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
|
||||||
|
let rhombus = Rhombus::from_side(std::f32::consts::SQRT_2);
|
||||||
|
assert_eq!(rhombus, Rhombus::new(2.0, 2.0));
|
||||||
|
assert_eq!(
|
||||||
|
rhombus,
|
||||||
|
Rhombus::from_inradius(std::f32::consts::FRAC_1_SQRT_2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ellipse_math() {
|
fn ellipse_math() {
|
||||||
let ellipse = Ellipse::new(3.0, 1.0);
|
let ellipse = Ellipse::new(3.0, 1.0);
|
||||||
|
|||||||
@ -28,6 +28,14 @@ impl_reflect!(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl_reflect!(
|
||||||
|
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[type_path = "bevy_math::primitives"]
|
||||||
|
struct Rhombus {
|
||||||
|
half_diagonals: Vec2,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl_reflect!(
|
impl_reflect!(
|
||||||
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[type_path = "bevy_math::primitives"]
|
#[type_path = "bevy_math::primitives"]
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use super::{MeshBuilder, Meshable};
|
|||||||
use bevy_math::{
|
use bevy_math::{
|
||||||
primitives::{
|
primitives::{
|
||||||
Annulus, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Rectangle,
|
Annulus, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Rectangle,
|
||||||
RegularPolygon, Triangle2d, Triangle3d, WindingOrder,
|
RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
|
||||||
},
|
},
|
||||||
FloatExt, Vec2,
|
FloatExt, Vec2,
|
||||||
};
|
};
|
||||||
@ -583,6 +583,38 @@ impl From<Annulus> for Mesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Meshable for Rhombus {
|
||||||
|
type Output = Mesh;
|
||||||
|
|
||||||
|
fn mesh(&self) -> Self::Output {
|
||||||
|
let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
|
||||||
|
let positions = vec![
|
||||||
|
[hhd, 0.0, 0.0],
|
||||||
|
[-hhd, 0.0, 0.0],
|
||||||
|
[0.0, vhd, 0.0],
|
||||||
|
[0.0, -vhd, 0.0],
|
||||||
|
];
|
||||||
|
let normals = vec![[0.0, 0.0, 1.0]; 4];
|
||||||
|
let uvs = vec![[1.0, 0.5], [0.0, 0.5], [0.5, 0.0], [0.5, 1.0]];
|
||||||
|
let indices = Indices::U32(vec![1, 0, 2, 1, 3, 0]);
|
||||||
|
|
||||||
|
Mesh::new(
|
||||||
|
PrimitiveTopology::TriangleList,
|
||||||
|
RenderAssetUsages::default(),
|
||||||
|
)
|
||||||
|
.with_inserted_indices(indices)
|
||||||
|
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
||||||
|
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
|
||||||
|
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rhombus> for Mesh {
|
||||||
|
fn from(rhombus: Rhombus) -> Self {
|
||||||
|
rhombus.mesh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Meshable for Triangle2d {
|
impl Meshable for Triangle2d {
|
||||||
type Output = Mesh;
|
type Output = Mesh;
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
const X_EXTENT: f32 = 800.;
|
const X_EXTENT: f32 = 900.;
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
@ -28,6 +28,7 @@ fn setup(
|
|||||||
Mesh2dHandle(meshes.add(Ellipse::new(25.0, 50.0))),
|
Mesh2dHandle(meshes.add(Ellipse::new(25.0, 50.0))),
|
||||||
Mesh2dHandle(meshes.add(Annulus::new(25.0, 50.0))),
|
Mesh2dHandle(meshes.add(Annulus::new(25.0, 50.0))),
|
||||||
Mesh2dHandle(meshes.add(Capsule2d::new(25.0, 50.0))),
|
Mesh2dHandle(meshes.add(Capsule2d::new(25.0, 50.0))),
|
||||||
|
Mesh2dHandle(meshes.add(Rhombus::new(75.0, 100.0))),
|
||||||
Mesh2dHandle(meshes.add(Rectangle::new(50.0, 100.0))),
|
Mesh2dHandle(meshes.add(Rectangle::new(50.0, 100.0))),
|
||||||
Mesh2dHandle(meshes.add(RegularPolygon::new(50.0, 6))),
|
Mesh2dHandle(meshes.add(RegularPolygon::new(50.0, 6))),
|
||||||
Mesh2dHandle(meshes.add(Triangle2d::new(
|
Mesh2dHandle(meshes.add(Triangle2d::new(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user