geop_core_sketch/space/
geometry.rs1use geop_core_geometry::nurb_curve::cos_sin;
12use geop_core_math::{
13 geop_error::GeopResult,
14 scalars::{Number, Scalar},
15 vector::Vector3,
16};
17
18#[derive(Clone, Copy, Debug)]
22pub struct Helix3<T: Number> {
23 pub start: Vector3<T>,
24 pub base: Vector3<T>,
25 pub top: Vector3<T>,
26 pub turns: Scalar,
27 pub left: bool,
28}
29
30impl<T: Number> Helix3<T> {
31 pub fn axes(&self) -> GeopResult<(Vector3<T>, Vector3<T>)> {
33 let w = self.top.sub(&self.base).normalize()?;
34 let off = self.start.sub(&self.base);
35 Ok((w, off.sub(&w.prod_scalar(off.prod_dot(&w)))))
36 }
37
38 pub fn end(&self) -> GeopResult<Vector3<T>> {
42 let (w, out) = self.axes()?;
43 let degrees = 360.0 * self.turns.to_f64() * if self.left { -1.0 } else { 1.0 };
44 let (cos, sin) = cos_sin(degrees);
45 Ok(self
46 .start
47 .add(&self.top.sub(&self.base))
48 .add(&out.prod_scalar(T::from_f64(cos - 1.0)))
49 .add(&w.prod_cross(&out).prod_scalar(T::from_f64(sin))))
50 }
51}
52
53#[derive(Clone, Copy, Debug)]
55pub struct Arc3<T: Number> {
56 pub s: Vector3<T>,
57 pub m: Vector3<T>,
58 pub e: Vector3<T>,
59}
60
61impl<T: Number> Arc3<T> {
62 fn legs(&self) -> (Vector3<T>, Vector3<T>) {
63 (self.s.sub(&self.m), self.e.sub(&self.m))
64 }
65
66 pub fn normal(&self) -> GeopResult<Vector3<T>> {
69 let (a, b) = self.legs();
70 b.prod_cross(&a).normalize()
71 }
72
73 pub fn half_sweep(&self) -> GeopResult<(T, T)> {
75 let (a, b) = self.legs();
76 let ab = a.try_norm()?.mul(b.try_norm()?);
77 Ok((
78 a.prod_dot(&b).neg().div(ab)?,
79 a.prod_cross(&b).try_norm()?.div(ab)?,
80 ))
81 }
82
83 pub fn radius(&self) -> GeopResult<T> {
85 let (a, b) = self.legs();
86 a.try_norm()?
87 .mul(b.try_norm()?)
88 .mul(self.e.sub(&self.s).try_norm()?)
89 .div(T::TWO.mul(a.prod_cross(&b).try_norm()?))
90 }
91
92 pub fn center(&self) -> GeopResult<Vector3<T>> {
94 let (a, b) = self.legs();
95 let n = a.prod_cross(&b);
96 let k = T::ONE.div(T::TWO.mul(n.norm_sq()))?;
97 let v = b.prod_scalar(a.norm_sq()).sub(&a.prod_scalar(b.norm_sq()));
98 Ok(self.m.add(&v.prod_cross(&n).prod_scalar(k)))
99 }
100
101 pub fn half_sweep_between(&self, p: &Vector3<T>, q: &Vector3<T>) -> GeopResult<(T, T)> {
109 let (c, r, n) = (self.center()?, self.radius()?, self.normal()?);
110 let chord = q.sub(p).try_norm()?;
111 let sin = chord.div(T::TWO.mul(r))?;
112 let turned = p.sub(&c).prod_cross(&q.sub(&c));
113 let cos = n.prod_dot(&turned).div(r.mul(chord))?;
114 Ok((cos, sin))
115 }
116
117 pub fn tangent(&self, at_end: bool) -> GeopResult<Vector3<T>> {
121 let chord = self.e.sub(&self.s).normalize()?;
122 let side = self.normal()?.prod_cross(&chord);
123 let (cos, sin) = self.half_sweep()?;
124 let sin = if at_end { sin } else { sin.neg() };
125 Ok(chord.prod_scalar(cos).add(&side.prod_scalar(sin)))
126 }
127}
128
129pub fn across(direction: &Vector3) -> GeopResult<[Vector3; 2]> {
134 let sharp = |v: &Vector3| v.map(|c| Scalar::from_f64(c.to_f64()));
135 let [u, v] = sharp(direction).normalize()?.across()?;
136 Ok([sharp(&u), sharp(&v)])
137}