Skip to main content

geop_core_geometry/nurb_curve/
construct.rs

1//! The simplest curves, built directly from their points: a straight line
2//! ([`NurbCurve3D::line`]) and a single rational quadratic
3//! ([`NurbCurve3D::arc`]), both over `[0, 1]`, and the points a clamped
4//! curve starts and ends at ([`NurbCurve3D::start`]).
5//!
6//! Every end point is taken with weight one as it is, never multiplied by
7//! it (see [`Vector3::homogeneous`]).
8
9use geop_core_math::{
10    geop_error::GeopResult,
11    scalars::Scalar,
12    vector::{Vector2, Vector3},
13};
14
15use super::{NurbCurve, NurbCurve2D, NurbCurve3D};
16
17/// The knots of a single Bézier span of `degree` on `[0, 1]`: `degree + 1`
18/// zeros, then as many ones.
19pub fn bezier_knots(degree: usize) -> Vec<Scalar> {
20    let mut knots = vec![Scalar::ZERO; degree + 1];
21    knots.extend(std::iter::repeat_n(Scalar::ONE, degree + 1));
22    knots
23}
24
25impl NurbCurve3D {
26    /// The straight line from `a` to `b`, over `[0, 1]`.
27    pub fn line(a: Vector3, b: Vector3) -> GeopResult<Self> {
28        NurbCurve::try_new(
29            1,
30            vec![a.homogeneous(Scalar::ONE), b.homogeneous(Scalar::ONE)],
31            bezier_knots(1),
32        )
33    }
34
35    /// The rational quadratic from `p0` to `p2` over `[0, 1]`, its middle
36    /// control point `mid` weighted `w`. With `mid` where the tangents of a
37    /// circle at `p0` and `p2` meet and `w` the cosine of half the angle
38    /// between them, it is that circle's arc exactly: for a quarter,
39    /// `mid = p0 + p2 - centre` and `w =` [`super::sqrt2_over_2`].
40    pub fn arc(p0: Vector3, mid: Vector3, p2: Vector3, w: Scalar) -> GeopResult<Self> {
41        NurbCurve::try_new(
42            2,
43            vec![
44                p0.homogeneous(Scalar::ONE),
45                mid.homogeneous(w),
46                p2.homogeneous(Scalar::ONE),
47            ],
48            bezier_knots(2),
49        )
50    }
51
52    /// The point the curve starts at, for the clamped knot vectors every
53    /// curve here has: its first control point, divided by its weight.
54    pub fn start(&self) -> GeopResult<Vector3> {
55        self.control_points[0].dehomogenized()
56    }
57
58    /// The point the curve ends at: its last control point (see
59    /// [`Self::start`]).
60    pub fn end(&self) -> GeopResult<Vector3> {
61        self.control_points[self.control_points.len() - 1].dehomogenized()
62    }
63}
64
65impl NurbCurve2D {
66    /// The straight line from `a` to `b`, over `[0, 1]`.
67    pub fn line(a: Vector2, b: Vector2) -> GeopResult<Self> {
68        NurbCurve::try_new(
69            1,
70            vec![a.homogeneous(Scalar::ONE), b.homogeneous(Scalar::ONE)],
71            bezier_knots(1),
72        )
73    }
74
75    /// The rational quadratic from `p0` to `p2` through the weighted
76    /// control point `mid` (see [`NurbCurve3D::arc`]).
77    pub fn arc(p0: Vector2, mid: Vector2, p2: Vector2, w: Scalar) -> GeopResult<Self> {
78        NurbCurve::try_new(
79            2,
80            vec![
81                p0.homogeneous(Scalar::ONE),
82                mid.homogeneous(w),
83                p2.homogeneous(Scalar::ONE),
84            ],
85            bezier_knots(2),
86        )
87    }
88
89    /// The point the curve starts at (see [`NurbCurve3D::start`]).
90    pub fn start(&self) -> GeopResult<Vector2> {
91        self.control_points[0].dehomogenized()
92    }
93
94    /// The point the curve ends at (see [`NurbCurve3D::start`]).
95    pub fn end(&self) -> GeopResult<Vector2> {
96        self.control_points[self.control_points.len() - 1].dehomogenized()
97    }
98}