1use crate::{self as truck_stepio};
2use derive_more::*;
3use serde::{Deserialize, Serialize};
4use truck_derivers::{DisplayByStep, StepCurve, StepLength, StepSurface};
5
6pub mod re_exports {
8 pub use truck_geometry::prelude::*;
9 pub use truck_polymesh::*;
10}
11pub use re_exports::*;
12
13pub type StepConvertingError = Box<dyn std::error::Error>;
15
16pub type Ellipse<P, M> = Processor<TrimmedCurve<UnitCircle<P>>, M>;
18pub type Hyperbola<P, M> = Processor<TrimmedCurve<UnitHyperbola<P>>, M>;
20pub type Parabola<P, M> = Processor<TrimmedCurve<UnitParabola<P>>, M>;
22pub type SphericalSurface = Processor<Sphere, Matrix4>;
24pub type CylindricalSurface = Processor<RevolutedCurve<Line<Point3>>, Matrix4>;
26pub type ToroidalSurface = Processor<Torus, Matrix4>;
28pub type ConicalSurface = Processor<RevolutedCurve<Line<Point3>>, Matrix4>;
30pub type StepExtrudedCurve = ExtrudedCurve<Curve3D, Vector3>;
32pub type StepRevolutedCurve = Processor<RevolutedCurve<Curve3D>, Matrix4>;
34pub type PCurve = truck_geometry::prelude::PCurve<Box<Curve2D>, Box<Surface>>;
36
37#[derive(
39 Clone,
40 Copy,
41 Debug,
42 PartialEq,
43 From,
44 Serialize,
45 Deserialize,
46 ParametricCurve,
47 BoundedCurve,
48 Cut,
49 Invertible,
50 ParameterDivision1D,
51 SearchParameterD1,
52 SearchNearestParameterD1,
53 TransformedM3,
54 StepLength,
55 DisplayByStep,
56 StepCurve,
57)]
58pub enum Conic2D {
59 Ellipse(Ellipse<Point2, Matrix3>),
60 Hyperbola(Hyperbola<Point2, Matrix3>),
61 Parabola(Parabola<Point2, Matrix3>),
62}
63
64#[derive(
66 Clone,
67 Debug,
68 PartialEq,
69 From,
70 Serialize,
71 Deserialize,
72 ParametricCurve,
73 BoundedCurve,
74 Cut,
75 Invertible,
76 ParameterDivision1D,
77 SearchParameterD1,
78 SearchNearestParameterD1,
79 TransformedM3,
80 StepLength,
81 DisplayByStep,
82 StepCurve,
83)]
84
85pub enum Curve2D {
86 Line(Line<Point2>),
87 Polyline(PolylineCurve<Point2>),
88 Conic(Conic2D),
89 BSplineCurve(BSplineCurve<Point2>),
90 NurbsCurve(NurbsCurve<Vector3>),
91}
92
93#[derive(
95 Clone,
96 Copy,
97 Debug,
98 PartialEq,
99 From,
100 Serialize,
101 Deserialize,
102 ParametricCurve,
103 BoundedCurve,
104 Cut,
105 Invertible,
106 ParameterDivision1D,
107 SearchParameterD1,
108 SearchNearestParameterD1,
109 TransformedM4,
110 StepLength,
111 DisplayByStep,
112 StepCurve,
113)]
114pub enum Conic3D {
115 Ellipse(Ellipse<Point3, Matrix4>),
116 Hyperbola(Hyperbola<Point3, Matrix4>),
117 Parabola(Parabola<Point3, Matrix4>),
118}
119
120#[derive(
122 Clone,
123 Debug,
124 PartialEq,
125 From,
126 Serialize,
127 Deserialize,
128 ParametricCurve,
129 BoundedCurve,
130 Cut,
131 Invertible,
132 ParameterDivision1D,
133 SearchParameterD1,
134 SearchNearestParameterD1,
135 TransformedM4,
136 StepLength,
137 DisplayByStep,
138 StepCurve,
139)]
140pub enum Curve3D {
141 Line(Line<Point3>),
142 Polyline(PolylineCurve<Point3>),
143 Conic(Conic3D),
144 BSplineCurve(BSplineCurve<Point3>),
145 PCurve(PCurve),
146 NurbsCurve(NurbsCurve<Vector4>),
147}
148
149#[derive(
151 Clone,
152 Copy,
153 Debug,
154 PartialEq,
155 Serialize,
156 Deserialize,
157 ParametricSurface3D,
158 ParameterDivision2D,
159 SearchParameterD2,
160 SearchNearestParameterD2,
161 Invertible,
162 TransformedM4,
163 StepLength,
164 DisplayByStep,
165 StepSurface,
166)]
167pub enum ElementarySurface {
168 Plane(Plane),
169 Sphere(SphericalSurface),
170 CylindricalSurface(CylindricalSurface),
171 ToroidalSurface(ToroidalSurface),
172 ConicalSurface(ConicalSurface),
173}
174
175#[derive(
177 Clone,
178 Debug,
179 PartialEq,
180 Serialize,
181 Deserialize,
182 ParametricSurface3D,
183 ParameterDivision2D,
184 SearchParameterD2,
185 SearchNearestParameterD2,
186 Invertible,
187 TransformedM4,
188 StepLength,
189 DisplayByStep,
190 StepSurface,
191)]
192pub enum SweptCurve {
193 ExtrudedCurve(StepExtrudedCurve),
194 RevolutedCurve(StepRevolutedCurve),
195}
196
197#[derive(
199 Clone,
200 Debug,
201 PartialEq,
202 Serialize,
203 Deserialize,
204 ParametricSurface3D,
205 ParameterDivision2D,
206 SearchParameterD2,
207 SearchNearestParameterD2,
208 Invertible,
209 TransformedM4,
210 StepLength,
211 StepSurface,
212)]
213pub enum Surface {
214 ElementarySurface(Box<ElementarySurface>),
215 SweptCurve(Box<SweptCurve>),
216 BSplineSurface(Box<BSplineSurface<Point3>>),
217 NurbsSurface(Box<NurbsSurface<Vector4>>),
218}
219
220impl truck_stepio::out::DisplayByStep for Surface {
221 fn fmt(&self, idx: usize, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 use Surface::*;
223 match self {
224 ElementarySurface(x) => x.fmt(idx, f),
225 SweptCurve(x) => x.fmt(idx, f),
226 BSplineSurface(x) => x.fmt(idx, f),
227 NurbsSurface(x) => x.fmt(idx, f),
228 }
229 }
230}
231
232#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, StepSurface)]
234pub struct Sphere(pub truck_geometry::prelude::Sphere);
235
236impl truck_stepio::out::StepSurface for Processor<Sphere, Matrix4> {
237 #[inline(always)]
238 fn same_sense(&self) -> bool { self.orientation() }
239}
240
241mod sphere {
242 use super::*;
243 use std::f64::consts::PI;
244 use std::ops::Bound;
245 impl ParametricSurface for Sphere {
246 type Point = Point3;
247 type Vector = Vector3;
248 #[inline]
249 fn subs(&self, u: f64, v: f64) -> Point3 { self.0.subs(PI / 2.0 - v, u) }
250 #[inline]
251 fn uder(&self, u: f64, v: f64) -> Vector3 { self.0.vder(PI / 2.0 - v, u) }
252 #[inline]
253 fn vder(&self, u: f64, v: f64) -> Vector3 { -self.0.uder(PI / 2.0 - v, u) }
254 #[inline]
255 fn uuder(&self, u: f64, v: f64) -> Vector3 { self.0.vvder(PI / 2.0 - v, u) }
256 #[inline]
257 fn uvder(&self, u: f64, v: f64) -> Vector3 { -self.0.uvder(PI / 2.0 - v, u) }
258 #[inline]
259 fn vvder(&self, u: f64, v: f64) -> Vector3 { self.0.uuder(PI / 2.0 - v, u) }
260 #[inline]
261 fn parameter_range(&self) -> (ParameterRange, ParameterRange) {
262 (
263 (Bound::Included(0.0), Bound::Excluded(2.0 * PI)),
264 (Bound::Included(-PI / 2.0), Bound::Excluded(PI / 2.0)),
265 )
266 }
267 #[inline]
268 fn u_period(&self) -> Option<f64> { Some(2.0 * PI) }
269 }
270 impl ParametricSurface3D for Sphere {
271 #[inline]
272 fn normal(&self, u: f64, v: f64) -> Vector3 { self.0.normal(PI / 2.0 - v, u) }
273 }
274 impl SearchNearestParameter<D2> for Sphere {
275 type Point = Point3;
276 #[inline]
277 fn search_nearest_parameter<H: Into<SPHint2D>>(
278 &self,
279 point: Self::Point,
280 hint: H,
281 trials: usize,
282 ) -> Option<(f64, f64)> {
283 self.0
284 .search_nearest_parameter(point, hint, trials)
285 .map(|(u, v)| (v, PI / 2.0 - u))
286 }
287 }
288 impl SearchParameter<D2> for Sphere {
289 type Point = Point3;
290 #[inline]
291 fn search_parameter<H: Into<SPHint2D>>(
292 &self,
293 point: Self::Point,
294 hint: H,
295 trials: usize,
296 ) -> Option<(f64, f64)> {
297 self.0
298 .search_parameter(point, hint, trials)
299 .map(|(u, v)| (v, PI / 2.0 - u))
300 }
301 }
302 impl ParameterDivision2D for Sphere {
303 #[inline]
304 fn parameter_division(
305 &self,
306 ((u0, u1), (v0, v1)): ((f64, f64), (f64, f64)),
307 tol: f64,
308 ) -> (Vec<f64>, Vec<f64>) {
309 let range = ((PI / 2.0 - v1, PI / 2.0 - v0), (u0, u1));
310 let (udiv0, vdiv0) = self.0.parameter_division(range, tol);
311 let vdiv = udiv0.into_iter().map(|u| PI / 2.0 - u).collect();
312 (vdiv0, vdiv)
313 }
314 }
315
316 #[cfg(test)]
317 proptest::proptest! {
318 #[test]
319 fn surface(
320 center in proptest::array::uniform3(-100.0f64..100.0f64),
321 radius in 0.1f64..100.0f64,
322 (u, v) in (0.0..=2.0 * PI, -PI / 2.0..=PI / 2.0),
323 ) {
324 const EPS: f64 = 1.0e-3;
325 let sphere = Sphere(truck_geometry::prelude::Sphere::new(center.into(), radius));
326
327 let uder0 = sphere.uder(u, v);
328 let uder1 = (sphere.subs(u + EPS, v) - sphere.subs(u - EPS, v)) / (2.0 * EPS);
329 assert!(
330 (uder0 - uder1).magnitude2() < EPS,
331 "uder failed: {uder0:?}, {uder1:?}"
332 );
333
334 let vder0 = sphere.vder(u, v);
335 let vder1 = (sphere.subs(u, v + EPS) - sphere.subs(u, v - EPS)) / (2.0 * EPS);
336 assert!(
337 (vder0 - vder1).magnitude2() < EPS,
338 "vder failed: {vder0:?}, {vder1:?}"
339 );
340
341 let uuder0 = sphere.uuder(u, v);
342 let uuder1 = (sphere.uder(u + EPS, v) - sphere.uder(u - EPS, v)) / (2.0 * EPS);
343 assert!(
344 (uuder0 - uuder1).magnitude2() < EPS,
345 "uuder failed: {uuder0:?}, {uuder1:?}"
346 );
347
348 let uvder0 = sphere.uvder(u, v);
349 let uvder1 = (sphere.uder(u, v + EPS) - sphere.uder(u, v - EPS)) / (2.0 * EPS);
350 assert!(
351 (uvder0 - uvder1).magnitude2() < EPS,
352 "uvder failed: {uvder0:?}, {uvder1:?}"
353 );
354
355 let vvder0 = sphere.vvder(u, v);
356 let vvder1 = (sphere.vder(u, v + EPS) - sphere.vder(u, v - EPS)) / (2.0 * EPS);
357 assert!(
358 (vvder0 - vvder1).magnitude2() < EPS,
359 "vvder failed: {vvder0:?}, {vvder1:?}"
360 );
361
362 let n0 = sphere.normal(u, v);
363 let n1 = sphere.uder(u, v).cross(sphere.vder(u, v)).normalize();
364 assert!(
365 (n0 - n1).magnitude2() < EPS,
366 "normal failed: {n0:?}, {n1:?}"
367 );
368 }
369 }
370}
371
372impl truck_stepio::out::ConstStepLength for Processor<Sphere, Matrix4> {
373 const LENGTH: usize = Processor::<truck_geometry::prelude::Sphere, Matrix4>::LENGTH;
374}
375impl truck_stepio::out::StepLength for Processor<Sphere, Matrix4> {
376 fn step_length(&self) -> usize { <Self as truck_stepio::out::ConstStepLength>::LENGTH }
377}
378impl truck_stepio::out::DisplayByStep for Processor<Sphere, Matrix4> {
379 fn fmt(&self, idx: usize, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 Processor::new(self.entity().0)
381 .transformed(*self.transform())
382 .fmt(idx, f)
383 }
384}
385
386mod from_pcurve {
388 use super::{Curve2D, Curve3D, Surface};
389 use truck_geometry::prelude::*;
390
391 impl From<PCurve<Line<Point2>, Surface>> for Curve3D {
392 fn from(value: PCurve<Line<Point2>, Surface>) -> Self {
393 let (line, surface) = value.decompose();
394 Curve3D::PCurve(PCurve::new(Curve2D::Line(line).into(), surface.into()))
395 }
396 }
397}