46 return "The number of points is different";
48 return "The number of edges is different";
50 return "The number of corners is different";
52 return "The number of faces is different";
54 return "The number of curves is different";
56 return "Some values of the point attributes are different";
58 return "Some values of the edge attributes are different";
60 return "Some values of the corner attributes are different";
62 return "Some values of the face attributes are different";
64 return "Some values of the curve attributes are different";
66 return "The edge topology is different";
68 return "The face topology is different";
70 return "The curve topology is different";
72 return "The sets of attribute ids are different";
74 return "Some attributes with the same name have different types";
76 return "The geometries are the same up to a change of indices";
87 inverted_map[map[
i]] =
i;
133 std::stable_sort(indices.
begin(), indices.
end(), [&](
int i1,
int i2) {
134 const T value1 = values[i1];
135 const T value2 = values[i2];
136 if constexpr (is_same_any_v<T, int, float, bool, int8_t, OrderedEdge>) {
138 return value1 < value2;
141 return value1[component_i] < value2[component_i];
143 if constexpr (std::is_same_v<T, math::Quaternion>) {
146 return value1_quat[component_i] < value2_quat[component_i];
148 if constexpr (std::is_same_v<T, float4x4>) {
149 return value1.base_ptr()[component_i] < value2.base_ptr()[component_i];
151 if constexpr (std::is_same_v<T, int2>) {
152 for (
int i = 0;
i < 2;
i++) {
153 if (value1[
i] != value2[
i]) {
154 return value1[
i] < value2[
i];
159 if constexpr (std::is_same_v<T, ColorGeometry4b>) {
160 for (
int i = 0;
i < 4;
i++) {
161 if (value1[
i] != value2[
i]) {
162 return value1[
i] < value2[
i];
181 std::stable_sort(indices.
begin(), indices.
end(), [&](
int i1,
int i2) {
182 return set_ids[values_to_sorted[values[i1]]] < set_ids[values_to_sorted[values[i2]]];
193 const int component_i)
196 while (
i < set_sizes.
size()) {
197 const int set_size = set_sizes[
i];
221 while (
i < sorted_to_domain1.
size()) {
222 const int set_size = set_sizes[
i];
248 const float threshold,
249 const int component_i)
253 return value1 != value2;
256 if constexpr (std::is_same_v<T, float>) {
262 if constexpr (std::is_same_v<T, math::Quaternion>) {
267 if constexpr (std::is_same_v<T, float4x4>) {
269 value1.base_ptr()[component_i], value2.base_ptr()[component_i], threshold);
285 const float threshold,
286 const int component_i)
289 const float value_threshold = 5 * threshold;
293 T previous = values1[0];
296 const T value1 = values1[sorted_to_values1[
i]];
297 const T value2 = values2[sorted_to_values2[
i]];
333 int previous = value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[0]]]];
336 const int value_id1 =
337 value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[
i]]]];
338 const int value_id2 =
339 value_set_ids[values2_to_sorted[domain_to_values2[sorted_to_domain2[
i]]]];
340 if (value_id1 != value_id2) {
344 if (value_id1 != previous || set_ids[
i] ==
i) {
347 previous = value_id1;
360 int i = set_ids.
size() - 1;
364 int set_size =
i - set_ids[
i] + 1;
366 for (
int k =
i - set_size + 1; k <=
i; k++) {
367 set_sizes[k] = set_size;
381 vert_set_ids[verts_to_sorted[
e.y]]);
432 corners.from_sorted1,
433 corners.from_sorted2);
440 corners.from_sorted1,
441 corners.from_sorted2);
442 if (!corners_line_up) {
454 for (
const int face_i : smallest_corner_ids.
index_range()) {
455 const int face_start = face_offsets[face_i];
456 const int face_end = face_offsets[face_i + 1];
457 int smallest = corner_set_ids[corners_to_sorted[face_start]];
459 for (
const int corner_i : corners.drop_front(1)) {
460 const int corner_id = corner_set_ids[corners_to_sorted[corner_i]];
461 smallest = std::min(corner_id, smallest);
463 smallest_corner_ids[face_i] = smallest;
479 face_offsets1, corners.to_sorted1, corners.set_ids, smallest_corner_ids1);
481 face_offsets2, corners.to_sorted2, corners.set_ids, smallest_corner_ids2);
485 smallest_corner_ids1.
as_span(),
486 smallest_corner_ids2.
as_span(),
489 smallest_corner_ids1.
as_span(),
490 smallest_corner_ids2.
as_span(),
495 if (!faces_line_up) {
512 id.startswith(
".pn.");
529 if (attribute_ids1 != attribute_ids2) {
533 for (
const StringRef id : attribute_ids1) {
536 if (!reader1 || !reader2) {
558 const float threshold)
563 for (
const StringRef name : excluded_attributes) {
568 for (
const StringRef id : attribute_ids) {
576 if (reader1.
domain != domain) {
581 std::optional<GeoMismatch> mismatch = {};
584 using T = decltype(dummy);
585 const VArraySpan<T> values1 = reader1.varray.typed<T>();
586 const VArraySpan<T> values2 = reader2.varray.typed<T>();
591 if constexpr (std::is_same_v<T, float2>) {
594 else if constexpr (std::is_same_v<T, float3>) {
603 for (
const int component_i :
IndexRange(num_loops)) {
613 if (!attributes_line_up) {
658 int match = sorted_i;
659 for (
const int other_index :
668 for (
const int other_set_i :
672 indices.
set_ids[other_set_i] = sorted_i + 1;
675 indices.
set_ids[sorted_i] = sorted_i;
682 for (
const int size : set_sizes) {
717 mesh1.edges(), mesh1.
verts_num, vert_to_edge_offsets1, vert_to_edge_indices1);
721 mesh2.edges(), mesh2.
verts_num, vert_to_edge_offsets2, vert_to_edge_indices2);
726 const Span<int> edges1 = vert_to_edge_map1[vert1];
732 const Span<int> edges2 = vert_to_edge_map2[vert2];
733 if (edges1.
size() != edges2.
size()) {
736 bool vert_matches =
true;
737 for (
const int edge1 : edges1) {
738 bool found_matching_edge =
false;
739 for (
const int edge2 : edges2) {
741 found_matching_edge =
true;
745 if (!found_matching_edge) {
746 vert_matches =
false;
751 matching_verts.
append(index_in_set);
765 int index_in_set = matching_verts.
first();
766 for (
const int other_index_in_set : matching_verts) {
767 const int other_sorted_index = verts.
set_ids[sorted_i] + other_index_in_set;
769 index_in_set = other_index_in_set;
777 verts.
set_ids[other_set_i] = sorted_i + 1;
780 verts.
set_ids[sorted_i] = sorted_i;
797 const float threshold)
814 std::optional<GeoMismatch> mismatch = {};
834 if (!
sort_edges(mesh1.edges(), mesh2.edges(), verts, edges)) {
839 mesh1_attributes, mesh2_attributes,
AttrDomain::Edge, {
".edge_verts"}, edges, threshold);
859 {
".corner_vert",
".corner_edge"},
867 corners.recalculate_inverse_maps();
875 mesh1_attributes, mesh2_attributes,
AttrDomain::Face, {}, faces, threshold);
887 if (!
sort_edges(mesh1.edges(), mesh2.edges(), verts, edges)) {
905 corners.recalculate_inverse_maps();
922 for (
const int sorted_i : corners.from_sorted1.index_range()) {
923 if (corners.from_sorted1[sorted_i] != corners.from_sorted2[sorted_i]) {
963 if (!curves_sizes_match) {
972 const float threshold)
982 std::optional<GeoMismatch> mismatch = {};
993 curves1_attributes, curves2_attributes,
AttrDomain::Point, {}, points, threshold);
1015 for (
const int sorted_i :
curves.from_sorted1.index_range()) {
1016 if (
curves.from_sorted1[sorted_i] !=
curves.from_sorted2[sorted_i]) {
1022 return std::nullopt;
#define BLI_assert_unreachable()
MINLINE bool compare_threshold_relative(float value1, float value2, float thresh)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
IndexRange index_range() const
constexpr IndexRange()=default
Span< T > as_span() const
MutableSpan< T > as_mutable_span()
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 T * end() const
constexpr T * begin() 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)
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
Span< int > offsets() const
AttributeAccessor attributes() const
IndexMapping(const int64_t domain_size)
void recalculate_inverse_maps()
Array< int > from_sorted2
Array< int > from_sorted1
IndexRange index_range() const
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
static bool sort_curves(const OffsetIndices< int > offset_indices1, const OffsetIndices< int > offset_indices2, IndexMapping &curves)
std::optional< GeoMismatch > 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 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 void edges_from_vert_sets(const Span< int2 > edges, const Span< int > verts_to_sorted, const Span< int > vert_set_ids, MutableSpan< OrderedEdge > r_edges)
static void sort_indices(MutableSpan< int > indices, const Span< T > values, const int component_i)
static std::optional< GeoMismatch > verify_attributes_compatible(const AttributeAccessor &attributes1, const AttributeAccessor &attributes2)
static bool sort_corners_based_on_domain(const Span< int > corner_domain1, const Span< int > corner_domain2, const IndexMapping &domain, IndexMapping &corners)
static bool ignored_attribute(const StringRef id)
static void make_set_sizes_one(IndexMapping &indices)
static void update_set_sizes(const Span< int > set_ids, MutableSpan< int > set_sizes)
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)
static bool sort_edges(const Span< int2 > edges1, const Span< int2 > edges2, const IndexMapping &verts, IndexMapping &edges)
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 all_set_sizes_one(const Span< int > set_sizes)
static std::optional< GeoMismatch > construct_vert_mapping(const Mesh &mesh1, const Mesh &mesh2, IndexMapping &verts, IndexMapping &edges)
std::optional< GeoMismatch > compare_curves(const CurvesGeometry &curves1, const CurvesGeometry &curves2, float threshold)
Checks if the two curves geometries are different, returning the type of mismatch if any....
static bool values_different(const T value1, const T value2, const float threshold, const int component_i)
static std::optional< GeoMismatch > sort_domain_using_attributes(const AttributeAccessor &attributes1, const AttributeAccessor &attributes2, const AttrDomain domain, const Span< StringRef > excluded_attributes, IndexMapping &maps, const float threshold)
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 bool sort_faces_based_on_corners(const IndexMapping &corners, const Span< int > face_offsets1, const Span< int > face_offsets2, IndexMapping &faces)
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 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)
const char * mismatch_to_string(const GeoMismatch &mismatch)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
bool attribute_name_is_anonymous(const StringRef name)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
constexpr bool is_same_any_v
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2