Blender V4.3
GEO_merge_curves_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "BKE_attribute.hh"
6#include "BKE_curves.hh"
7
8#include "BLI_array_utils.hh"
9#include "GEO_merge_curves.hh"
10
11#include "testing/testing.h"
12
13using namespace blender::bke;
14
16
18{
19 BLI_assert(!offsets.is_empty());
20 const int curves_num = offsets.size() - 1;
21 BLI_assert(cyclic.size() == curves_num);
22 const int points_num = offsets.last();
23
24 bke::CurvesGeometry curves(points_num, curves_num);
25 curves.offsets_for_write().copy_from(offsets);
26 curves.cyclic_for_write().copy_from(cyclic);
27
28 /* Attribute storing original indices to test point remapping. */
29 SpanAttributeWriter<int> test_indices_writer =
30 curves.attributes_for_write().lookup_or_add_for_write_span<int>(
32 array_utils::fill_index_range(test_indices_writer.span);
33 test_indices_writer.finish();
34
35 return curves;
36}
37
38TEST(merge_curves, NoConnections)
39{
40 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
41 {false, true, true, false});
42
43 Array<int> connect_to_curve(4, -1);
44 Array<bool> flip_direction(4, false);
45
47 src_curves, connect_to_curve, flip_direction, {});
48 const VArraySpan<bool> cyclic = dst_curves.cyclic();
49
50 EXPECT_EQ(dst_curves.points_num(), 12);
51 EXPECT_EQ(dst_curves.curves_num(), 4);
52 EXPECT_EQ_ARRAY(Span({0, 3, 6, 9, 12}).data(), dst_curves.offsets().data(), 5);
53 EXPECT_EQ_ARRAY(Span({false, true, true, false}).data(), cyclic.data(), 4);
54}
55
56TEST(merge_curves, ConnectSingleCurve)
57{
58 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
59 {false, true, true, false});
60
61 Array<int> connect_to_curve = {-1, -1, -1, 1};
62 Array<bool> flip_direction(4, false);
63
65 src_curves, connect_to_curve, flip_direction, {});
66 const VArraySpan<bool> cyclic = dst_curves.cyclic();
67 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
68
69 EXPECT_EQ(dst_curves.points_num(), 12);
70 EXPECT_EQ(dst_curves.curves_num(), 3);
71 EXPECT_EQ_ARRAY(Span({0, 3, 6, 12}).data(), dst_curves.offsets().data(), 4);
72 EXPECT_EQ_ARRAY(Span({false, true, false}).data(), cyclic.data(), 3);
73 EXPECT_EQ_ARRAY(Span({0, 1, 2, 6, 7, 8, 9, 10, 11, 3, 4, 5}).data(), dst_indices.data(), 12);
74}
75
76TEST(merge_curves, ReverseCurves)
77{
78 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
79 {false, true, true, false});
80
81 Array<int> connect_to_curve = {-1, -1, -1, -1};
82 Array<bool> flip_direction = {false, true, false, true};
83
85 src_curves, connect_to_curve, flip_direction, {});
86 const VArraySpan<bool> cyclic = dst_curves.cyclic();
87 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
88
89 EXPECT_EQ(dst_curves.points_num(), 12);
90 EXPECT_EQ(dst_curves.curves_num(), 4);
91 EXPECT_EQ_ARRAY(Span({0, 3, 6, 9, 12}).data(), dst_curves.offsets().data(), 5);
92 EXPECT_EQ_ARRAY(Span({false, true, true, false}).data(), cyclic.data(), 3);
93 EXPECT_EQ_ARRAY(Span({0, 1, 2, 5, 4, 3, 6, 7, 8, 11, 10, 9}).data(), dst_indices.data(), 12);
94}
95
96TEST(merge_curves, ConnectAndReverseCurves)
97{
98 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
99 {false, true, true, false});
100
101 Array<int> connect_to_curve = {3, 0, -1, -1};
102 Array<bool> flip_direction = {true, false, true, false};
103
105 src_curves, connect_to_curve, flip_direction, {});
106 const VArraySpan<bool> cyclic = dst_curves.cyclic();
107 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
108
109 EXPECT_EQ(dst_curves.points_num(), 12);
110 EXPECT_EQ(dst_curves.curves_num(), 2);
111 EXPECT_EQ_ARRAY(Span({0, 9, 12}).data(), dst_curves.offsets().data(), 3);
112 EXPECT_EQ_ARRAY(Span({false, true}).data(), cyclic.data(), 2);
113 EXPECT_EQ_ARRAY(Span({3, 4, 5, 2, 1, 0, 9, 10, 11, 8, 7, 6}).data(), dst_indices.data(), 12);
114}
115
116TEST(merge_curves, CyclicConnection)
117{
118 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
119 {false, true, true, false});
120
121 Array<int> connect_to_curve = {-1, 3, -1, 1};
122 Array<bool> flip_direction(4, false);
123
125 src_curves, connect_to_curve, flip_direction, {});
126 const VArraySpan<bool> cyclic = dst_curves.cyclic();
127 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
128
129 EXPECT_EQ(dst_curves.points_num(), 12);
130 EXPECT_EQ(dst_curves.curves_num(), 3);
131 EXPECT_EQ_ARRAY(Span({0, 3, 9, 12}).data(), dst_curves.offsets().data(), 4);
132 EXPECT_EQ_ARRAY(Span({false, true, true}).data(), cyclic.data(), 3);
133 EXPECT_EQ_ARRAY(Span({0, 1, 2, 3, 4, 5, 9, 10, 11, 6, 7, 8}).data(), dst_indices.data(), 12);
134}
135
136TEST(merge_curves, SelfConnectCurve)
137{
138 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
139 {false, false, false, false});
140
141 Array<int> connect_to_curve = {-1, 1, 2, -1};
142 Array<bool> flip_direction(4, false);
143
145 src_curves, connect_to_curve, flip_direction, {});
146 const VArraySpan<bool> cyclic = dst_curves.cyclic();
147 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
148
149 EXPECT_EQ(dst_curves.points_num(), 12);
150 EXPECT_EQ(dst_curves.curves_num(), 4);
151 EXPECT_EQ_ARRAY(Span({0, 3, 6, 9, 12}).data(), dst_curves.offsets().data(), 5);
152 EXPECT_EQ_ARRAY(Span({false, true, true, false}).data(), cyclic.data(), 4);
153 EXPECT_EQ_ARRAY(Span({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}).data(), dst_indices.data(), 12);
154}
155
156TEST(merge_curves, MergeAll)
157{
158 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
159 {false, true, true, false});
160
161 Array<int> connect_to_curve = {2, 0, 3, 1};
162 Array<bool> flip_direction(4, false);
163
165 src_curves, connect_to_curve, flip_direction, {});
166 const VArraySpan<bool> cyclic = dst_curves.cyclic();
167 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
168
169 EXPECT_EQ(dst_curves.points_num(), 12);
170 EXPECT_EQ(dst_curves.curves_num(), 1);
171 EXPECT_EQ_ARRAY(Span({0, 12}).data(), dst_curves.offsets().data(), 2);
172 EXPECT_EQ_ARRAY(Span({true}).data(), cyclic.data(), 1);
173 EXPECT_EQ_ARRAY(Span({0, 1, 2, 6, 7, 8, 9, 10, 11, 3, 4, 5}).data(), dst_indices.data(), 12);
174}
175
176TEST(merge_curves, Branching)
177{
178 bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
179 {false, true, true, false});
180
181 /* Multiple curves connect to curve 2, one connection is ignored. */
182 Array<int> connect_to_curve = {2, 2, -1, -1};
183 Array<bool> flip_direction(4, false);
184
186 src_curves, connect_to_curve, flip_direction, {});
187 const VArraySpan<bool> cyclic = dst_curves.cyclic();
188 const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
189
190 EXPECT_EQ(dst_curves.points_num(), 12);
191 EXPECT_EQ(dst_curves.curves_num(), 3);
192 EXPECT_EQ_ARRAY(Span({0, 6, 9, 12}).data(), dst_curves.offsets().data(), 4);
193 EXPECT_EQ_ARRAY(Span({false, false, false}).data(), cyclic.data(), 3);
194 EXPECT_EQ_ARRAY(Span({0, 1, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11}).data(), dst_indices.data(), 12);
195}
196
197} // namespace blender::geometry::tests
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:50
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr bool is_empty() const
Definition BLI_span.hh:261
GAttributeReader lookup(const StringRef attribute_id) const
VArray< bool > cyclic() const
void fill_index_range(MutableSpan< T > span, const T start=0)
TEST(merge_curves, NoConnections)
static bke::CurvesGeometry create_test_curves(Span< int > offsets, Span< bool > cyclic)
bke::CurvesGeometry curves_merge_endpoints(const bke::CurvesGeometry &src_curves, Span< int > connect_to_curve, Span< bool > flip_direction, const bke::AttributeFilter &attribute_filter)