1#![warn(missing_docs)]
18use facet::Facet;
19use fidget_core::context::Tree;
20
21pub mod types;
22use types::{Axis, Plane, Vec2, Vec3};
23
24#[derive(Clone, Facet)]
29pub struct Circle {
30 #[facet(default = Vec2::new(0.0, 0.0))]
32 pub center: Vec2,
33 #[facet(default = 1.0)]
35 pub radius: f64,
36}
37
38impl From<Circle> for Tree {
39 fn from(v: Circle) -> Self {
40 let (x, y, _) = Tree::axes();
41 ((x - v.center.x).square() + (y - v.center.y).square()).sqrt()
42 - v.radius
43 }
44}
45
46#[derive(Clone, Facet)]
48pub struct Rectangle {
49 pub lower: Vec2,
51 pub upper: Vec2,
53}
54
55impl From<Rectangle> for Tree {
56 fn from(v: Rectangle) -> Self {
57 let (x, y, _) = Tree::axes();
58 (v.lower.x - x.clone())
59 .max(x - v.upper.x)
60 .max((v.lower.y - y.clone()).max(y - v.upper.y))
61 }
62}
63
64#[derive(Clone, Facet)]
69pub struct Sphere {
70 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
72 pub center: Vec3,
73 #[facet(default = 1.0)]
75 pub radius: f64,
76}
77
78impl From<Sphere> for Tree {
79 fn from(v: Sphere) -> Self {
80 let (x, y, z) = Tree::axes();
81 ((x - v.center.x).square()
82 + (y - v.center.y).square()
83 + (z - v.center.z).square())
84 .sqrt()
85 - v.radius
86 }
87}
88
89#[derive(Clone, Facet)]
91pub struct Box {
92 pub lower: Vec3,
94 pub upper: Vec3,
96}
97
98impl From<Box> for Tree {
99 fn from(v: Box) -> Self {
100 let (x, y, z) = Tree::axes();
101 (v.lower.x - x.clone())
102 .max(x - v.upper.x)
103 .max((v.lower.y - y.clone()).max(y - v.upper.y))
104 .max((v.lower.z - z.clone()).max(z - v.upper.z))
105 }
106}
107
108#[derive(Clone, Facet)]
115pub struct Union {
116 pub input: Vec<Tree>,
118}
119
120impl From<Union> for Tree {
121 fn from(v: Union) -> Self {
122 if v.input.is_empty() {
123 Tree::constant(f64::INFINITY)
125 } else {
126 fn recurse(s: &[Tree]) -> Tree {
127 match s.len() {
128 1 => s[0].clone(),
129 n => recurse(&s[..n / 2]).min(recurse(&s[n / 2..])),
130 }
131 }
132 recurse(&v.input)
133 }
134 }
135}
136
137#[derive(Clone, Facet)]
143pub struct Blend {
144 pub a: Tree,
146 pub b: Tree,
148 pub radius: f64,
150}
151
152impl From<Blend> for Tree {
153 fn from(v: Blend) -> Self {
154 if v.radius > 0.0 {
155 v.a.clone().min(v.b.clone())
156 - 1.0 / (4.0 * v.radius)
157 * (v.radius - (v.a - v.b).abs()).max(0.0).square()
158 } else {
159 v.a.min(v.b)
160 }
161 }
162}
163
164#[derive(Clone, Facet)]
168pub struct Intersection {
169 pub input: Vec<Tree>,
171}
172
173impl From<Intersection> for Tree {
174 fn from(v: Intersection) -> Self {
175 if v.input.is_empty() {
176 Tree::constant(-f64::INFINITY)
178 } else {
179 fn recurse(s: &[Tree]) -> Tree {
180 match s.len() {
181 1 => s[0].clone(),
182 n => recurse(&s[..n / 2]).max(recurse(&s[n / 2..])),
183 }
184 }
185 recurse(&v.input)
186 }
187 }
188}
189
190#[derive(Clone, Facet)]
192pub struct Inverse {
193 pub shape: Tree,
195}
196
197impl From<Inverse> for Tree {
198 fn from(v: Inverse) -> Self {
199 -v.shape
200 }
201}
202
203#[derive(Clone, Facet)]
205pub struct Difference {
206 pub shape: Tree,
208 pub cutout: Tree,
210}
211
212impl From<Difference> for Tree {
213 fn from(v: Difference) -> Self {
214 v.shape.max(-v.cutout)
215 }
216}
217
218#[derive(Clone, Facet)]
223pub struct Move {
224 pub shape: Tree,
226 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
228 pub offset: Vec3,
229}
230
231impl From<Move> for Tree {
232 fn from(v: Move) -> Self {
233 v.shape.remap_affine(nalgebra::convert(
234 nalgebra::Translation3::<f64>::new(
235 -v.offset.x,
236 -v.offset.y,
237 -v.offset.z,
238 ),
239 ))
240 }
241}
242
243#[derive(Clone, Facet)]
245pub struct Scale {
246 pub shape: Tree,
248 #[facet(default = Vec3::new(1.0, 1.0, 1.0))]
250 pub scale: Vec3,
251}
252
253impl From<Scale> for Tree {
254 fn from(v: Scale) -> Self {
255 v.shape
256 .remap_affine(nalgebra::convert(nalgebra::Scale3::<f64>::new(
257 1.0 / v.scale.x,
258 1.0 / v.scale.y,
259 1.0 / v.scale.z,
260 )))
261 }
262}
263
264#[derive(Clone, Facet)]
266pub struct ScaleUniform {
267 pub shape: Tree,
269 #[facet(default = 1.0)]
271 pub scale: f64,
272}
273
274impl From<ScaleUniform> for Tree {
275 fn from(v: ScaleUniform) -> Self {
276 let s = 1.0 / v.scale;
277 v.shape
278 .remap_affine(nalgebra::convert(nalgebra::Scale3::<f64>::new(
279 s, s, s,
280 )))
281 }
282}
283
284#[derive(Clone, Facet)]
286pub struct Reflect {
287 pub shape: Tree,
289
290 #[facet(default = Plane::YZ)]
292 pub plane: Plane,
293}
294
295impl From<Reflect> for Tree {
296 fn from(v: Reflect) -> Self {
297 let a = v.plane.axis.vec();
298 let (x, y, z) = Tree::axes();
299 let d = a.x * x.clone() + a.y * y.clone() + a.z * z.clone()
300 - v.plane.offset;
301 let scale: Tree = 2.0 * d;
302 v.shape.remap_xyz(
306 x - scale.clone() * a.x,
307 y - scale.clone() * a.y,
308 z - scale * a.z,
309 )
310 }
311}
312
313#[derive(Clone, Facet)]
315pub struct ReflectX {
316 pub shape: Tree,
318
319 #[facet(default = 0.0)]
321 pub offset: f64,
322}
323
324impl From<ReflectX> for Tree {
325 fn from(v: ReflectX) -> Self {
326 Reflect {
327 shape: v.shape,
328 plane: Plane {
329 axis: Axis::X,
330 offset: v.offset,
331 },
332 }
333 .into()
334 }
335}
336
337#[derive(Clone, Facet)]
339pub struct ReflectY {
340 pub shape: Tree,
342
343 #[facet(default = 0.0)]
345 pub offset: f64,
346}
347
348impl From<ReflectY> for Tree {
349 fn from(v: ReflectY) -> Self {
350 Reflect {
351 shape: v.shape,
352 plane: Plane {
353 axis: Axis::Y,
354 offset: v.offset,
355 },
356 }
357 .into()
358 }
359}
360
361#[derive(Clone, Facet)]
363pub struct ReflectZ {
364 pub shape: Tree,
366
367 #[facet(default = 0.0)]
369 pub offset: f64,
370}
371
372impl From<ReflectZ> for Tree {
373 fn from(v: ReflectZ) -> Self {
374 Reflect {
375 shape: v.shape,
376 plane: Plane {
377 axis: Axis::Z,
378 offset: v.offset,
379 },
380 }
381 .into()
382 }
383}
384
385#[derive(Clone, Facet)]
387pub struct Rotate {
388 pub shape: Tree,
390
391 #[facet(default = Axis::Z)]
393 pub axis: Axis,
394
395 #[facet(default = 0.0)]
397 pub angle: f64,
398
399 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
401 pub center: Vec3,
402}
403
404impl From<Rotate> for Tree {
405 fn from(v: Rotate) -> Self {
406 let shape = Tree::from(Move {
407 shape: v.shape,
408 offset: -v.center,
409 });
410 let d = -v.angle.to_radians();
411 let axis = v.axis.vec();
412 let shape = shape.remap_affine(nalgebra::convert(
413 nalgebra::Rotation3::<f64>::new(nalgebra::Vector3::from(d * *axis)),
414 ));
415 Move {
416 shape,
417 offset: v.center,
418 }
419 .into()
420 }
421}
422
423#[derive(Clone, Facet)]
425pub struct RotateX {
426 pub shape: Tree,
428
429 #[facet(default = 0.0)]
431 pub angle: f64,
432
433 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
435 pub center: Vec3,
436}
437
438impl From<RotateX> for Tree {
439 fn from(v: RotateX) -> Self {
440 Rotate {
441 shape: v.shape,
442 angle: v.angle,
443 center: v.center,
444 axis: Axis::X,
445 }
446 .into()
447 }
448}
449
450#[derive(Clone, Facet)]
452pub struct RotateY {
453 pub shape: Tree,
455
456 #[facet(default = 0.0)]
458 pub angle: f64,
459
460 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
462 pub center: Vec3,
463}
464
465impl From<RotateY> for Tree {
466 fn from(v: RotateY) -> Self {
467 Rotate {
468 shape: v.shape,
469 angle: v.angle,
470 center: v.center,
471 axis: Axis::Y,
472 }
473 .into()
474 }
475}
476
477#[derive(Clone, Facet)]
479pub struct RotateZ {
480 pub shape: Tree,
482
483 #[facet(default = 0.0)]
485 pub angle: f64,
486
487 #[facet(default = Vec3::new(0.0, 0.0, 0.0))]
489 pub center: Vec3,
490}
491
492impl From<RotateZ> for Tree {
493 fn from(v: RotateZ) -> Self {
494 Rotate {
495 shape: v.shape,
496 angle: v.angle,
497 center: v.center,
498 axis: Axis::Z,
499 }
500 .into()
501 }
502}
503
504#[derive(Clone, Facet)]
508pub struct RevolveY {
509 pub shape: Tree,
511 #[facet(default = 0.0)]
513 pub offset: f64,
514}
515
516impl From<RevolveY> for Tree {
517 fn from(v: RevolveY) -> Self {
518 let offset = Vec3::new(-v.offset, 0.0, 0.0);
519 let shape = Tree::from(Move {
520 shape: v.shape.clone(),
521 offset: -offset,
522 });
523 let (x, y, z) = Tree::axes();
524 let r = (x.square() + y.square()).sqrt();
525 let shape = shape.remap_xyz(r, y, z);
526 Move { shape, offset }.into()
527 }
528}
529
530#[derive(Clone, Facet)]
532pub struct ExtrudeZ {
533 pub shape: Tree,
535 #[facet(default = 0.0)]
537 pub lower: f64,
538 #[facet(default = 1.0)]
540 pub upper: f64,
541}
542
543impl From<ExtrudeZ> for Tree {
544 fn from(v: ExtrudeZ) -> Self {
545 let (x, y, z) = Tree::axes();
546 let t = v.shape.remap_xyz(x, y, Tree::constant(0.0));
547 t.max((v.lower - z.clone()).max(z - v.upper))
548 }
549}
550
551#[derive(Clone, Facet)]
553pub struct LoftZ {
554 pub a: Tree,
556 pub b: Tree,
558 #[facet(default = 0.0)]
560 pub lower: f64,
561 #[facet(default = 1.0)]
563 pub upper: f64,
564}
565
566impl From<LoftZ> for Tree {
567 fn from(v: LoftZ) -> Self {
568 let (x, y, z) = Tree::axes();
569 let ta = v.a.remap_xyz(x.clone(), y.clone(), Tree::constant(0.0));
570 let tb = v.b.remap_xyz(x, y, Tree::constant(0.0));
571 let t = ((z.clone() - v.lower) * tb + (v.upper - z.clone()) * ta)
572 / (v.upper - v.lower);
573 t.max((v.lower - z.clone()).max(z - v.upper))
574 }
575}
576
577pub trait ShapeVisitor {
581 fn visit<T: Facet<'static> + Clone + Send + Sync + Into<Tree> + 'static>(
583 &mut self,
584 );
585}
586
587pub fn visit_shapes<V: ShapeVisitor>(visitor: &mut V) {
589 visitor.visit::<Sphere>();
590 visitor.visit::<Box>();
591 visitor.visit::<Plane>();
592
593 visitor.visit::<Circle>();
594 visitor.visit::<Rectangle>();
595
596 visitor.visit::<Move>();
597 visitor.visit::<Scale>();
598 visitor.visit::<ScaleUniform>();
599 visitor.visit::<Reflect>();
600 visitor.visit::<ReflectX>();
601 visitor.visit::<ReflectY>();
602 visitor.visit::<ReflectZ>();
603 visitor.visit::<Rotate>();
604 visitor.visit::<RotateX>();
605 visitor.visit::<RotateY>();
606 visitor.visit::<RotateZ>();
607 visitor.visit::<RevolveY>();
608 visitor.visit::<ExtrudeZ>();
609 visitor.visit::<LoftZ>();
610
611 visitor.visit::<Union>();
612 visitor.visit::<Blend>();
613 visitor.visit::<Intersection>();
614 visitor.visit::<Difference>();
615 visitor.visit::<Inverse>();
616}
617
618#[cfg(test)]
621mod test {
622 use super::*;
623 use fidget_core::Context;
624
625 #[test]
626 fn circle_docstring() {
627 assert_eq!(Circle::SHAPE.doc, &[" 2D circle"]);
628 }
629
630 #[test]
631 fn transform_order() {
632 let x = Tree::x();
633 let moved: Tree = Move {
634 shape: x,
635 offset: Vec3::new(-1.0, 0.0, 0.0),
636 }
637 .into();
638 let mut ctx = Context::new();
639 let cm = ctx.import(&moved);
640 assert_eq!(ctx.eval_xyz(cm, 0.0, 0.0, 0.0).unwrap(), 1.0);
641 assert_eq!(ctx.eval_xyz(cm, 0.0, 1.0, 0.0).unwrap(), 1.0);
642 assert_eq!(ctx.eval_xyz(cm, -1.0, 0.0, 0.0).unwrap(), 0.0);
643
644 let rotated: Tree = RotateZ {
645 shape: moved,
646 angle: 90.0,
647 center: Vec3::new(0.0, 0.0, 0.0),
648 }
649 .into();
650 let cr = ctx.import(&rotated);
651 assert_eq!(ctx.eval_xyz(cr, 0.0, 0.0, 0.0).unwrap(), 1.0);
652 assert_eq!(ctx.eval_xyz(cr, 0.0, -1.0, 0.0).unwrap(), 0.0);
653 assert_eq!(ctx.eval_xyz(cr, 0.0, 1.0, 0.0).unwrap(), 2.0);
654 }
655
656 #[test]
657 fn scale_default_fn() {
658 let facet::Type::User(facet::UserType::Struct(s)) = Scale::SHAPE.ty
659 else {
660 panic!();
661 };
662 for f in s.fields {
663 if f.name == "scale" {
664 let Some(f) = f.vtable.default_fn else {
665 panic!()
666 };
667 let mut v = std::mem::MaybeUninit::<Vec3>::uninit();
668 let ptr = facet::PtrUninit::new(&mut v);
669 let v: Vec3 = unsafe { *f(ptr).as_ptr() };
670 assert_eq!(v.x, 1.0);
671 assert_eq!(v.y, 1.0);
672 assert_eq!(v.z, 1.0);
673 } else {
674 assert!(f.vtable.default_fn.is_none());
675 }
676 }
677 }
678
679 #[test]
680 fn validate_shapes() {
681 struct Visitor;
682 impl ShapeVisitor for Visitor {
683 fn visit<
684 T: Facet<'static> + Clone + Send + Sync + Into<Tree> + 'static,
685 >(
686 &mut self,
687 ) {
688 let facet::Type::User(facet::UserType::Struct(s)) = T::SHAPE.ty
689 else {
690 panic!("must be a struct-shaped type");
691 };
692 for f in s.fields {
693 if types::Type::try_from(f.shape().id).is_err() {
694 panic!("unknown type: {}", f.shape());
695 }
696 }
697 }
698 }
699 let mut v = Visitor;
700 visit_shapes(&mut v);
701 }
702}