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}