39 return "The number of vertices is different";
41 return "The number of edges is different";
43 return "The number of corners is different";
45 return "The number of faces is different";
47 return "Some values of the vertex attributes are different";
49 return "Some values of the edge attributes are different";
51 return "Some values of the corner attributes are different";
53 return "Some values of the face attributes are different";
55 return "The edge topology is different";
57 return "The face topology is different";
59 return "The sets of attribute ids are different";
61 return "Some attributes with the same name have different types";
63 return "The meshes are the same up to a change of indices";
73 for (
const int i : map.index_range()) {
74 inverted_map[map[i]] = i;
120 std::stable_sort(indices.begin(), indices.end(), [&](
int i1,
int i2) {
121 const T value1 = values[i1];
122 const T value2 = values[i2];
123 if constexpr (is_same_any_v<T, int, float, bool, int8_t, OrderedEdge>) {
125 return value1 < value2;
128 return value1[component_i] < value2[component_i];
130 if constexpr (std::is_same_v<T, math::Quaternion>) {
133 return value1_quat[component_i] < value2_quat[component_i];
135 if constexpr (std::is_same_v<T, float4x4>) {
136 return value1.base_ptr()[component_i] < value2.base_ptr()[component_i];
138 if constexpr (std::is_same_v<T, int2>) {
139 for (
int i = 0; i < 2; i++) {
140 if (value1[i] != value2[i]) {
141 return value1[i] < value2[i];
146 if constexpr (std::is_same_v<T, ColorGeometry4b>) {
147 for (
int i = 0; i < 4; i++) {
148 if (value1[i] != value2[i]) {
149 return value1[i] < value2[i];
168 std::stable_sort(indices.begin(), indices.end(), [&](
int i1,
int i2) {
169 return set_ids[values_to_sorted[values[i1]]] < set_ids[values_to_sorted[values[i2]]];
180 const int component_i)
183 while (i < set_sizes.
size()) {
184 const int set_size = set_sizes[i];
208 while (i < sorted_to_domain1.
size()) {
209 const int set_size = set_sizes[i];
235 const float threshold,
236 const int component_i)
238 if constexpr (is_same_any_v<T, int, int2, bool, int8_t, OrderedEdge, ColorGeometry4b>) {
240 return value1 != value2;
243 if constexpr (std::is_same_v<T, float>) {
246 if constexpr (is_same_any_v<T, float2, float3, ColorGeometry4f>) {
249 if constexpr (std::is_same_v<T, math::Quaternion>) {
254 if constexpr (std::is_same_v<T, float4x4>) {
256 value1.base_ptr()[component_i], value2.base_ptr()[component_i], threshold);
272 const float threshold,
273 const int component_i)
276 const float value_threshold = 5 * threshold;
280 T previous = values1[0];
283 const T value1 = values1[sorted_to_values1[i]];
284 const T value2 = values2[sorted_to_values2[i]];
320 int previous = value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[0]]]];
322 for (
const int i : sorted_to_domain1.
index_range()) {
323 const int value_id1 =
324 value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[i]]]];
325 const int value_id2 =
326 value_set_ids[values2_to_sorted[domain_to_values2[sorted_to_domain2[i]]]];
327 if (value_id1 != value_id2) {
331 if (value_id1 != previous || set_ids[i] == i) {
334 previous = value_id1;
347 int i = set_ids.
size() - 1;
351 int set_size = i - set_ids[i] + 1;
353 for (
int k = i - set_size + 1; k <= i; k++) {
354 set_sizes[k] = set_size;
366 const int2 e = edges[i];
367 r_edges[i] =
OrderedEdge(vertex_set_ids[verts_to_sorted[
e.x]],
368 vertex_set_ids[verts_to_sorted[
e.y]]);
419 corners.from_sorted1,
420 corners.from_sorted2);
427 corners.from_sorted1,
428 corners.from_sorted2);
429 if (!corners_line_up) {
441 for (
const int face_i : smallest_corner_ids.
index_range()) {
442 const int face_start = face_offsets[face_i];
443 const int face_end = face_offsets[face_i + 1];
444 int smallest = corner_set_ids[corners_to_sorted[face_start]];
446 for (
const int corner_i : corners.drop_front(1)) {
447 const int corner_id = corner_set_ids[corners_to_sorted[corner_i]];
448 if (corner_id < smallest) {
449 smallest = corner_id;
452 smallest_corner_ids[face_i] = smallest;
465 Array<int> smallest_corner_ids1(faces.from_sorted1.size());
466 Array<int> smallest_corner_ids2(faces.from_sorted2.size());
468 face_offsets1, corners.to_sorted1, corners.set_ids, smallest_corner_ids1);
470 face_offsets2, corners.to_sorted2, corners.set_ids, smallest_corner_ids2);
474 smallest_corner_ids1.
as_span(),
475 smallest_corner_ids2.
as_span(),
478 smallest_corner_ids1.
as_span(),
479 smallest_corner_ids2.
as_span(),
484 if (!faces_line_up) {
501 id.startswith(
".pn.");
518 if (mesh1_attribute_ids != mesh2_attribute_ids) {
522 for (
const StringRef id : mesh1_attribute_ids) {
525 if (!reader1 || !reader2) {
530 return MeshMismatch::AttributeTypes;
547 const float threshold)
552 for (
const StringRef name : excluded_attributes) {
557 for (
const StringRef id : attribute_ids) {
558 if (!mesh2_attributes.
contains(
id)) {
560 return MeshMismatch::Attributes;
565 if (reader1.
domain != domain) {
570 std::optional<MeshMismatch> mismatch = {};
572 attribute_math::convert_to_static_type(reader1.
varray.
type(), [&](
auto dummy) {
573 using T = decltype(dummy);
574 const VArraySpan<T> values1 = reader1.varray.typed<T>();
575 const VArraySpan<T> values2 = reader2.varray.typed<T>();
580 if constexpr (std::is_same_v<T, float2>) {
583 else if constexpr (std::is_same_v<T, float3>) {
586 else if constexpr (is_same_any_v<T, math::Quaternion, ColorGeometry4f>) {
589 else if constexpr (is_same_any_v<T, float4x4>) {
592 for (
const int component_i :
IndexRange(num_loops)) {
594 maps.set_sizes, maps.from_sorted1, maps.from_sorted2, values1, values2, component_i);
602 if (!attributes_line_up) {
604 case AttrDomain::Point:
605 mismatch = MeshMismatch::VertexAttributes;
607 case AttrDomain::Edge:
608 mismatch = MeshMismatch::EdgeAttributes;
610 case AttrDomain::Corner:
611 mismatch = MeshMismatch::CornerAttributes;
613 case AttrDomain::Face:
614 mismatch = MeshMismatch::FaceAttributes;
640 for (
const int sorted_i : indices.set_sizes.index_range()) {
641 if (indices.set_sizes[sorted_i] == 1) {
644 int match = sorted_i;
645 for (
const int other_index :
646 IndexRange(indices.set_ids[sorted_i], indices.set_sizes[sorted_i]))
648 if (indices.from_sorted1[sorted_i] == indices.from_sorted2[other_index]) {
653 std::swap(indices.from_sorted2[sorted_i], indices.from_sorted2[match]);
654 for (
const int other_set_i :
655 IndexRange(indices.set_ids[sorted_i], indices.set_sizes[sorted_i]))
658 indices.set_ids[other_set_i] = sorted_i + 1;
659 indices.set_sizes[other_set_i] -= 1;
661 indices.set_ids[sorted_i] = sorted_i;
662 indices.set_sizes[sorted_i] = 1;
668 for (
const int size : set_sizes) {
703 mesh1.edges(), mesh1.
verts_num, vert_to_edge_offsets1, vert_to_edge_indices1);
707 mesh2.edges(), mesh2.
verts_num, vert_to_edge_offsets2, vert_to_edge_indices2);
709 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
710 const int vert1 =
verts.from_sorted1[sorted_i];
712 const Span<int> edges1 = vert_to_edge_map1[vert1];
717 const int vert2 =
verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set];
718 const Span<int> edges2 = vert_to_edge_map2[vert2];
719 if (edges1.
size() != edges2.
size()) {
722 bool vertex_matches =
true;
723 for (
const int edge1 : edges1) {
724 bool found_matching_edge =
false;
725 for (
const int edge2 : edges2) {
726 if (edges.set_ids[edges.to_sorted1[edge1]] == edges.set_ids[edges.to_sorted2[edge2]]) {
727 found_matching_edge =
true;
731 if (!found_matching_edge) {
732 vertex_matches =
false;
736 if (vertex_matches) {
737 matching_verts.
append(index_in_set);
742 return MeshMismatch::EdgeTopology;
751 int index_in_set = matching_verts.
first();
752 for (
const int other_index_in_set : matching_verts) {
753 const int other_sorted_index =
verts.set_ids[sorted_i] + other_index_in_set;
754 if (
verts.from_sorted1[sorted_i] ==
verts.from_sorted2[other_sorted_index]) {
755 index_in_set = other_index_in_set;
759 std::swap(
verts.from_sorted2[sorted_i],
760 verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set]);
763 verts.set_ids[other_set_i] = sorted_i + 1;
764 verts.set_sizes[other_set_i] -= 1;
766 verts.set_ids[sorted_i] = sorted_i;
767 verts.set_sizes[sorted_i] = 1;
772 verts.recalculate_inverse_maps();
783 const float threshold)
788 return MeshMismatch::NumVerts;
791 return MeshMismatch::NumEdges;
794 return MeshMismatch::NumCorners;
797 return MeshMismatch::NumFaces;
800 std::optional<MeshMismatch> mismatch = {};
811 mesh1_attributes, mesh2_attributes, AttrDomain::Point, {},
verts, threshold);
817 verts.recalculate_inverse_maps();
821 return MeshMismatch::EdgeTopology;
825 mesh1_attributes, mesh2_attributes, AttrDomain::Edge, {
".edge_verts"}, edges, threshold);
831 edges.recalculate_inverse_maps();
835 return MeshMismatch::FaceTopology;
839 return MeshMismatch::FaceTopology;
845 {
".corner_vert",
".corner_edge"},
853 corners.recalculate_inverse_maps();
857 return MeshMismatch::FaceTopology;
861 mesh1_attributes, mesh2_attributes, AttrDomain::Face, {},
faces, threshold);
874 return MeshMismatch::EdgeTopology;
879 edges.recalculate_inverse_maps();
882 return MeshMismatch::FaceTopology;
886 return MeshMismatch::FaceTopology;
891 corners.recalculate_inverse_maps();
894 return MeshMismatch::FaceTopology;
901 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
902 if (
verts.from_sorted1[sorted_i] !=
verts.from_sorted2[sorted_i]) {
903 return MeshMismatch::Indices;
908 for (
const int sorted_i : corners.from_sorted1.index_range()) {
909 if (corners.from_sorted1[sorted_i] != corners.from_sorted2[sorted_i]) {
910 return MeshMismatch::Indices;
913 for (
const int sorted_i : faces.from_sorted1.index_range()) {
914 if (faces.from_sorted1[sorted_i] != faces.from_sorted2[sorted_i]) {
915 return MeshMismatch::Indices;
#define BLI_assert_unreachable()
MINLINE bool compare_threshold_relative(float value1, float value2, float thresh)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
Span< T > as_span() const
void fill(const T &value) const
const CPPType & type() const
constexpr int64_t size() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr bool is_empty() const
constexpr IndexRange index_range() const
bool remove_as(const ForwardKey &key)
int64_t remove_if(Predicate &&predicate)
constexpr int64_t size() const
constexpr IndexRange index_range() const
void append(const T &value)
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
bool contains(const StringRef attribute_id) const
Array< int > from_sorted1
void recalculate_inverse_maps()
IndexMapping(const int64_t domain_size)
Array< int > from_sorted2
static bool sort_faces_based_on_corners(const IndexMapping &corners, const Span< int > face_offsets1, const Span< int > face_offsets2, IndexMapping &faces)
static std::optional< MeshMismatch > construct_vertex_mapping(const Mesh &mesh1, const Mesh &mesh2, IndexMapping &verts, IndexMapping &edges)
const char * mismatch_to_string(const MeshMismatch &mismatch)
static void edges_from_vertex_sets(const Span< int2 > edges, const Span< int > verts_to_sorted, const Span< int > vertex_set_ids, MutableSpan< OrderedEdge > r_edges)
static std::optional< MeshMismatch > sort_domain_using_attributes(const AttributeAccessor &mesh1_attributes, const AttributeAccessor &mesh2_attributes, const AttrDomain domain, const Span< StringRef > excluded_attributes, IndexMapping &maps, const float threshold)
static void update_set_sizes(const Span< int > set_ids, MutableSpan< int > set_sizes)
static bool all_set_sizes_one(const Span< int > set_sizes)
static bool update_set_ids_with_id_maps(MutableSpan< int > set_ids, const Span< int > domain_to_values1, const Span< int > domain_to_values2, const Span< int > values1_to_sorted, const Span< int > values2_to_sorted, const Span< int > value_set_ids, const Span< int > sorted_to_domain1, const Span< int > sorted_to_domain2)
static bool sort_corners_based_on_domain(const Span< int > corner_domain1, const Span< int > corner_domain2, const IndexMapping &domain, IndexMapping &corners)
static void sort_indices_with_id_maps(MutableSpan< int > indices, const Span< int > values, const Span< int > values_to_sorted, const Span< int > set_ids)
static void calc_smallest_corner_ids(const Span< int > face_offsets, const Span< int > corners_to_sorted, const Span< int > corner_set_ids, MutableSpan< int > smallest_corner_ids)
static bool sort_edges(const Span< int2 > edges1, const Span< int2 > edges2, const IndexMapping &verts, IndexMapping &edges)
static void make_set_sizes_one(IndexMapping &indices)
static bool ignored_attribute(const StringRef id)
static bool update_set_ids(MutableSpan< int > set_ids, const Span< T > values1, const Span< T > values2, const Span< int > sorted_to_values1, MutableSpan< int > sorted_to_values2, const float threshold, const int component_i)
static bool values_different(const T value1, const T value2, const float threshold, const int component_i)
std::optional< MeshMismatch > compare_meshes(const Mesh &mesh1, const Mesh &mesh2, float threshold)
Checks if the two meshes are different, returning the type of mismatch if any. Changes in index order...
static void sort_indices(MutableSpan< int > indices, const Span< T > values, const int component_i)
static std::optional< MeshMismatch > verify_attributes_compatible(const AttributeAccessor &mesh1_attributes, const AttributeAccessor &mesh2_attributes)
static void sort_per_set_with_id_maps(const Span< int > set_sizes, const Span< int > values1, const Span< int > values2, const Span< int > values1_to_sorted, const Span< int > values2_to_sorted, const Span< int > value_set_ids, MutableSpan< int > sorted_to_domain1, MutableSpan< int > sorted_to_domain2)
static void sort_per_set_based_on_attributes(const Span< int > set_sizes, MutableSpan< int > sorted_to_domain1, MutableSpan< int > sorted_to_domain2, const Span< T > values1, const Span< T > values2, const int component_i)
bool attribute_name_is_anonymous(const StringRef name)
constexpr bool is_same_any_v
VecBase< float, 4 > float4