1use crate::crispy::fnv1a_hash;
9use crate::types::SdfNode;
10use std::sync::Arc;
11
12pub type TreePath = Vec<usize>;
19
20#[derive(Debug, Clone)]
22pub enum DiffOp {
23 Replace {
25 path: TreePath,
27 old_hash: u64,
29 new_node: Arc<SdfNode>,
31 },
32 Insert {
34 path: TreePath,
36 old_hash: u64,
38 wrapper: Arc<SdfNode>,
40 },
41 Delete {
43 path: TreePath,
45 old_hash: u64,
47 child_index: usize,
49 },
50}
51
52#[derive(Debug, Clone)]
54pub enum DiffError {
55 InvalidPath(TreePath),
57 HashMismatch {
59 path: TreePath,
61 expected: u64,
63 actual: u64,
65 },
66}
67
68impl std::fmt::Display for DiffError {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::InvalidPath(p) => write!(f, "Invalid path: {:?}", p),
72 Self::HashMismatch {
73 path,
74 expected,
75 actual,
76 } => write!(
77 f,
78 "Hash mismatch at {:?}: expected {:#x}, got {:#x}",
79 path, expected, actual
80 ),
81 }
82 }
83}
84
85#[derive(Debug, Clone)]
87pub struct TreePatch {
88 pub ops: Vec<DiffOp>,
90 pub content_hash: u64,
92}
93
94pub fn tree_hash(node: &SdfNode) -> u64 {
101 let serialized = format!("{:?}", node);
102 fnv1a_hash(serialized.as_bytes())
103}
104
105pub fn tree_diff(old: &SdfNode, new: &SdfNode) -> TreePatch {
112 let mut ops = Vec::new();
113 diff_recursive(old, new, &mut vec![], &mut ops);
114
115 let mut hash_data = Vec::with_capacity(ops.len() * 8);
116 for (i, op) in ops.iter().enumerate() {
117 hash_data.extend_from_slice(&(i as u64).to_le_bytes());
118 match op {
119 DiffOp::Replace { old_hash, .. }
120 | DiffOp::Insert { old_hash, .. }
121 | DiffOp::Delete { old_hash, .. } => {
122 hash_data.extend_from_slice(&old_hash.to_le_bytes());
123 }
124 }
125 }
126 let content_hash = if hash_data.is_empty() {
127 0
128 } else {
129 fnv1a_hash(&hash_data)
130 };
131
132 TreePatch { ops, content_hash }
133}
134
135fn diff_recursive(old: &SdfNode, new: &SdfNode, path: &mut Vec<usize>, ops: &mut Vec<DiffOp>) {
137 let old_hash = tree_hash(old);
138 let new_hash = tree_hash(new);
139
140 if old_hash == new_hash {
142 return;
143 }
144
145 let old_children = get_children(old);
147 let new_children = get_children(new);
148
149 if same_variant(old, new)
150 && old_children.len() == new_children.len()
151 && !old_children.is_empty()
152 {
153 let mut grafted = old.clone();
160 for (i, child) in new_children.iter().enumerate() {
161 grafted = replace_child(&grafted, i, Arc::new((*child).clone()));
162 }
163
164 if tree_hash(&grafted) == new_hash {
165 for i in 0..old_children.len() {
166 path.push(i);
167 diff_recursive(old_children[i], new_children[i], path, ops);
168 path.pop();
169 }
170 } else {
171 ops.push(DiffOp::Replace {
172 path: path.clone(),
173 old_hash,
174 new_node: Arc::new(new.clone()),
175 });
176 }
177 } else {
178 ops.push(DiffOp::Replace {
180 path: path.clone(),
181 old_hash,
182 new_node: Arc::new(new.clone()),
183 });
184 }
185}
186
187fn same_variant(a: &SdfNode, b: &SdfNode) -> bool {
189 std::mem::discriminant(a) == std::mem::discriminant(b)
190}
191
192fn get_children(node: &SdfNode) -> Vec<&SdfNode> {
194 match node {
195 SdfNode::Union { a, b }
197 | SdfNode::Intersection { a, b }
198 | SdfNode::Subtraction { a, b }
199 | SdfNode::SmoothUnion { a, b, .. }
200 | SdfNode::SmoothIntersection { a, b, .. }
201 | SdfNode::SmoothSubtraction { a, b, .. }
202 | SdfNode::ChamferUnion { a, b, .. }
203 | SdfNode::ChamferIntersection { a, b, .. }
204 | SdfNode::ChamferSubtraction { a, b, .. }
205 | SdfNode::StairsUnion { a, b, .. }
206 | SdfNode::StairsIntersection { a, b, .. }
207 | SdfNode::StairsSubtraction { a, b, .. }
208 | SdfNode::XOR { a, b }
209 | SdfNode::Morph { a, b, .. }
210 | SdfNode::ColumnsUnion { a, b, .. }
211 | SdfNode::ColumnsIntersection { a, b, .. }
212 | SdfNode::ColumnsSubtraction { a, b, .. }
213 | SdfNode::Pipe { a, b, .. }
214 | SdfNode::Engrave { a, b, .. }
215 | SdfNode::Groove { a, b, .. }
216 | SdfNode::Tongue { a, b, .. }
217 | SdfNode::ExpSmoothUnion { a, b, .. }
218 | SdfNode::ExpSmoothIntersection { a, b, .. }
219 | SdfNode::ExpSmoothSubtraction { a, b, .. } => vec![a.as_ref(), b.as_ref()],
220
221 SdfNode::Translate { child, .. }
223 | SdfNode::Rotate { child, .. }
224 | SdfNode::Scale { child, .. }
225 | SdfNode::ScaleNonUniform { child, .. }
226 | SdfNode::ProjectiveTransform { child, .. }
227 | SdfNode::LatticeDeform { child, .. }
228 | SdfNode::SdfSkinning { child, .. }
229 | SdfNode::Twist { child, .. }
230 | SdfNode::Bend { child, .. }
231 | SdfNode::RepeatInfinite { child, .. }
232 | SdfNode::RepeatFinite { child, .. }
233 | SdfNode::Noise { child, .. }
234 | SdfNode::Round { child, .. }
235 | SdfNode::Onion { child, .. }
236 | SdfNode::Elongate { child, .. }
237 | SdfNode::Mirror { child, .. }
238 | SdfNode::OctantMirror { child }
239 | SdfNode::Revolution { child, .. }
240 | SdfNode::Extrude { child, .. }
241 | SdfNode::Taper { child, .. }
242 | SdfNode::Displacement { child, .. }
243 | SdfNode::SineDisplacement { child, .. }
244 | SdfNode::PolarRepeat { child, .. }
245 | SdfNode::SweepBezier { child, .. }
246 | SdfNode::IcosahedralSymmetry { child }
247 | SdfNode::IFS { child, .. }
248 | SdfNode::HeightmapDisplacement { child, .. }
249 | SdfNode::SurfaceRoughness { child, .. }
250 | SdfNode::Shear { child, .. }
251 | SdfNode::Animated { child, .. }
252 | SdfNode::WithMaterial { child, .. } => vec![child.as_ref()],
253
254 _ => vec![],
256 }
257}
258
259pub fn apply_patch(tree: &SdfNode, patch: &TreePatch) -> Result<SdfNode, DiffError> {
263 let mut result = tree.clone();
264 for op in &patch.ops {
265 let at = |e: DiffError, op_path: &TreePath| match e {
268 DiffError::HashMismatch {
269 expected, actual, ..
270 } => DiffError::HashMismatch {
271 path: op_path.clone(),
272 expected,
273 actual,
274 },
275 other => other,
276 };
277 match op {
278 DiffOp::Replace {
279 path,
280 old_hash,
281 new_node,
282 } => {
283 result =
284 replace_at_path(&result, path, *old_hash, new_node).map_err(|e| at(e, path))?;
285 }
286 DiffOp::Insert {
287 path,
288 old_hash,
289 wrapper,
290 } => {
291 result =
292 insert_at_path(&result, path, *old_hash, wrapper).map_err(|e| at(e, path))?;
293 }
294 DiffOp::Delete {
295 path,
296 old_hash,
297 child_index,
298 } => {
299 result = delete_at_path(&result, path, *old_hash, *child_index)
300 .map_err(|e| at(e, path))?;
301 }
302 }
303 }
304 Ok(result)
305}
306
307pub fn invert_patch(original: &SdfNode, patch: &TreePatch) -> Result<TreePatch, DiffError> {
314 let result = apply_patch(original, patch)?;
315 Ok(tree_diff(&result, original))
316}
317
318pub fn merge_patches(
323 original: &SdfNode,
324 patch_a: &TreePatch,
325 patch_b: &TreePatch,
326) -> Result<TreePatch, DiffError> {
327 let after_a = apply_patch(original, patch_a)?;
328 let after_b = apply_patch(&after_a, patch_b)?;
329 Ok(tree_diff(original, &after_b))
330}
331
332impl TreePatch {
334 pub fn op_count(&self) -> usize {
336 self.ops.len()
337 }
338
339 pub fn is_empty(&self) -> bool {
341 self.ops.is_empty()
342 }
343}
344
345fn replace_at_path(
347 node: &SdfNode,
348 path: &[usize],
349 expected_hash: u64,
350 replacement: &Arc<SdfNode>,
351) -> Result<SdfNode, DiffError> {
352 if path.is_empty() {
353 let actual_hash = tree_hash(node);
354 if actual_hash != expected_hash {
355 return Err(DiffError::HashMismatch {
356 path: vec![],
357 expected: expected_hash,
358 actual: actual_hash,
359 });
360 }
361 return Ok(replacement.as_ref().clone());
362 }
363
364 let idx = path[0];
365 let rest = &path[1..];
366 let children = get_children(node);
367
368 if idx >= children.len() {
369 return Err(DiffError::InvalidPath(path.to_vec()));
370 }
371
372 let new_child = Arc::new(replace_at_path(
373 children[idx],
374 rest,
375 expected_hash,
376 replacement,
377 )?);
378 Ok(replace_child(node, idx, new_child))
379}
380
381fn insert_at_path(
383 node: &SdfNode,
384 path: &[usize],
385 expected_hash: u64,
386 wrapper: &Arc<SdfNode>,
387) -> Result<SdfNode, DiffError> {
388 if path.is_empty() {
389 let actual_hash = tree_hash(node);
390 if actual_hash != expected_hash {
391 return Err(DiffError::HashMismatch {
392 path: vec![],
393 expected: expected_hash,
394 actual: actual_hash,
395 });
396 }
397 let children = get_children(wrapper);
399 if children.len() == 1 {
400 return Ok(replace_child(wrapper, 0, Arc::new(node.clone())));
401 }
402 Ok(replace_child(wrapper, 0, Arc::new(node.clone())))
404 } else {
405 let idx = path[0];
406 let rest = &path[1..];
407 let children = get_children(node);
408 if idx >= children.len() {
409 return Err(DiffError::InvalidPath(path.to_vec()));
410 }
411 let new_child = Arc::new(insert_at_path(children[idx], rest, expected_hash, wrapper)?);
412 Ok(replace_child(node, idx, new_child))
413 }
414}
415
416fn delete_at_path(
418 node: &SdfNode,
419 path: &[usize],
420 expected_hash: u64,
421 child_index: usize,
422) -> Result<SdfNode, DiffError> {
423 if path.is_empty() {
424 let actual_hash = tree_hash(node);
425 if actual_hash != expected_hash {
426 return Err(DiffError::HashMismatch {
427 path: vec![],
428 expected: expected_hash,
429 actual: actual_hash,
430 });
431 }
432 let children = get_children(node);
433 if child_index >= children.len() {
434 return Err(DiffError::InvalidPath(vec![child_index]));
435 }
436 Ok(children[child_index].clone())
437 } else {
438 let idx = path[0];
439 let rest = &path[1..];
440 let children = get_children(node);
441 if idx >= children.len() {
442 return Err(DiffError::InvalidPath(path.to_vec()));
443 }
444 let new_child = Arc::new(delete_at_path(
445 children[idx],
446 rest,
447 expected_hash,
448 child_index,
449 )?);
450 Ok(replace_child(node, idx, new_child))
451 }
452}
453
454fn replace_child(node: &SdfNode, child_idx: usize, new_child: Arc<SdfNode>) -> SdfNode {
456 match node {
457 SdfNode::Union { a, b } => {
459 if child_idx == 0 {
460 SdfNode::Union {
461 a: new_child,
462 b: b.clone(),
463 }
464 } else {
465 SdfNode::Union {
466 a: a.clone(),
467 b: new_child,
468 }
469 }
470 }
471 SdfNode::Intersection { a, b } => {
472 if child_idx == 0 {
473 SdfNode::Intersection {
474 a: new_child,
475 b: b.clone(),
476 }
477 } else {
478 SdfNode::Intersection {
479 a: a.clone(),
480 b: new_child,
481 }
482 }
483 }
484 SdfNode::Subtraction { a, b } => {
485 if child_idx == 0 {
486 SdfNode::Subtraction {
487 a: new_child,
488 b: b.clone(),
489 }
490 } else {
491 SdfNode::Subtraction {
492 a: a.clone(),
493 b: new_child,
494 }
495 }
496 }
497 SdfNode::SmoothUnion { a, b, k } => {
498 if child_idx == 0 {
499 SdfNode::SmoothUnion {
500 a: new_child,
501 b: b.clone(),
502 k: *k,
503 }
504 } else {
505 SdfNode::SmoothUnion {
506 a: a.clone(),
507 b: new_child,
508 k: *k,
509 }
510 }
511 }
512 SdfNode::SmoothIntersection { a, b, k } => {
513 if child_idx == 0 {
514 SdfNode::SmoothIntersection {
515 a: new_child,
516 b: b.clone(),
517 k: *k,
518 }
519 } else {
520 SdfNode::SmoothIntersection {
521 a: a.clone(),
522 b: new_child,
523 k: *k,
524 }
525 }
526 }
527 SdfNode::SmoothSubtraction { a, b, k } => {
528 if child_idx == 0 {
529 SdfNode::SmoothSubtraction {
530 a: new_child,
531 b: b.clone(),
532 k: *k,
533 }
534 } else {
535 SdfNode::SmoothSubtraction {
536 a: a.clone(),
537 b: new_child,
538 k: *k,
539 }
540 }
541 }
542 SdfNode::ChamferUnion { a, b, r } => {
543 if child_idx == 0 {
544 SdfNode::ChamferUnion {
545 a: new_child,
546 b: b.clone(),
547 r: *r,
548 }
549 } else {
550 SdfNode::ChamferUnion {
551 a: a.clone(),
552 b: new_child,
553 r: *r,
554 }
555 }
556 }
557 SdfNode::ChamferIntersection { a, b, r } => {
558 if child_idx == 0 {
559 SdfNode::ChamferIntersection {
560 a: new_child,
561 b: b.clone(),
562 r: *r,
563 }
564 } else {
565 SdfNode::ChamferIntersection {
566 a: a.clone(),
567 b: new_child,
568 r: *r,
569 }
570 }
571 }
572 SdfNode::ChamferSubtraction { a, b, r } => {
573 if child_idx == 0 {
574 SdfNode::ChamferSubtraction {
575 a: new_child,
576 b: b.clone(),
577 r: *r,
578 }
579 } else {
580 SdfNode::ChamferSubtraction {
581 a: a.clone(),
582 b: new_child,
583 r: *r,
584 }
585 }
586 }
587 SdfNode::StairsUnion { a, b, r, n } => {
588 if child_idx == 0 {
589 SdfNode::StairsUnion {
590 a: new_child,
591 b: b.clone(),
592 r: *r,
593 n: *n,
594 }
595 } else {
596 SdfNode::StairsUnion {
597 a: a.clone(),
598 b: new_child,
599 r: *r,
600 n: *n,
601 }
602 }
603 }
604 SdfNode::StairsIntersection { a, b, r, n } => {
605 if child_idx == 0 {
606 SdfNode::StairsIntersection {
607 a: new_child,
608 b: b.clone(),
609 r: *r,
610 n: *n,
611 }
612 } else {
613 SdfNode::StairsIntersection {
614 a: a.clone(),
615 b: new_child,
616 r: *r,
617 n: *n,
618 }
619 }
620 }
621 SdfNode::StairsSubtraction { a, b, r, n } => {
622 if child_idx == 0 {
623 SdfNode::StairsSubtraction {
624 a: new_child,
625 b: b.clone(),
626 r: *r,
627 n: *n,
628 }
629 } else {
630 SdfNode::StairsSubtraction {
631 a: a.clone(),
632 b: new_child,
633 r: *r,
634 n: *n,
635 }
636 }
637 }
638 SdfNode::XOR { a, b } => {
639 if child_idx == 0 {
640 SdfNode::XOR {
641 a: new_child,
642 b: b.clone(),
643 }
644 } else {
645 SdfNode::XOR {
646 a: a.clone(),
647 b: new_child,
648 }
649 }
650 }
651 SdfNode::Morph { a, b, t } => {
652 if child_idx == 0 {
653 SdfNode::Morph {
654 a: new_child,
655 b: b.clone(),
656 t: *t,
657 }
658 } else {
659 SdfNode::Morph {
660 a: a.clone(),
661 b: new_child,
662 t: *t,
663 }
664 }
665 }
666 SdfNode::ColumnsUnion { a, b, r, n } => {
667 if child_idx == 0 {
668 SdfNode::ColumnsUnion {
669 a: new_child,
670 b: b.clone(),
671 r: *r,
672 n: *n,
673 }
674 } else {
675 SdfNode::ColumnsUnion {
676 a: a.clone(),
677 b: new_child,
678 r: *r,
679 n: *n,
680 }
681 }
682 }
683 SdfNode::ColumnsIntersection { a, b, r, n } => {
684 if child_idx == 0 {
685 SdfNode::ColumnsIntersection {
686 a: new_child,
687 b: b.clone(),
688 r: *r,
689 n: *n,
690 }
691 } else {
692 SdfNode::ColumnsIntersection {
693 a: a.clone(),
694 b: new_child,
695 r: *r,
696 n: *n,
697 }
698 }
699 }
700 SdfNode::ColumnsSubtraction { a, b, r, n } => {
701 if child_idx == 0 {
702 SdfNode::ColumnsSubtraction {
703 a: new_child,
704 b: b.clone(),
705 r: *r,
706 n: *n,
707 }
708 } else {
709 SdfNode::ColumnsSubtraction {
710 a: a.clone(),
711 b: new_child,
712 r: *r,
713 n: *n,
714 }
715 }
716 }
717 SdfNode::Pipe { a, b, r } => {
718 if child_idx == 0 {
719 SdfNode::Pipe {
720 a: new_child,
721 b: b.clone(),
722 r: *r,
723 }
724 } else {
725 SdfNode::Pipe {
726 a: a.clone(),
727 b: new_child,
728 r: *r,
729 }
730 }
731 }
732 SdfNode::Engrave { a, b, r } => {
733 if child_idx == 0 {
734 SdfNode::Engrave {
735 a: new_child,
736 b: b.clone(),
737 r: *r,
738 }
739 } else {
740 SdfNode::Engrave {
741 a: a.clone(),
742 b: new_child,
743 r: *r,
744 }
745 }
746 }
747 SdfNode::Groove { a, b, ra, rb } => {
748 if child_idx == 0 {
749 SdfNode::Groove {
750 a: new_child,
751 b: b.clone(),
752 ra: *ra,
753 rb: *rb,
754 }
755 } else {
756 SdfNode::Groove {
757 a: a.clone(),
758 b: new_child,
759 ra: *ra,
760 rb: *rb,
761 }
762 }
763 }
764 SdfNode::Tongue { a, b, ra, rb } => {
765 if child_idx == 0 {
766 SdfNode::Tongue {
767 a: new_child,
768 b: b.clone(),
769 ra: *ra,
770 rb: *rb,
771 }
772 } else {
773 SdfNode::Tongue {
774 a: a.clone(),
775 b: new_child,
776 ra: *ra,
777 rb: *rb,
778 }
779 }
780 }
781 SdfNode::ExpSmoothUnion { a, b, k } => {
782 if child_idx == 0 {
783 SdfNode::ExpSmoothUnion {
784 a: new_child,
785 b: b.clone(),
786 k: *k,
787 }
788 } else {
789 SdfNode::ExpSmoothUnion {
790 a: a.clone(),
791 b: new_child,
792 k: *k,
793 }
794 }
795 }
796 SdfNode::ExpSmoothIntersection { a, b, k } => {
797 if child_idx == 0 {
798 SdfNode::ExpSmoothIntersection {
799 a: new_child,
800 b: b.clone(),
801 k: *k,
802 }
803 } else {
804 SdfNode::ExpSmoothIntersection {
805 a: a.clone(),
806 b: new_child,
807 k: *k,
808 }
809 }
810 }
811 SdfNode::ExpSmoothSubtraction { a, b, k } => {
812 if child_idx == 0 {
813 SdfNode::ExpSmoothSubtraction {
814 a: new_child,
815 b: b.clone(),
816 k: *k,
817 }
818 } else {
819 SdfNode::ExpSmoothSubtraction {
820 a: a.clone(),
821 b: new_child,
822 k: *k,
823 }
824 }
825 }
826
827 SdfNode::Translate { offset, .. } => SdfNode::Translate {
829 child: new_child,
830 offset: *offset,
831 },
832 SdfNode::Rotate { rotation, .. } => SdfNode::Rotate {
833 child: new_child,
834 rotation: *rotation,
835 },
836 SdfNode::Scale { factor, .. } => SdfNode::Scale {
837 child: new_child,
838 factor: *factor,
839 },
840 SdfNode::ScaleNonUniform { factors, .. } => SdfNode::ScaleNonUniform {
841 child: new_child,
842 factors: *factors,
843 },
844 SdfNode::ProjectiveTransform {
845 inv_matrix,
846 lipschitz_bound,
847 ..
848 } => SdfNode::ProjectiveTransform {
849 child: new_child,
850 inv_matrix: *inv_matrix,
851 lipschitz_bound: *lipschitz_bound,
852 },
853 SdfNode::LatticeDeform {
854 control_points,
855 nx,
856 ny,
857 nz,
858 bbox_min,
859 bbox_max,
860 ..
861 } => SdfNode::LatticeDeform {
862 child: new_child,
863 control_points: control_points.clone(),
864 nx: *nx,
865 ny: *ny,
866 nz: *nz,
867 bbox_min: *bbox_min,
868 bbox_max: *bbox_max,
869 },
870 SdfNode::SdfSkinning { bones, .. } => SdfNode::SdfSkinning {
871 child: new_child,
872 bones: bones.clone(),
873 },
874
875 SdfNode::Twist { strength, .. } => SdfNode::Twist {
877 child: new_child,
878 strength: *strength,
879 },
880 SdfNode::Bend { curvature, .. } => SdfNode::Bend {
881 child: new_child,
882 curvature: *curvature,
883 },
884 SdfNode::RepeatInfinite { spacing, .. } => SdfNode::RepeatInfinite {
885 child: new_child,
886 spacing: *spacing,
887 },
888 SdfNode::RepeatFinite { count, spacing, .. } => SdfNode::RepeatFinite {
889 child: new_child,
890 count: *count,
891 spacing: *spacing,
892 },
893 SdfNode::Noise {
894 amplitude,
895 frequency,
896 seed,
897 ..
898 } => SdfNode::Noise {
899 child: new_child,
900 amplitude: *amplitude,
901 frequency: *frequency,
902 seed: *seed,
903 },
904 SdfNode::Round { radius, .. } => SdfNode::Round {
905 child: new_child,
906 radius: *radius,
907 },
908 SdfNode::Onion { thickness, .. } => SdfNode::Onion {
909 child: new_child,
910 thickness: *thickness,
911 },
912 SdfNode::Elongate { amount, .. } => SdfNode::Elongate {
913 child: new_child,
914 amount: *amount,
915 },
916 SdfNode::Mirror { axes, .. } => SdfNode::Mirror {
917 child: new_child,
918 axes: *axes,
919 },
920 SdfNode::OctantMirror { .. } => SdfNode::OctantMirror { child: new_child },
921 SdfNode::Revolution { offset, .. } => SdfNode::Revolution {
922 child: new_child,
923 offset: *offset,
924 },
925 SdfNode::Extrude { half_height, .. } => SdfNode::Extrude {
926 child: new_child,
927 half_height: *half_height,
928 },
929 SdfNode::Taper { factor, reach, .. } => SdfNode::Taper {
930 child: new_child,
931 factor: *factor,
932 reach: *reach,
933 },
934 SdfNode::Displacement { strength, .. } => SdfNode::Displacement {
935 child: new_child,
936 strength: *strength,
937 },
938 SdfNode::SineDisplacement {
939 amplitude,
940 frequency,
941 ..
942 } => SdfNode::SineDisplacement {
943 child: new_child,
944 amplitude: *amplitude,
945 frequency: *frequency,
946 },
947 SdfNode::PolarRepeat { count, .. } => SdfNode::PolarRepeat {
948 child: new_child,
949 count: *count,
950 },
951 SdfNode::SweepBezier { p0, p1, p2, .. } => SdfNode::SweepBezier {
952 child: new_child,
953 p0: *p0,
954 p1: *p1,
955 p2: *p2,
956 },
957 SdfNode::IcosahedralSymmetry { .. } => SdfNode::IcosahedralSymmetry { child: new_child },
958 SdfNode::IFS {
959 transforms,
960 iterations,
961 ..
962 } => SdfNode::IFS {
963 child: new_child,
964 transforms: transforms.clone(),
965 iterations: *iterations,
966 },
967 SdfNode::HeightmapDisplacement {
968 heightmap,
969 width,
970 height,
971 amplitude,
972 scale,
973 ..
974 } => SdfNode::HeightmapDisplacement {
975 child: new_child,
976 heightmap: heightmap.clone(),
977 width: *width,
978 height: *height,
979 amplitude: *amplitude,
980 scale: *scale,
981 },
982 SdfNode::SurfaceRoughness {
983 frequency,
984 amplitude,
985 octaves,
986 ..
987 } => SdfNode::SurfaceRoughness {
988 child: new_child,
989 frequency: *frequency,
990 amplitude: *amplitude,
991 octaves: *octaves,
992 },
993 SdfNode::Shear { shear, .. } => SdfNode::Shear {
994 child: new_child,
995 shear: *shear,
996 },
997 SdfNode::Animated {
998 speed, amplitude, ..
999 } => SdfNode::Animated {
1000 child: new_child,
1001 speed: *speed,
1002 amplitude: *amplitude,
1003 },
1004 SdfNode::WithMaterial { material_id, .. } => SdfNode::WithMaterial {
1005 child: new_child,
1006 material_id: *material_id,
1007 },
1008
1009 _ => unreachable!("Leaf nodes have no children"),
1011 }
1012}
1013
1014#[cfg(test)]
1017mod tests {
1018 use super::*;
1019
1020 #[test]
1021 fn identical_trees_empty_diff() {
1022 let tree = SdfNode::sphere(1.0);
1023 let patch = tree_diff(&tree, &tree);
1024 assert!(patch.ops.is_empty());
1025 assert_eq!(patch.content_hash, 0);
1026 }
1027
1028 #[test]
1029 fn different_primitives() {
1030 let old = SdfNode::sphere(1.0);
1031 let new = SdfNode::sphere(2.0);
1032 let patch = tree_diff(&old, &new);
1033 assert_eq!(patch.ops.len(), 1);
1034 match &patch.ops[0] {
1035 DiffOp::Replace { path, .. } => assert!(path.is_empty()),
1036 _ => panic!("Expected Replace op"),
1037 }
1038 }
1039
1040 #[test]
1041 fn different_primitive_types() {
1042 let old = SdfNode::sphere(1.0);
1043 let new = SdfNode::box3d(1.0, 1.0, 1.0);
1044 let patch = tree_diff(&old, &new);
1045 assert_eq!(patch.ops.len(), 1);
1046 }
1047
1048 #[test]
1049 fn child_changed_in_union() {
1050 let old = SdfNode::sphere(1.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1051 let new = SdfNode::sphere(2.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1052 let patch = tree_diff(&old, &new);
1053 assert_eq!(patch.ops.len(), 1);
1055 match &patch.ops[0] {
1056 DiffOp::Replace { path, .. } => assert_eq!(path, &vec![0]),
1057 _ => panic!("Expected Replace op"),
1058 }
1059 }
1060
1061 #[test]
1062 fn apply_patch_roundtrip() {
1063 let old = SdfNode::sphere(1.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1064 let new = SdfNode::sphere(2.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1065 let patch = tree_diff(&old, &new);
1066 let result = apply_patch(&old, &patch).unwrap();
1067 assert_eq!(tree_hash(&result), tree_hash(&new));
1068 }
1069
1070 #[test]
1071 fn tree_hash_deterministic() {
1072 let a = SdfNode::sphere(1.0);
1073 let h1 = tree_hash(&a);
1074 let h2 = tree_hash(&a);
1075 assert_eq!(h1, h2);
1076 assert_ne!(h1, 0);
1077 }
1078
1079 #[test]
1080 fn tree_hash_differs() {
1081 let a = SdfNode::sphere(1.0);
1082 let b = SdfNode::sphere(2.0);
1083 assert_ne!(tree_hash(&a), tree_hash(&b));
1084 }
1085
1086 #[test]
1087 fn patch_content_hash_nonempty() {
1088 let old = SdfNode::sphere(1.0);
1089 let new = SdfNode::sphere(2.0);
1090 let patch = tree_diff(&old, &new);
1091 assert_ne!(patch.content_hash, 0);
1092 }
1093
1094 #[test]
1095 fn apply_patch_invalid_path() {
1096 let tree = SdfNode::sphere(1.0);
1097 let patch = TreePatch {
1098 ops: vec![DiffOp::Replace {
1099 path: vec![0], old_hash: 0,
1101 new_node: Arc::new(SdfNode::sphere(2.0)),
1102 }],
1103 content_hash: 1,
1104 };
1105 assert!(apply_patch(&tree, &patch).is_err());
1106 }
1107
1108 #[test]
1109 fn apply_patch_hash_mismatch() {
1110 let tree = SdfNode::sphere(1.0);
1111 let patch = TreePatch {
1112 ops: vec![DiffOp::Replace {
1113 path: vec![],
1114 old_hash: 9999, new_node: Arc::new(SdfNode::sphere(2.0)),
1116 }],
1117 content_hash: 1,
1118 };
1119 match apply_patch(&tree, &patch) {
1120 Err(DiffError::HashMismatch { .. }) => {}
1121 _ => panic!("Expected HashMismatch error"),
1122 }
1123 }
1124
1125 #[test]
1126 fn nested_tree_diff() {
1127 let old = SdfNode::sphere(1.0)
1128 .translate(1.0, 0.0, 0.0)
1129 .union(SdfNode::box3d(1.0, 1.0, 1.0));
1130 let new = SdfNode::sphere(1.0)
1131 .translate(2.0, 0.0, 0.0) .union(SdfNode::box3d(1.0, 1.0, 1.0));
1133 let patch = tree_diff(&old, &new);
1134 assert!(!patch.ops.is_empty());
1135 }
1136
1137 #[test]
1138 fn test_replace_child_smooth_subtraction() {
1139 let old = SdfNode::SmoothSubtraction {
1140 a: Arc::new(SdfNode::sphere(1.0)),
1141 b: Arc::new(SdfNode::box3d(0.5, 0.5, 0.5)),
1142 k: 0.1,
1143 };
1144 let new = SdfNode::SmoothSubtraction {
1145 a: Arc::new(SdfNode::sphere(2.0)), b: Arc::new(SdfNode::box3d(0.5, 0.5, 0.5)),
1147 k: 0.1,
1148 };
1149 let patch = tree_diff(&old, &new);
1150 assert_eq!(patch.ops.len(), 1);
1151 let result = apply_patch(&old, &patch).unwrap();
1152 assert_eq!(tree_hash(&result), tree_hash(&new));
1153 }
1154
1155 #[test]
1156 fn test_replace_child_morph() {
1157 let old = SdfNode::Morph {
1158 a: Arc::new(SdfNode::sphere(1.0)),
1159 b: Arc::new(SdfNode::box3d(1.0, 1.0, 1.0)),
1160 t: 0.5,
1161 };
1162 let new = SdfNode::Morph {
1163 a: Arc::new(SdfNode::sphere(1.0)),
1164 b: Arc::new(SdfNode::box3d(2.0, 2.0, 2.0)), t: 0.5,
1166 };
1167 let patch = tree_diff(&old, &new);
1168 assert_eq!(patch.ops.len(), 1);
1169 match &patch.ops[0] {
1170 DiffOp::Replace { path, .. } => assert_eq!(path, &vec![1]),
1171 _ => panic!("Expected Replace op"),
1172 }
1173 let result = apply_patch(&old, &patch).unwrap();
1174 assert_eq!(tree_hash(&result), tree_hash(&new));
1175 }
1176
1177 #[test]
1178 fn test_replace_child_xor() {
1179 let old = SdfNode::XOR {
1180 a: Arc::new(SdfNode::sphere(1.0)),
1181 b: Arc::new(SdfNode::sphere(1.5)),
1182 };
1183 let new = SdfNode::XOR {
1184 a: Arc::new(SdfNode::sphere(1.0)),
1185 b: Arc::new(SdfNode::sphere(3.0)), };
1187 let patch = tree_diff(&old, &new);
1188 assert_eq!(patch.ops.len(), 1);
1189 let result = apply_patch(&old, &patch).unwrap();
1190 assert_eq!(tree_hash(&result), tree_hash(&new));
1191 }
1192
1193 #[test]
1194 fn test_replace_child_complex_tree() {
1195 use glam::Vec3;
1197 let old = SdfNode::SmoothUnion {
1198 a: Arc::new(SdfNode::Groove {
1199 a: Arc::new(SdfNode::sphere(1.0)),
1200 b: Arc::new(SdfNode::box3d(0.5, 0.5, 0.5)),
1201 ra: 0.1,
1202 rb: 0.2,
1203 }),
1204 b: Arc::new(SdfNode::StairsUnion {
1205 a: Arc::new(SdfNode::cylinder(0.5, 2.0)),
1206 b: Arc::new(SdfNode::torus(1.0, 0.2)),
1207 r: 0.3,
1208 n: 4.0,
1209 }),
1210 k: 0.05,
1211 };
1212 let new = SdfNode::SmoothUnion {
1214 a: Arc::new(SdfNode::Groove {
1215 a: Arc::new(SdfNode::sphere(1.0)),
1216 b: Arc::new(SdfNode::box3d(0.5, 0.5, 0.5)),
1217 ra: 0.1,
1218 rb: 0.2,
1219 }),
1220 b: Arc::new(SdfNode::StairsUnion {
1221 a: Arc::new(SdfNode::cylinder(0.5, 2.0)),
1222 b: Arc::new(SdfNode::torus(1.0, 0.5)), r: 0.3,
1224 n: 4.0,
1225 }),
1226 k: 0.05,
1227 };
1228 let _ = Vec3::ZERO; let patch = tree_diff(&old, &new);
1230 assert!(!patch.ops.is_empty());
1231 let result = apply_patch(&old, &patch).unwrap();
1232 assert_eq!(tree_hash(&result), tree_hash(&new));
1233 }
1234
1235 #[test]
1236 fn invert_patch_roundtrip() {
1237 let old = SdfNode::sphere(1.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1238 let new = SdfNode::sphere(2.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1239 let fwd = tree_diff(&old, &new);
1240 let result = apply_patch(&old, &fwd).unwrap();
1241 let inv = super::invert_patch(&old, &fwd).unwrap();
1242 let restored = apply_patch(&result, &inv).unwrap();
1243 assert_eq!(tree_hash(&restored), tree_hash(&old));
1244 }
1245
1246 #[test]
1247 fn merge_patches_equivalent() {
1248 let a = SdfNode::sphere(1.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1249 let b = SdfNode::sphere(2.0).union(SdfNode::box3d(1.0, 1.0, 1.0));
1250 let c = SdfNode::sphere(2.0).union(SdfNode::box3d(3.0, 3.0, 3.0));
1251 let p1 = tree_diff(&a, &b);
1252 let p2 = tree_diff(&b, &c);
1253 let merged = super::merge_patches(&a, &p1, &p2).unwrap();
1254 let result = apply_patch(&a, &merged).unwrap();
1255 assert_eq!(tree_hash(&result), tree_hash(&c));
1256 }
1257
1258 #[test]
1259 fn tree_patch_op_count() {
1260 let old = SdfNode::sphere(1.0);
1261 let new = SdfNode::sphere(2.0);
1262 let patch = tree_diff(&old, &new);
1263 assert_eq!(patch.op_count(), 1);
1264 assert!(!patch.is_empty());
1265 }
1266
1267 #[test]
1268 fn empty_patch_is_empty() {
1269 let tree = SdfNode::sphere(1.0);
1270 let patch = tree_diff(&tree, &tree);
1271 assert!(patch.is_empty());
1272 assert_eq!(patch.op_count(), 0);
1273 }
1274
1275 #[test]
1276 fn insert_op_wraps_node() {
1277 let tree = SdfNode::sphere(1.0);
1278 let wrapper = SdfNode::Translate {
1279 child: Arc::new(SdfNode::sphere(0.0)), offset: glam::Vec3::new(1.0, 0.0, 0.0),
1281 };
1282 let patch = TreePatch {
1283 ops: vec![DiffOp::Insert {
1284 path: vec![],
1285 old_hash: tree_hash(&tree),
1286 wrapper: Arc::new(wrapper),
1287 }],
1288 content_hash: 1,
1289 };
1290 let result = apply_patch(&tree, &patch).unwrap();
1291 match result {
1292 SdfNode::Translate { ref child, offset } => {
1293 assert_eq!(tree_hash(child), tree_hash(&tree));
1294 assert_eq!(offset, glam::Vec3::new(1.0, 0.0, 0.0));
1295 }
1296 _ => panic!("Expected Translate wrapper"),
1297 }
1298 }
1299
1300 #[test]
1301 fn delete_op_unwraps_node() {
1302 let inner = SdfNode::sphere(1.0);
1303 let tree = SdfNode::Translate {
1304 child: Arc::new(inner.clone()),
1305 offset: glam::Vec3::new(1.0, 0.0, 0.0),
1306 };
1307 let patch = TreePatch {
1308 ops: vec![DiffOp::Delete {
1309 path: vec![],
1310 old_hash: tree_hash(&tree),
1311 child_index: 0,
1312 }],
1313 content_hash: 1,
1314 };
1315 let result = apply_patch(&tree, &patch).unwrap();
1316 assert_eq!(tree_hash(&result), tree_hash(&inner));
1317 }
1318
1319 #[test]
1320 fn delete_invalid_child_index() {
1321 let tree = SdfNode::sphere(1.0); let patch = TreePatch {
1323 ops: vec![DiffOp::Delete {
1324 path: vec![],
1325 old_hash: tree_hash(&tree),
1326 child_index: 0,
1327 }],
1328 content_hash: 1,
1329 };
1330 assert!(apply_patch(&tree, &patch).is_err());
1331 }
1332
1333 #[test]
1334 fn test_replace_child_single_child_modifiers() {
1335 use glam::Vec3;
1337
1338 let old_bend = SdfNode::Bend {
1340 child: Arc::new(SdfNode::sphere(1.0)),
1341 curvature: 0.5,
1342 };
1343 let new_bend = SdfNode::Bend {
1344 child: Arc::new(SdfNode::sphere(2.0)),
1345 curvature: 0.5,
1346 };
1347 let patch = tree_diff(&old_bend, &new_bend);
1348 assert_eq!(patch.ops.len(), 1);
1349 let result = apply_patch(&old_bend, &patch).unwrap();
1350 assert_eq!(tree_hash(&result), tree_hash(&new_bend));
1351
1352 let old_elongate = SdfNode::Elongate {
1354 child: Arc::new(SdfNode::sphere(1.0)),
1355 amount: Vec3::new(0.5, 0.5, 0.5),
1356 };
1357 let new_elongate = SdfNode::Elongate {
1358 child: Arc::new(SdfNode::sphere(3.0)),
1359 amount: Vec3::new(0.5, 0.5, 0.5),
1360 };
1361 let patch2 = tree_diff(&old_elongate, &new_elongate);
1362 assert_eq!(patch2.ops.len(), 1);
1363 let result2 = apply_patch(&old_elongate, &patch2).unwrap();
1364 assert_eq!(tree_hash(&result2), tree_hash(&new_elongate));
1365
1366 let old_mirror = SdfNode::Mirror {
1368 child: Arc::new(SdfNode::box3d(1.0, 1.0, 1.0)),
1369 axes: Vec3::new(1.0, 0.0, 0.0),
1370 };
1371 let new_mirror = SdfNode::Mirror {
1372 child: Arc::new(SdfNode::box3d(2.0, 2.0, 2.0)),
1373 axes: Vec3::new(1.0, 0.0, 0.0),
1374 };
1375 let patch3 = tree_diff(&old_mirror, &new_mirror);
1376 assert_eq!(patch3.ops.len(), 1);
1377 let result3 = apply_patch(&old_mirror, &patch3).unwrap();
1378 assert_eq!(tree_hash(&result3), tree_hash(&new_mirror));
1379 }
1380}