@@ -641,6 +641,65 @@ function Base.:(==)(a::Mesh, b::Mesh)
641
641
(faces (a) == faces (b)) && (a. views == b. views)
642
642
end
643
643
644
+ """
645
+ strictly_equal_face_vertices(a::Mesh, b::Mesh)
646
+
647
+ Checks whether mesh a and b are equal in terms of vertices used in their faces.
648
+ This allows for vertex data and indices to be synchronously permuted. For
649
+ example, this will recognize
650
+ ```
651
+ a = Mesh([a, b, c], [GLTriangleFace(1,2,3)])
652
+ b = Mesh([a, c, b], [GLTriangleFace(1,3,2)])
653
+ ```
654
+ as equal, because while the positions and faces have different orders the vertices
655
+ in the face are the same:
656
+ ```
657
+ [a, c, b][[1, 3, 2]] == [a, b, c] == [a, b, c][[1,2,3]]
658
+ ```
659
+
660
+ This still returns false if the order of faces is permuted, e.g.
661
+ `Mesh(ps, [f1, f2]) != Mesh(ps, [f2, f1])`. It also returns false if vertices are
662
+ cyclically permuted within a face, i.e. `ps[[1,2,3]] != ps[[2,3,1]]`.
663
+ """
664
+ function strictly_equal_face_vertices (a:: Mesh , b:: Mesh )
665
+ # Quick checks
666
+ if propertynames (a) != propertynames (b) || length (faces (a)) != length (faces (b))
667
+ return false
668
+ end
669
+
670
+ N = length (faces (a))
671
+ # for views we want to ignore empty ranges (they don't represent geometry)
672
+ # and treat 1:N as no range (as that is used interchangeably)
673
+ views1 = filter (view -> length (view) > 0 && (minimum (view) > 1 || maximum (view) < N), a. views)
674
+ views2 = filter (view -> length (view) > 0 && (minimum (view) > 1 || maximum (view) < N), b. views)
675
+ views1 != views2 && return false
676
+
677
+ # TODO : Allow different face orders & cyclic permutation within faces.
678
+ # E.g. use hash.(data[face]), cyclically permute min to front, hash result
679
+ # and add them to heaps (or sets?) so we can compare them at the end
680
+ # That should probably be another function as it's probably a significant
681
+ # step up in overhead?
682
+ for (attrib1, attrib2) in zip (vertex_attributes (a), vertex_attributes (b))
683
+ if attrib1 isa FaceView
684
+ if ! (attrib2 isa FaceView) || length (faces (attrib1)) != length (faces (attrib2))
685
+ return false
686
+ end
687
+ for (f1, f2) in zip (faces (attrib1), faces (attrib2))
688
+ values (attrib1)[f1] == values (attrib2)[f2] || return false
689
+ end
690
+ else
691
+ if attrib2 isa FaceView
692
+ return false
693
+ end
694
+ for (f1, f2) in zip (faces (a), faces (b))
695
+ attrib1[f1] == attrib2[f2] || return false
696
+ end
697
+ end
698
+ end
699
+
700
+ return true
701
+ end
702
+
644
703
function Base. iterate (mesh:: Mesh , i= 1 )
645
704
return i - 1 < length (mesh) ? (mesh[i], i + 1 ) : nothing
646
705
end
0 commit comments