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