Improve Segment2d
/Segment3d
API and docs (#18206)
# Objective #17404 reworked the `Segment2d` and `Segment3d` types to be defined by two endpoints rather than a direction and half-length. However, the API is still very minimal and limited, and documentation is inconsistent and outdated. ## Solution Add the following helper methods for `Segment2d` and `Segment3d`: - `from_scaled_direction` - `from_ray_and_length` - `length_squared` - `direction` - `try_direction` - `scaled_direction` - `transformed` - `reversed` `Segment2d` has a few 2D-specific methods: - `left_normal` - `try_left_normal` - `scaled_left_normal` - `right_normal` - `try_right_normal` - `scaled_right_normal` There are now also `From` implementations for converting `[Vec2; 2]` and `(Vec2, Vec2)` to a `Segment2d`, and similarly for 3D. I have also updated documentation to be more accurate and consistent, and simplified a few methods. --- ## Prior Art Parry's [`Segment`](https://docs.rs/parry2d/latest/parry2d/shape/struct.Segment.html) type has a lot of similar methods, though my implementation is a bit more comprehensive. A lot of these methods can be useful for various geometry algorithms. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
This commit is contained in:
parent
11e0ef5391
commit
9f6d628c48
@ -634,9 +634,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
let segment = Segment2d::new(self.point1, self.point2)
|
||||
.rotated(self.isometry.rotation)
|
||||
.translated(self.isometry.translation);
|
||||
let segment = Segment2d::new(self.point1, self.point2).transformed(self.isometry);
|
||||
|
||||
if self.draw_arrow {
|
||||
self.gizmos
|
||||
|
@ -228,10 +228,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
let isometry: Isometry3d = isometry.into();
|
||||
let transformed = primitive
|
||||
.rotated(isometry.rotation)
|
||||
.translated(isometry.translation.into());
|
||||
let transformed = primitive.transformed(isometry);
|
||||
self.line(transformed.point1(), transformed.point2(), color);
|
||||
}
|
||||
}
|
||||
|
@ -881,9 +881,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn segment() {
|
||||
let segment = Segment2d::new(Vec2::new(-1.0, -0.5), Vec2::new(1.0, 0.5));
|
||||
let translation = Vec2::new(2.0, 1.0);
|
||||
let isometry = Isometry2d::from_translation(translation);
|
||||
let segment = Segment2d::new(Vec2::new(-1.0, -0.5), Vec2::new(1.0, 0.5));
|
||||
|
||||
let aabb = segment.aabb_2d(isometry);
|
||||
assert_eq!(aabb.min, Vec2::new(1.0, 0.5));
|
||||
|
@ -349,7 +349,7 @@ mod tests {
|
||||
#[test]
|
||||
fn segment() {
|
||||
let extrusion = Extrusion::new(
|
||||
Segment2d::from_direction_and_length(Dir2::new_unchecked(Vec2::NEG_Y), 3.),
|
||||
Segment2d::new(Vec2::new(0.0, -1.5), Vec2::new(0.0, 1.5)),
|
||||
4.0,
|
||||
);
|
||||
let translation = Vec3::new(3., 4., 5.);
|
||||
|
@ -457,9 +457,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn segment() {
|
||||
let translation = Vec3::new(2.0, 1.0, 0.0);
|
||||
|
||||
let segment = Segment3d::new(Vec3::new(-1.0, -0.5, 0.0), Vec3::new(1.0, 0.5, 0.0));
|
||||
let translation = Vec3::new(2.0, 1.0, 0.0);
|
||||
|
||||
let aabb = segment.aabb_3d(translation);
|
||||
assert_eq!(aabb.min, Vec3A::new(1.0, 0.5, 0.0));
|
||||
|
@ -5,7 +5,7 @@ use thiserror::Error;
|
||||
use super::{Measured2d, Primitive2d, WindingOrder};
|
||||
use crate::{
|
||||
ops::{self, FloatPow},
|
||||
Dir2, Rot2, Vec2,
|
||||
Dir2, InvalidDirectionError, Isometry2d, Ray2d, Rot2, Vec2,
|
||||
};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -1211,7 +1211,7 @@ pub struct Line2d {
|
||||
}
|
||||
impl Primitive2d for Line2d {}
|
||||
|
||||
/// A segment of a line going through the origin along a direction in 2D space.
|
||||
/// A line segment defined by two endpoints in 2D space.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
||||
@ -1227,7 +1227,7 @@ pub struct Segment2d {
|
||||
impl Primitive2d for Segment2d {}
|
||||
|
||||
impl Segment2d {
|
||||
/// Create a new `Segment2d` from its endpoints
|
||||
/// Create a new `Segment2d` from its endpoints.
|
||||
#[inline(always)]
|
||||
pub const fn new(point1: Vec2, point2: Vec2) -> Self {
|
||||
Self {
|
||||
@ -1235,62 +1235,194 @@ impl Segment2d {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Segment2d` from its endpoints and compute its geometric center
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `point1 == point2`
|
||||
/// Create a new `Segment2d` from its endpoints and compute its geometric center.
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.16.0", note = "Use the `new` constructor instead")]
|
||||
pub fn from_points(point1: Vec2, point2: Vec2) -> (Self, Vec2) {
|
||||
(Self::new(point1, point2), (point1 + point2) / 2.)
|
||||
}
|
||||
|
||||
/// Create a new `Segment2d` at the origin from a `direction` and `length`
|
||||
/// Create a new `Segment2d` centered at the origin with the given direction and length.
|
||||
///
|
||||
/// The endpoints will be at `-direction * length / 2.0` and `direction * length / 2.0`.
|
||||
#[inline(always)]
|
||||
pub fn from_direction_and_length(direction: Dir2, length: f32) -> Segment2d {
|
||||
let half_length = length / 2.;
|
||||
Self::new(direction * -half_length, direction * half_length)
|
||||
pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
|
||||
let endpoint = 0.5 * length * direction;
|
||||
Self {
|
||||
vertices: [-endpoint, endpoint],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the position of the first point on the line segment
|
||||
/// Create a new `Segment2d` centered at the origin from a vector representing
|
||||
/// the direction and length of the line segment.
|
||||
///
|
||||
/// The endpoints will be at `-scaled_direction / 2.0` and `scaled_direction / 2.0`.
|
||||
#[inline(always)]
|
||||
pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
|
||||
let endpoint = 0.5 * scaled_direction;
|
||||
Self {
|
||||
vertices: [-endpoint, endpoint],
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Segment2d` starting from the origin of the given `ray`,
|
||||
/// going in the direction of the ray for the given `length`.
|
||||
///
|
||||
/// The endpoints will be at `ray.origin` and `ray.origin + length * ray.direction`.
|
||||
#[inline(always)]
|
||||
pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
|
||||
Self {
|
||||
vertices: [ray.origin, ray.get_point(length)],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the position of the first endpoint of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn point1(&self) -> Vec2 {
|
||||
self.vertices[0]
|
||||
}
|
||||
|
||||
/// Get the position of the second point on the line segment
|
||||
/// Get the position of the second endpoint of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn point2(&self) -> Vec2 {
|
||||
self.vertices[1]
|
||||
}
|
||||
|
||||
/// Get the segment's center
|
||||
/// Compute the midpoint between the two endpoints of the line segment.
|
||||
#[inline(always)]
|
||||
#[doc(alias = "midpoint")]
|
||||
pub fn center(&self) -> Vec2 {
|
||||
(self.point1() + self.point2()) / 2.
|
||||
self.point1().midpoint(self.point2())
|
||||
}
|
||||
|
||||
/// Get the segment's length
|
||||
/// Compute the length of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn length(&self) -> f32 {
|
||||
self.point1().distance(self.point2())
|
||||
}
|
||||
|
||||
/// Get the segment translated by the given vector
|
||||
/// Compute the squared length of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
self.point1().distance_squared(self.point2())
|
||||
}
|
||||
|
||||
/// Compute the normalized direction pointing from the first endpoint to the second endpoint.
|
||||
///
|
||||
/// For the non-panicking version, see [`Segment2d::try_direction`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a valid direction could not be computed, for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn direction(&self) -> Dir2 {
|
||||
self.try_direction().unwrap_or_else(|err| {
|
||||
panic!("Failed to compute the direction of a line segment: {err}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to compute the normalized direction pointing from the first endpoint to the second endpoint.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid direction could not be computed,
|
||||
/// for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
|
||||
Dir2::new(self.scaled_direction())
|
||||
}
|
||||
|
||||
/// Compute the vector from the first endpoint to the second endpoint.
|
||||
#[inline(always)]
|
||||
pub fn scaled_direction(&self) -> Vec2 {
|
||||
self.point2() - self.point1()
|
||||
}
|
||||
|
||||
/// Compute the normalized counterclockwise normal on the left-hand side of the line segment.
|
||||
///
|
||||
/// For the non-panicking version, see [`Segment2d::try_left_normal`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn left_normal(&self) -> Dir2 {
|
||||
self.try_left_normal().unwrap_or_else(|err| {
|
||||
panic!("Failed to compute the left-hand side normal of a line segment: {err}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to compute the normalized counterclockwise normal on the left-hand side of the line segment.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
|
||||
/// for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
|
||||
Dir2::new(self.scaled_left_normal())
|
||||
}
|
||||
|
||||
/// Compute the non-normalized counterclockwise normal on the left-hand side of the line segment.
|
||||
///
|
||||
/// The length of the normal is the distance between the endpoints.
|
||||
#[inline(always)]
|
||||
pub fn scaled_left_normal(&self) -> Vec2 {
|
||||
let scaled_direction = self.scaled_direction();
|
||||
Vec2::new(-scaled_direction.y, scaled_direction.x)
|
||||
}
|
||||
|
||||
/// Compute the normalized clockwise normal on the right-hand side of the line segment.
|
||||
///
|
||||
/// For the non-panicking version, see [`Segment2d::try_right_normal`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn right_normal(&self) -> Dir2 {
|
||||
self.try_right_normal().unwrap_or_else(|err| {
|
||||
panic!("Failed to compute the right-hand side normal of a line segment: {err}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to compute the normalized clockwise normal on the right-hand side of the line segment.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
|
||||
/// for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
|
||||
Dir2::new(self.scaled_right_normal())
|
||||
}
|
||||
|
||||
/// Compute the non-normalized clockwise normal on the right-hand side of the line segment.
|
||||
///
|
||||
/// The length of the normal is the distance between the endpoints.
|
||||
#[inline(always)]
|
||||
pub fn scaled_right_normal(&self) -> Vec2 {
|
||||
let scaled_direction = self.scaled_direction();
|
||||
Vec2::new(scaled_direction.y, -scaled_direction.x)
|
||||
}
|
||||
|
||||
/// Compute the segment transformed by the given [`Isometry2d`].
|
||||
#[inline(always)]
|
||||
pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
|
||||
let isometry: Isometry2d = isometry.into();
|
||||
Self::new(
|
||||
isometry.transform_point(self.point1()),
|
||||
isometry.transform_point(self.point2()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the segment translated by the given vector.
|
||||
#[inline(always)]
|
||||
pub fn translated(&self, translation: Vec2) -> Segment2d {
|
||||
Self::new(self.point1() + translation, self.point2() + translation)
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around the origin
|
||||
/// Compute the segment rotated around the origin by the given rotation.
|
||||
#[inline(always)]
|
||||
pub fn rotated(&self, rotation: Rot2) -> Segment2d {
|
||||
Segment2d::new(rotation * self.point1(), rotation * self.point2())
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around a given point
|
||||
/// Compute the segment rotated around the given point by the given rotation.
|
||||
#[inline(always)]
|
||||
pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
|
||||
// We offset our segment so that our segment is rotated as if from the origin, then we can apply the offset back
|
||||
@ -1299,28 +1431,57 @@ impl Segment2d {
|
||||
rotated.translated(point)
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around its center
|
||||
/// Compute the segment rotated around its own center.
|
||||
#[inline(always)]
|
||||
pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
|
||||
self.rotated_around(rotation, self.center())
|
||||
}
|
||||
|
||||
/// Get the segment with its center at the origin
|
||||
/// Compute the segment with its center at the origin, keeping the same direction and length.
|
||||
#[inline(always)]
|
||||
pub fn centered(&self) -> Segment2d {
|
||||
let center = self.center();
|
||||
self.translated(-center)
|
||||
}
|
||||
|
||||
/// Get the segment with a new length
|
||||
/// Compute the segment with a new length, keeping the same direction and center.
|
||||
#[inline(always)]
|
||||
pub fn resized(&self, length: f32) -> Segment2d {
|
||||
let offset_from_origin = self.center();
|
||||
let centered = self.centered();
|
||||
let centered = self.translated(-offset_from_origin);
|
||||
let ratio = length / self.length();
|
||||
let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
|
||||
segment.translated(offset_from_origin)
|
||||
}
|
||||
|
||||
/// Reverses the direction of the line segment by swapping the endpoints.
|
||||
#[inline(always)]
|
||||
pub fn reverse(&mut self) {
|
||||
let [point1, point2] = &mut self.vertices;
|
||||
core::mem::swap(point1, point2);
|
||||
}
|
||||
|
||||
/// Returns the line segment with its direction reversed by swapping the endpoints.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn reversed(mut self) -> Self {
|
||||
self.reverse();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Vec2; 2]> for Segment2d {
|
||||
#[inline(always)]
|
||||
fn from(vertices: [Vec2; 2]) -> Self {
|
||||
Self { vertices }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Vec2, Vec2)> for Segment2d {
|
||||
#[inline(always)]
|
||||
fn from((point1, point2): (Vec2, Vec2)) -> Self {
|
||||
Self::new(point1, point2)
|
||||
}
|
||||
}
|
||||
|
||||
/// A series of connected line segments in 2D space.
|
||||
|
@ -3,7 +3,7 @@ use core::f32::consts::{FRAC_PI_3, PI};
|
||||
use super::{Circle, Measured2d, Measured3d, Primitive2d, Primitive3d};
|
||||
use crate::{
|
||||
ops::{self, FloatPow},
|
||||
Dir3, InvalidDirectionError, Isometry3d, Mat3, Vec2, Vec3,
|
||||
Dir3, InvalidDirectionError, Isometry3d, Mat3, Ray3d, Vec2, Vec3,
|
||||
};
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
@ -349,8 +349,7 @@ pub struct Line3d {
|
||||
}
|
||||
impl Primitive3d for Line3d {}
|
||||
|
||||
/// A segment of a line going through the origin along a direction in 3D space.
|
||||
#[doc(alias = "LineSegment3d")]
|
||||
/// A line segment defined by two endpoints in 3D space.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
||||
@ -358,6 +357,7 @@ impl Primitive3d for Line3d {}
|
||||
all(feature = "serialize", feature = "bevy_reflect"),
|
||||
reflect(Serialize, Deserialize)
|
||||
)]
|
||||
#[doc(alias = "LineSegment3d")]
|
||||
pub struct Segment3d {
|
||||
/// The endpoints of the line segment.
|
||||
pub vertices: [Vec3; 2],
|
||||
@ -365,7 +365,7 @@ pub struct Segment3d {
|
||||
impl Primitive3d for Segment3d {}
|
||||
|
||||
impl Segment3d {
|
||||
/// Create a new `Segment3d` from its endpoints
|
||||
/// Create a new `Segment3d` from its endpoints.
|
||||
#[inline(always)]
|
||||
pub const fn new(point1: Vec3, point2: Vec3) -> Self {
|
||||
Self {
|
||||
@ -373,65 +373,130 @@ impl Segment3d {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Segment3d` from a direction and full length of the segment
|
||||
#[inline(always)]
|
||||
pub fn from_direction_and_length(direction: Dir3, length: f32) -> Self {
|
||||
let half_length = length / 2.;
|
||||
Self::new(direction * -half_length, direction * half_length)
|
||||
}
|
||||
|
||||
/// Create a new `Segment3d` from its endpoints and compute its geometric center
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `point1 == point2`
|
||||
/// Create a new `Segment3d` from its endpoints and compute its geometric center.
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.16.0", note = "Use the `new` constructor instead")]
|
||||
pub fn from_points(point1: Vec3, point2: Vec3) -> (Self, Vec3) {
|
||||
(Self::new(point1, point2), (point1 + point2) / 2.)
|
||||
}
|
||||
|
||||
/// Get the position of the first point on the line segment
|
||||
/// Create a new `Segment3d` centered at the origin with the given direction and length.
|
||||
///
|
||||
/// The endpoints will be at `-direction * length / 2.0` and `direction * length / 2.0`.
|
||||
#[inline(always)]
|
||||
pub fn from_direction_and_length(direction: Dir3, length: f32) -> Self {
|
||||
let endpoint = 0.5 * length * direction;
|
||||
Self {
|
||||
vertices: [-endpoint, endpoint],
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Segment3d` centered at the origin from a vector representing
|
||||
/// the direction and length of the line segment.
|
||||
///
|
||||
/// The endpoints will be at `-scaled_direction / 2.0` and `scaled_direction / 2.0`.
|
||||
#[inline(always)]
|
||||
pub fn from_scaled_direction(scaled_direction: Vec3) -> Self {
|
||||
let endpoint = 0.5 * scaled_direction;
|
||||
Self {
|
||||
vertices: [-endpoint, endpoint],
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Segment3d` starting from the origin of the given `ray`,
|
||||
/// going in the direction of the ray for the given `length`.
|
||||
///
|
||||
/// The endpoints will be at `ray.origin` and `ray.origin + length * ray.direction`.
|
||||
#[inline(always)]
|
||||
pub fn from_ray_and_length(ray: Ray3d, length: f32) -> Self {
|
||||
Self {
|
||||
vertices: [ray.origin, ray.get_point(length)],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the position of the first endpoint of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn point1(&self) -> Vec3 {
|
||||
self.vertices[0]
|
||||
}
|
||||
|
||||
/// Get the position of the second point on the line segment
|
||||
/// Get the position of the second endpoint of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn point2(&self) -> Vec3 {
|
||||
self.vertices[1]
|
||||
}
|
||||
|
||||
/// Get the center of the segment
|
||||
/// Compute the midpoint between the two endpoints of the line segment.
|
||||
#[inline(always)]
|
||||
#[doc(alias = "midpoint")]
|
||||
pub fn center(&self) -> Vec3 {
|
||||
(self.point1() + self.point2()) / 2.
|
||||
self.point1().midpoint(self.point2())
|
||||
}
|
||||
|
||||
/// Get the length of the segment
|
||||
/// Compute the length of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn length(&self) -> f32 {
|
||||
self.point1().distance(self.point2())
|
||||
}
|
||||
|
||||
/// Get the segment translated by a vector
|
||||
/// Compute the squared length of the line segment.
|
||||
#[inline(always)]
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
self.point1().distance_squared(self.point2())
|
||||
}
|
||||
|
||||
/// Compute the normalized direction pointing from the first endpoint to the second endpoint.
|
||||
///
|
||||
/// For the non-panicking version, see [`Segment3d::try_direction`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a valid direction could not be computed, for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn direction(&self) -> Dir3 {
|
||||
self.try_direction().unwrap_or_else(|err| {
|
||||
panic!("Failed to compute the direction of a line segment: {err}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to compute the normalized direction pointing from the first endpoint to the second endpoint.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid direction could not be computed,
|
||||
/// for example when the endpoints are coincident, NaN, or infinite.
|
||||
#[inline(always)]
|
||||
pub fn try_direction(&self) -> Result<Dir3, InvalidDirectionError> {
|
||||
Dir3::new(self.scaled_direction())
|
||||
}
|
||||
|
||||
/// Compute the vector from the first endpoint to the second endpoint.
|
||||
#[inline(always)]
|
||||
pub fn scaled_direction(&self) -> Vec3 {
|
||||
self.point2() - self.point1()
|
||||
}
|
||||
|
||||
/// Compute the segment transformed by the given [`Isometry3d`].
|
||||
#[inline(always)]
|
||||
pub fn transformed(&self, isometry: impl Into<Isometry3d>) -> Self {
|
||||
let isometry: Isometry3d = isometry.into();
|
||||
Self::new(
|
||||
isometry.transform_point(self.point1()).into(),
|
||||
isometry.transform_point(self.point2()).into(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the segment translated by the given vector.
|
||||
#[inline(always)]
|
||||
pub fn translated(&self, translation: Vec3) -> Segment3d {
|
||||
Self::new(self.point1() + translation, self.point2() + translation)
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around the origin
|
||||
/// Compute the segment rotated around the origin by the given rotation.
|
||||
#[inline(always)]
|
||||
pub fn rotated(&self, rotation: Quat) -> Segment3d {
|
||||
Segment3d::new(
|
||||
rotation.mul_vec3(self.point1()),
|
||||
rotation.mul_vec3(self.point2()),
|
||||
)
|
||||
Segment3d::new(rotation * self.point1(), rotation * self.point2())
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around a given point
|
||||
/// Compute the segment rotated around the given point by the given rotation.
|
||||
#[inline(always)]
|
||||
pub fn rotated_around(&self, rotation: Quat, point: Vec3) -> Segment3d {
|
||||
// We offset our segment so that our segment is rotated as if from the origin, then we can apply the offset back
|
||||
@ -440,28 +505,57 @@ impl Segment3d {
|
||||
rotated.translated(point)
|
||||
}
|
||||
|
||||
/// Compute a new segment, based on the original segment rotated around its center
|
||||
/// Compute the segment rotated around its own center.
|
||||
#[inline(always)]
|
||||
pub fn rotated_around_center(&self, rotation: Quat) -> Segment3d {
|
||||
self.rotated_around(rotation, self.center())
|
||||
}
|
||||
|
||||
/// Get the segment offset so that it's center is at the origin
|
||||
/// Compute the segment with its center at the origin, keeping the same direction and length.
|
||||
#[inline(always)]
|
||||
pub fn centered(&self) -> Segment3d {
|
||||
let center = self.center();
|
||||
self.translated(-center)
|
||||
}
|
||||
|
||||
/// Get the segment with a new length
|
||||
/// Compute the segment with a new length, keeping the same direction and center.
|
||||
#[inline(always)]
|
||||
pub fn resized(&self, length: f32) -> Segment3d {
|
||||
let offset_from_origin = self.center();
|
||||
let centered = self.centered();
|
||||
let centered = self.translated(-offset_from_origin);
|
||||
let ratio = length / self.length();
|
||||
let segment = Segment3d::new(centered.point1() * ratio, centered.point2() * ratio);
|
||||
segment.translated(offset_from_origin)
|
||||
}
|
||||
|
||||
/// Reverses the direction of the line segment by swapping the endpoints.
|
||||
#[inline(always)]
|
||||
pub fn reverse(&mut self) {
|
||||
let [point1, point2] = &mut self.vertices;
|
||||
core::mem::swap(point1, point2);
|
||||
}
|
||||
|
||||
/// Returns the line segment with its direction reversed by swapping the endpoints.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn reversed(mut self) -> Self {
|
||||
self.reverse();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Vec3; 2]> for Segment3d {
|
||||
#[inline(always)]
|
||||
fn from(vertices: [Vec3; 2]) -> Self {
|
||||
Self { vertices }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Vec3, Vec3)> for Segment3d {
|
||||
#[inline(always)]
|
||||
fn from((point1, point2): (Vec3, Vec3)) -> Self {
|
||||
Self::new(point1, point2)
|
||||
}
|
||||
}
|
||||
|
||||
/// A series of connected line segments in 3D space.
|
||||
|
Loading…
Reference in New Issue
Block a user