49 return "The number of points is different";
51 return "The number of edges is different";
53 return "The number of corners is different";
55 return "The number of faces is different";
57 return "The number of curves is different";
59 return "Some values of the point attributes are different";
61 return "Some values of the edge attributes are different";
63 return "Some values of the corner attributes are different";
65 return "Some values of the face attributes are different";
67 return "Some values of the curve attributes are different";
69 return "The edge topology is different";
71 return "The face topology is different";
73 return "The curve topology is different";
75 return "The sets of attribute ids are different";
77 return "Some attributes with the same name have different types";
79 return "The geometries are the same up to a change of indices";
90 inverted_map[map[
i]] =
i;
136 std::stable_sort(
indices.begin(),
indices.end(), [&](
int i1,
int i2) {
137 const T value1 = values[i1];
138 const T value2 = values[i2];
139 if constexpr (is_same_any_v<T, int, float, bool, int8_t, OrderedEdge>) {
141 return value1 < value2;
144 return value1[component_i] < value2[component_i];
146 if constexpr (std::is_same_v<T, math::Quaternion>) {
149 return value1_quat[component_i] < value2_quat[component_i];
151 if constexpr (std::is_same_v<T, float4x4>) {
152 return value1.base_ptr()[component_i] < value2.base_ptr()[component_i];
154 if constexpr (std::is_same_v<T, int2>) {
155 for (
int i = 0;
i < 2;
i++) {
156 if (value1[
i] != value2[
i]) {
157 return value1[
i] < value2[
i];
162 if constexpr (std::is_same_v<T, ColorGeometry4b>) {
163 for (
int i = 0;
i < 4;
i++) {
164 if (value1[
i] != value2[
i]) {
165 return value1[
i] < value2[
i];
184 std::stable_sort(
indices.begin(),
indices.end(), [&](
int i1,
int i2) {
185 return set_ids[values_to_sorted[values[i1]]] < set_ids[values_to_sorted[values[i2]]];
196 const int component_i)
199 while (
i < set_sizes.
size()) {
200 const int set_size = set_sizes[
i];
224 while (
i < sorted_to_domain1.
size()) {
225 const int set_size = set_sizes[
i];
251 const float threshold,
252 const int component_i)
256 return value1 != value2;
259 if constexpr (std::is_same_v<T, float>) {
264#if (defined(__GNUC__) && (__GNUC__ >= 15) && !defined(__clang__))
265# define ASSERT_AND_ASSUME(expr) \
269# define ASSERT_AND_ASSUME(expr) BLI_assert(expr);
284 if constexpr (std::is_same_v<T, math::Quaternion>) {
290 if constexpr (std::is_same_v<T, float4x4>) {
293 value1.base_ptr()[component_i], value2.base_ptr()[component_i], threshold);
296#undef ASSERT_AND_ASSUME
312 const float threshold,
313 const int component_i)
316 const float value_threshold = 5 * threshold;
320 T previous = values1[0];
323 const T value1 = values1[sorted_to_values1[
i]];
324 const T value2 = values2[sorted_to_values2[
i]];
360 int previous = value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[0]]]];
363 const int value_id1 =
364 value_set_ids[values1_to_sorted[domain_to_values1[sorted_to_domain1[
i]]]];
365 const int value_id2 =
366 value_set_ids[values2_to_sorted[domain_to_values2[sorted_to_domain2[
i]]]];
367 if (value_id1 != value_id2) {
371 if (value_id1 != previous || set_ids[
i] ==
i) {
374 previous = value_id1;
387 int i = set_ids.
size() - 1;
391 int set_size =
i - set_ids[
i] + 1;
393 for (
int k =
i - set_size + 1; k <=
i; k++) {
394 set_sizes[k] = set_size;
408 vert_set_ids[verts_to_sorted[
e.y]]);
459 corners.from_sorted1,
460 corners.from_sorted2);
467 corners.from_sorted1,
468 corners.from_sorted2);
469 if (!corners_line_up) {
481 for (
const int face_i : smallest_corner_ids.
index_range()) {
482 const int face_start = face_offsets[face_i];
483 const int face_end = face_offsets[face_i + 1];
484 int smallest = corner_set_ids[corners_to_sorted[face_start]];
486 for (
const int corner_i : corners.drop_front(1)) {
487 const int corner_id = corner_set_ids[corners_to_sorted[corner_i]];
488 smallest = std::min(corner_id, smallest);
490 smallest_corner_ids[face_i] = smallest;
506 face_offsets1, corners.to_sorted1, corners.set_ids, smallest_corner_ids1);
508 face_offsets2, corners.to_sorted2, corners.set_ids, smallest_corner_ids2);
512 smallest_corner_ids1.
as_span(),
513 smallest_corner_ids2.
as_span(),
516 smallest_corner_ids1.
as_span(),
517 smallest_corner_ids2.
as_span(),
522 if (!faces_line_up) {
539 ELEM(
id,
".uv_select_vert",
".uv_select_edge",
".uv_select_face");
556 if (attribute_ids1 != attribute_ids2) {
560 for (
const StringRef id : attribute_ids1) {
563 if (!reader1 || !reader2) {
585 const float threshold)
595 for (
const StringRef id : attribute_ids) {
603 if (reader1.
domain != domain) {
608 std::optional<GeoMismatch> mismatch = {};
611 using T = decltype(dummy);
612 const VArraySpan<T> values1 = reader1.varray.typed<T>();
613 const VArraySpan<T> values2 = reader2.varray.typed<T>();
618 if constexpr (std::is_same_v<T, float2>) {
621 else if constexpr (std::is_same_v<T, float3>) {
630 for (
const int component_i :
IndexRange(num_loops)) {
640 if (!attributes_line_up) {
681 for (
const int sorted_i :
indices.set_sizes.index_range()) {
682 if (
indices.set_sizes[sorted_i] == 1) {
685 int match = sorted_i;
686 for (
const int other_index :
689 if (
indices.from_sorted1[sorted_i] ==
indices.from_sorted2[other_index]) {
694 std::swap(
indices.from_sorted2[sorted_i],
indices.from_sorted2[match]);
695 for (
const int other_set_i :
699 indices.set_ids[other_set_i] = sorted_i + 1;
700 indices.set_sizes[other_set_i] -= 1;
702 indices.set_ids[sorted_i] = sorted_i;
703 indices.set_sizes[sorted_i] = 1;
709 for (
const int size : set_sizes) {
744 mesh1.edges(), mesh1.
verts_num, vert_to_edge_offsets1, vert_to_edge_indices1);
748 mesh2.edges(), mesh2.
verts_num, vert_to_edge_offsets2, vert_to_edge_indices2);
750 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
751 const int vert1 =
verts.from_sorted1[sorted_i];
753 const Span<int> edges1 = vert_to_edge_map1[vert1];
758 const int vert2 =
verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set];
759 const Span<int> edges2 = vert_to_edge_map2[vert2];
760 if (edges1.
size() != edges2.
size()) {
763 bool vert_matches =
true;
764 for (
const int edge1 : edges1) {
765 bool found_matching_edge =
false;
766 for (
const int edge2 : edges2) {
768 found_matching_edge =
true;
772 if (!found_matching_edge) {
773 vert_matches =
false;
778 matching_verts.
append(index_in_set);
792 int index_in_set = matching_verts.
first();
793 for (
const int other_index_in_set : matching_verts) {
794 const int other_sorted_index =
verts.set_ids[sorted_i] + other_index_in_set;
795 if (
verts.from_sorted1[sorted_i] ==
verts.from_sorted2[other_sorted_index]) {
796 index_in_set = other_index_in_set;
800 std::swap(
verts.from_sorted2[sorted_i],
801 verts.from_sorted2[
verts.set_ids[sorted_i] + index_in_set]);
804 verts.set_ids[other_set_i] = sorted_i + 1;
805 verts.set_sizes[other_set_i] -= 1;
807 verts.set_ids[sorted_i] = sorted_i;
808 verts.set_sizes[sorted_i] = 1;
813 verts.recalculate_inverse_maps();
824 const float threshold)
841 std::optional<GeoMismatch> mismatch = {};
858 verts.recalculate_inverse_maps();
866 mesh1_attributes, mesh2_attributes,
AttrDomain::Edge, {
".edge_verts"}, edges, threshold);
886 {
".corner_vert",
".corner_edge"},
894 corners.recalculate_inverse_maps();
932 corners.recalculate_inverse_maps();
942 for (
const int sorted_i :
verts.from_sorted1.index_range()) {
943 if (
verts.from_sorted1[sorted_i] !=
verts.from_sorted2[sorted_i]) {
949 for (
const int sorted_i : corners.from_sorted1.index_range()) {
950 if (corners.from_sorted1[sorted_i] != corners.from_sorted2[sorted_i]) {
954 for (
const int sorted_i :
faces.from_sorted1.index_range()) {
955 if (
faces.from_sorted1[sorted_i] !=
faces.from_sorted2[sorted_i]) {
990 if (!curves_sizes_match) {
999 const float threshold)
1009 std::optional<GeoMismatch> mismatch = {};
1020 curves1_attributes, curves2_attributes,
AttrDomain::Point, {}, points, threshold);
1042 for (
const int sorted_i :
curves.from_sorted1.index_range()) {
1043 if (
curves.from_sorted1[sorted_i] !=
curves.from_sorted2[sorted_i]) {
1049 return std::nullopt;
1066 const int num_points = lattice1.
pntsu * lattice1.
pntsv * lattice1.
pntsw;
1070 const float3 co1 = bpoints1[
i].vec;
1071 const float3 co2 = bpoints2[
i].vec;
1080 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
#define ASSERT_AND_ASSUME(expr)
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 update_set_ids(MutableSpan< int > set_ids, const Span< T > values1, const Span< T > values2, const Span< int > sorted_to_values1, const Span< int > sorted_to_values2, const float threshold, 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)
const char * mismatch_to_string(const GeoMismatch &mismatch)
std::optional< GeoMismatch > compare_lattices(const Lattice &lattice1, const Lattice &lattice2, float threshold)
Checks if the two lattices are different, returning the type of mismatch if any.
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
VecBase< float, 3 > float3