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) {
654 for (
const int sorted_i :
indices.set_sizes.index_range()) {
655 if (
indices.set_sizes[sorted_i] == 1) {
658 int match = sorted_i;
659 for (
const int other_index :
662 if (
indices.from_sorted1[sorted_i] ==
indices.from_sorted2[other_index]) {
667 std::swap(
indices.from_sorted2[sorted_i],
indices.from_sorted2[match]);
668 for (
const int other_set_i :
672 indices.set_ids[other_set_i] = sorted_i + 1;
673 indices.set_sizes[other_set_i] -= 1;
675 indices.set_ids[sorted_i] = sorted_i;
676 indices.set_sizes[sorted_i] = 1;
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);
723 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
724 const int vert1 =
verts.from_sorted1[sorted_i];
726 const Span<int> edges1 = vert_to_edge_map1[vert1];
731 const int vert2 =
verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set];
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;
768 if (
verts.from_sorted1[sorted_i] ==
verts.from_sorted2[other_sorted_index]) {
769 index_in_set = other_index_in_set;
773 std::swap(
verts.from_sorted2[sorted_i],
774 verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set]);
777 verts.set_ids[other_set_i] = sorted_i + 1;
778 verts.set_sizes[other_set_i] -= 1;
780 verts.set_ids[sorted_i] = sorted_i;
781 verts.set_sizes[sorted_i] = 1;
786 verts.recalculate_inverse_maps();
797 const float threshold)
814 std::optional<GeoMismatch> mismatch = {};
831 verts.recalculate_inverse_maps();
839 mesh1_attributes, mesh2_attributes,
AttrDomain::Edge, {
".edge_verts"}, edges, threshold);
859 {
".corner_vert",
".corner_edge"},
867 corners.recalculate_inverse_maps();
905 corners.recalculate_inverse_maps();
915 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
916 if (
verts.from_sorted1[sorted_i] !=
verts.from_sorted2[sorted_i]) {
922 for (
const int sorted_i : corners.from_sorted1.index_range()) {
923 if (corners.from_sorted1[sorted_i] != corners.from_sorted2[sorted_i]) {
927 for (
const int sorted_i :
faces.from_sorted1.index_range()) {
928 if (
faces.from_sorted1[sorted_i] !=
faces.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
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
IndexRange index_range() const
Span< T > as_span() const
MutableSpan< T > as_mutable_span()
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)
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
VecBase< float, 4 > float4
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