Skip to content

Commit d2bfb2b

Browse files
Review feedback and other minor improvements.
1 parent 271f299 commit d2bfb2b

File tree

4 files changed

+135
-52
lines changed

4 files changed

+135
-52
lines changed

crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::f32::consts::PI;
55
use glam::{Mat2, Vec2};
66

77
use crate::primitives::{
8-
Arc, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, Direction2d, Ellipse,
8+
Arc2d, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, Direction2d, Ellipse,
99
Line2d, Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Segment2d, Triangle2d,
1010
};
1111

@@ -21,7 +21,7 @@ impl Bounded2d for Circle {
2121
}
2222
}
2323

24-
impl Bounded2d for Arc {
24+
impl Bounded2d for Arc2d {
2525
fn aabb_2d(&self, translation: Vec2, rotation: f32) -> Aabb2d {
2626
// For a sufficiently wide arc, the bounding points in a given direction will be the outer
2727
// limits of a circle centered at the origin.
@@ -60,14 +60,14 @@ impl Bounded2d for Arc {
6060
// Otherwise, the widest distance between two points is the chord,
6161
// so a circle of that diameter around the midpoint will contain the entire arc.
6262
let center = self.chord_midpoint().rotate(Vec2::from_angle(rotation));
63-
BoundingCircle::new(center + translation, self.half_chord_len())
63+
BoundingCircle::new(center + translation, self.half_chord_length())
6464
}
6565
}
6666
}
6767

6868
impl Bounded2d for CircularSector {
6969
fn aabb_2d(&self, translation: Vec2, rotation: f32) -> Aabb2d {
70-
// This is identical to the implementation for Arc, above, with the additional possibility of the
70+
// This is identical to the implementation for Arc2d, above, with the additional possibility of the
7171
// origin point, the center of the arc, acting as a bounding point.
7272
//
7373
// See comments above for discussion.
@@ -99,7 +99,7 @@ impl Bounded2d for CircularSector {
9999
// If the arc is major, then the widest distance between two points is a diameter of the arc's circle;
100100
// therefore, that circle is the bounding radius.
101101
BoundingCircle::new(translation, self.arc.radius)
102-
} else if self.arc.chord_len() < self.arc.radius {
102+
} else if self.arc.chord_length() < self.arc.radius {
103103
// If the chord length is smaller than the radius, then the radius is the widest distance between two points,
104104
// so the radius is the diameter of the bounding circle.
105105
let half_radius = self.arc.radius / 2.0;
@@ -110,7 +110,7 @@ impl Bounded2d for CircularSector {
110110
// Otherwise, the widest distance between two points is the chord,
111111
// so a circle of that diameter around the midpoint will contain the entire arc.
112112
let center = self.arc.chord_midpoint().rotate(Vec2::from_angle(rotation));
113-
BoundingCircle::new(center + translation, self.arc.half_chord_len())
113+
BoundingCircle::new(center + translation, self.arc.half_chord_length())
114114
}
115115
}
116116
}

crates/bevy_math/src/primitives/dim2.rs

Lines changed: 123 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ impl Circle {
176176
}
177177
}
178178

179-
/// A primitive representing an arc: a segment of a circle.
179+
const HALF_PI: f32 = PI / 2.0;
180+
181+
/// A primitive representing an arc between two points on a circle.
180182
///
181183
/// An arc has no area.
182184
/// If you want to include the portion of a circle's area swept out by the arc,
@@ -189,35 +191,62 @@ impl Circle {
189191
/// The arc is drawn with the center of its circle at the origin (0, 0),
190192
/// meaning that the center may not be inside its convex hull.
191193
#[derive(Clone, Copy, Debug, PartialEq)]
194+
#[doc(alias("CircularArc", "CircleArc"))]
192195
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
193-
pub struct Arc {
196+
pub struct Arc2d {
194197
/// The radius of the circle
195198
pub radius: f32,
196-
/// Half the angle swept out by the arc.
199+
/// Half the angle subtended by the arc.
197200
pub half_angle: f32,
198201
}
199-
impl Primitive2d for Arc {}
202+
impl Primitive2d for Arc2d {}
200203

201-
impl Default for Arc {
202-
/// Returns the default [`Arc`] with radius `0.5` and angle `1.0`.
204+
impl Default for Arc2d {
205+
/// Returns the default [`Arc2d`] with radius `0.5`, covering a quarter of a circle.
203206
fn default() -> Self {
204207
Self {
205208
radius: 0.5,
206-
half_angle: 0.5,
209+
half_angle: PI / 4.0,
207210
}
208211
}
209212
}
210213

211-
impl Arc {
212-
/// Create a new [`Arc`] from a `radius`, and an `angle`
214+
impl Arc2d {
215+
/// Create a new [`Arc2d`] from a `radius`, and a `half_angle`
213216
#[inline(always)]
214-
pub fn new(radius: f32, angle: f32) -> Self {
217+
pub fn new(radius: f32, half_angle: f32) -> Self {
218+
Self { radius, half_angle }
219+
}
220+
221+
/// Create a new [`Arc2d`] from a `radius` and an `angle` in radians.
222+
#[inline(always)]
223+
pub fn from_radians(radius: f32, angle: f32) -> Self {
215224
Self {
216225
radius,
217226
half_angle: angle / 2.0,
218227
}
219228
}
220229

230+
/// Create a new [`Arc2d`] from a `radius` and an angle in `degrees`.
231+
#[inline(always)]
232+
pub fn from_degrees(radius: f32, degrees: f32) -> Self {
233+
Self {
234+
radius,
235+
half_angle: degrees.to_radians() / 2.0,
236+
}
237+
}
238+
239+
/// Create a new [`Arc2d`] from a `radius` and a `fraction` of a circle.
240+
///
241+
/// A `fraction` of 1.0 would be a whole circle; 0.5 would be a semicircle.
242+
#[inline(always)]
243+
pub fn from_fraction(radius: f32, fraction: f32) -> Self {
244+
Self {
245+
radius,
246+
half_angle: fraction * PI,
247+
}
248+
}
249+
221250
/// Get the angle of the arc
222251
#[inline(always)]
223252
pub fn angle(&self) -> f32 {
@@ -256,76 +285,89 @@ impl Arc {
256285

257286
/// Get half the length of the chord subtended by the arc
258287
#[inline(always)]
259-
pub fn half_chord_len(&self) -> f32 {
288+
pub fn half_chord_length(&self) -> f32 {
260289
self.radius * f32::sin(self.half_angle)
261290
}
262291

263292
/// Get the length of the chord subtended by the arc
264293
#[inline(always)]
265-
pub fn chord_len(&self) -> f32 {
266-
2.0 * self.half_chord_len()
294+
pub fn chord_length(&self) -> f32 {
295+
2.0 * self.half_chord_length()
267296
}
268297

269-
/// Get the midpoint of the chord
298+
/// Get the midpoint of the chord subtended by the arc
270299
#[inline(always)]
271300
pub fn chord_midpoint(&self) -> Vec2 {
272-
self.apothem_len() * Vec2::from_angle(self.half_angle)
301+
self.apothem() * Vec2::from_angle(self.half_angle)
273302
}
274303

275304
/// Get the length of the apothem of this arc, that is,
276305
/// the distance from the center of the circle to the midpoint of the chord.
277306
/// Equivalently, the height of the triangle whose base is the chord and whose apex is the center of the circle.
278307
#[inline(always)]
279-
pub fn apothem_len(&self) -> f32 {
280-
f32::sqrt(self.radius.powi(2) - self.half_chord_len().powi(2))
308+
// Naming note: Various sources are inconsistent as to whether the apothem is the segment between the center and the
309+
// midpoint of a chord, or the length of that segment. Given this confusion, we've opted for the definition
310+
// used by Wolfram MathWorld, which is the distance rather than the segment.
311+
pub fn apothem(&self) -> f32 {
312+
f32::sqrt(self.radius.powi(2) - self.half_chord_length().powi(2))
281313
}
282314

283315
/// Get the legnth of the sagitta of this arc, that is,
284316
/// the length of the line between the midpoints of the arc and its chord.
285317
/// Equivalently, the height of the triangle whose base is the chord and whose apex is the midpoint of the arc.
286318
///
287-
/// If the arc is minor, i.e. less than half the circle, the this will be the difference of the [`radius`](Self::radius)
288-
/// and the [`apothem`](Self::apothem_len).
319+
/// If the arc is [minor](Self::is_minor), i.e. less than or equal to a semicircle,
320+
/// this will be the difference of the [`radius`](Self::radius) and the [`apothem`](Self::apothem).
289321
/// If the arc is [major](Self::is_major), it will be their sum.
290322
#[inline(always)]
291-
pub fn sagitta_len(&self) -> f32 {
323+
pub fn sagitta(&self) -> f32 {
292324
if self.is_major() {
293-
self.radius + self.apothem_len()
325+
self.radius + self.apothem()
294326
} else {
295-
self.radius - self.apothem_len()
327+
self.radius - self.apothem()
296328
}
297329
}
298330

331+
/// Produces true if the arc is at most half a circle.
332+
///
333+
/// **Note:** This is not the negation of [`is_major`](Self::is_major): an exact semicircle is both major and minor.
334+
#[inline(always)]
335+
pub fn is_minor(&self) -> bool {
336+
self.half_angle <= HALF_PI
337+
}
338+
299339
/// Produces true if the arc is at least half a circle.
340+
///
341+
/// **Note:** This is not the negation of [`is_minor`](Self::is_minor): an exact semicircle is both major and minor.
300342
#[inline(always)]
301343
pub fn is_major(&self) -> bool {
302-
self.angle() >= PI
344+
self.half_angle >= HALF_PI
303345
}
304346
}
305347

306348
/// A primitive representing a circular sector: a pie slice of a circle.
307349
///
308350
/// The sector is drawn starting from [`Vec2::X`], going counterclockwise.
309351
/// To orient the sector differently, apply a rotation.
310-
/// The sector is drawn with the center of its circle at the origin (0, 0).
352+
/// The sector is drawn with the center of its circle at the origin [`Vec2::ZERO`].
311353
#[derive(Clone, Copy, Debug, PartialEq)]
312354
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
313355
pub struct CircularSector {
314356
/// The arc from which this sector is contructed.
315-
#[cfg_attr(feature = "seriealize", serde(flatten))]
316-
pub arc: Arc,
357+
#[cfg_attr(feature = "serialize", serde(flatten))]
358+
pub arc: Arc2d,
317359
}
318360
impl Primitive2d for CircularSector {}
319361

320362
impl Default for CircularSector {
321-
/// Returns the default [`CircularSector`] with radius `0.5` and angle `1.0`.
363+
/// Returns the default [`CircularSector`] with radius `0.5` and covering a quarter circle.
322364
fn default() -> Self {
323-
Arc::default().into()
365+
Self::from(Arc2d::default())
324366
}
325367
}
326368

327-
impl From<Arc> for CircularSector {
328-
fn from(arc: Arc) -> Self {
369+
impl From<Arc2d> for CircularSector {
370+
fn from(arc: Arc2d) -> Self {
329371
Self { arc }
330372
}
331373
}
@@ -334,7 +376,27 @@ impl CircularSector {
334376
/// Create a new [`CircularSector`] from a `radius`, and an `angle`
335377
#[inline(always)]
336378
pub fn new(radius: f32, angle: f32) -> Self {
337-
Arc::new(radius, angle).into()
379+
Self::from(Arc2d::new(radius, angle))
380+
}
381+
382+
/// Create a new [`CircularSector`] from a `radius` and an `angle` in radians.
383+
#[inline(always)]
384+
pub fn from_radians(radius: f32, angle: f32) -> Self {
385+
Self::from(Arc2d::from_radians(radius, angle))
386+
}
387+
388+
/// Create a new [`CircularSector`] from a `radius` and an angle in `degrees`.
389+
#[inline(always)]
390+
pub fn from_degrees(radius: f32, degrees: f32) -> Self {
391+
Self::from(Arc2d::from_degrees(radius, degrees))
392+
}
393+
394+
/// Create a new [`CircularSector`] from a `radius` and a `fraction` of a circle.
395+
///
396+
/// A `fraction` of 1.0 would be a whole circle; 0.5 would be a semicircle.
397+
#[inline(always)]
398+
pub fn from_fraction(radius: f32, fraction: f32) -> Self {
399+
Self::from(Arc2d::from_fraction(radius, fraction))
338400
}
339401

340402
/// Returns the area of this sector
@@ -349,27 +411,27 @@ impl CircularSector {
349411
///
350412
/// The segment is drawn starting from [`Vec2::X`], going counterclockwise.
351413
/// To orient the segment differently, apply a rotation.
352-
/// The segment is drawn with the center of its circle at the origin (0, 0).
353-
/// When positioning the segment, the [`apothem_len`](Arc::apothem_len) and [`sagitta_len`](Arc::sagitta_len) functions
354-
/// may be particularly useful.
414+
/// The segment is drawn with the center of its circle at the origin [`Vec2::ZERO`].
415+
/// When positioning a segment, the [`apothem`](Arc2d::apothem) function may be particularly useful,
416+
/// as it computes the distance between the segment and the origin.
355417
#[derive(Clone, Copy, Debug, PartialEq)]
356418
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
357419
pub struct CircularSegment {
358420
/// The arc from which this segment is contructed.
359-
#[cfg_attr(feature = "seriealize", serde(flatten))]
360-
pub arc: Arc,
421+
#[cfg_attr(feature = "serialize", serde(flatten))]
422+
pub arc: Arc2d,
361423
}
362424
impl Primitive2d for CircularSegment {}
363425

364426
impl Default for CircularSegment {
365-
/// Returns the default [`CircularSegment`] with radius `0.5` and angle `1.0`.
427+
/// Returns the default [`CircularSegment`] with radius `0.5` and covering a quarter circle.
366428
fn default() -> Self {
367-
Arc::default().into()
429+
Self::from(Arc2d::default())
368430
}
369431
}
370432

371-
impl From<Arc> for CircularSegment {
372-
fn from(arc: Arc) -> Self {
433+
impl From<Arc2d> for CircularSegment {
434+
fn from(arc: Arc2d) -> Self {
373435
Self { arc }
374436
}
375437
}
@@ -378,7 +440,27 @@ impl CircularSegment {
378440
/// Create a new [`CircularSegment`] from a `radius`, and an `angle`
379441
#[inline(always)]
380442
pub fn new(radius: f32, angle: f32) -> Self {
381-
Arc::new(radius, angle).into()
443+
Self::from(Arc2d::new(radius, angle))
444+
}
445+
446+
/// Create a new [`CircularSegment`] from a `radius` and an `angle` in radians.
447+
#[inline(always)]
448+
pub fn from_radians(radius: f32, angle: f32) -> Self {
449+
Self::from(Arc2d::from_radians(radius, angle))
450+
}
451+
452+
/// Create a new [`CircularSegment`] from a `radius` and an angle in `degrees`.
453+
#[inline(always)]
454+
pub fn from_degrees(radius: f32, degrees: f32) -> Self {
455+
Self::from(Arc2d::from_degrees(radius, degrees))
456+
}
457+
458+
/// Create a new [`CircularSegment`] from a `radius` and a `fraction` of a circle.
459+
///
460+
/// A `fraction` of 1.0 would be a whole circle; 0.5 would be a semicircle.
461+
#[inline(always)]
462+
pub fn from_fraction(radius: f32, fraction: f32) -> Self {
463+
Self::from(Arc2d::from_fraction(radius, fraction))
382464
}
383465

384466
/// Returns the area of this segment

crates/bevy_render/src/mesh/primitives/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! ```
2121
2222
mod dim2;
23-
pub use dim2::{CircleMeshBuilder, EllipseMeshBuilder};
23+
pub use dim2::*;
2424

2525
mod dim3;
2626
pub use dim3::*;

examples/2d/2d_shapes.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn setup(
2727
});
2828

2929
// Circular sector
30-
let sector = CircularSector::new(50.0, 5.0);
30+
let sector = CircularSector::from_radians(50.0, 5.0);
3131
let mut sector_transform = Transform::from_translation(Vec3::new(-275.0, 0.0, 0.0));
3232
// A sector is drawn counterclockwise from the right.
3333
// To make it face left, rotate by negative half its angle.
@@ -45,20 +45,21 @@ fn setup(
4545
mesh: meshes.add(Circle { radius: 7.0 }).into(),
4646
material: materials.add(Color::WHITE),
4747
transform: Transform::from_translation(
48-
sector_transform.translation + Vec3::new(-45.0, 0.0, 0.0),
48+
sector_transform.translation + Vec3::new(45.0, 0.0, 0.0),
4949
),
5050
..default()
5151
});
5252

5353
// Circular segment
54-
let segment = CircularSegment::new(50.0, 2.5);
54+
let segment = CircularSegment::from_radians(50.0, 2.5);
5555
let mut segment_transform = Transform::from_translation(Vec3::new(-150.0, 0.0, 0.0));
5656
// A segment is drawn counterclockwise from the right.
5757
// To make it symmetrical about the X axis, rotate by negative half its angle.
5858
// To make it symmetrical about the Y axis, rotate by an additional PI/2 radians.
5959
segment_transform.rotate_z(-segment.arc.half_angle + PI / 2.0);
6060
// The segment is drawn with the center as the center of the circle.
61-
// To move the midpoint of the chord to the origin, we must use an additional translation.
61+
// By subtracting the apothem, we move the segment down so that it touches the line x = 0.
62+
segment_transform.translation.y -= segment.arc.apothem();
6263
commands.spawn(MaterialMesh2dBundle {
6364
mesh: meshes.add(segment).into(),
6465
material: materials.add(Color::MIDNIGHT_BLUE),

0 commit comments

Comments
 (0)