Blender V5.0
mesh_to_curve_convert.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_deform.hh"
6#include "BLI_array.hh"
7#include "BLI_array_utils.hh"
8#include "BLI_set.hh"
9
10#include "BKE_attribute.hh"
11#include "BKE_attribute_math.hh"
12#include "BKE_curves.hh"
13#include "BKE_mesh.hh"
14
15#include "GEO_mesh_to_curve.hh"
16#include "GEO_randomize.hh"
17
18namespace blender::geometry {
19
20/* Don't copy attributes that are built-in on meshes but not on curves. */
21static auto filter_builtin_attributes(const bke::AttributeAccessor &mesh_attributes,
22 const bke::AttributeAccessor &curves_attributes,
23 Set<StringRef> &storage,
24 const bke::AttributeFilter &attribute_filter)
25{
26 for (const StringRef id : mesh_attributes.all_ids()) {
27 if (mesh_attributes.is_builtin(id) && !curves_attributes.is_builtin(id)) {
28 storage.add(id);
29 }
30 }
31 return bke::attribute_filter_with_skip_ref(attribute_filter, storage);
32}
33
35 const bke::AttributeAccessor &mesh_attributes,
36 const Span<int> vert_indices,
37 const Span<int> curve_offsets,
38 const IndexRange cyclic_curves,
39 const bke::AttributeFilter &attribute_filter)
40{
41 bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
42 curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
43 curves.offsets_for_write().last() = vert_indices.size();
45
46 bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
47
48 if (!cyclic_curves.is_empty()) {
49 curves.cyclic_for_write().slice(cyclic_curves).fill(true);
50 }
51
52 Set<StringRef> skip_storage;
53 const auto attribute_filter_with_skip = filter_builtin_attributes(
54 mesh_attributes, curves_attributes, skip_storage, attribute_filter);
55
56 bke::gather_attributes(mesh_attributes,
59 attribute_filter_with_skip,
60 vert_indices,
61 curves_attributes);
62
63 mesh_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
64 if (iter.domain == bke::AttrDomain::Point) {
65 return;
66 }
67 if (iter.data_type == bke::AttrType::String) {
68 return;
69 }
70 if (attribute_filter_with_skip.allow_skip(iter.name)) {
71 return;
72 }
73
75 /* Some attributes might not exist if they were builtin on domains that don't have
76 * any elements, i.e. a face attribute on the output of the line primitive node. */
77 if (!src) {
78 return;
79 }
82 if (!dst) {
83 return;
84 }
85 bke::attribute_math::gather(*src, vert_indices, dst.span);
86 dst.finish();
87 });
88
90
91 return curves;
92}
93
102
104 const Span<int2> edges)
105{
106 /* Compute the number of edges connecting to each vertex. */
107 Array<int> neighbor_offsets_data(verts_num + 1, 0);
108 offset_indices::build_reverse_offsets(edges.cast<int>(), neighbor_offsets_data);
109 const OffsetIndices<int> neighbor_offsets(neighbor_offsets_data);
110
111 /* Use as an index into the "neighbor group" for each vertex. */
112 Array<int> used_slots(verts_num, 0);
113 /* Calculate the indices of each vertex's neighboring edges. */
114 Array<int> neighbors(edges.size() * 2);
115 for (const int i : edges.index_range()) {
116 const int v1 = edges[i][0];
117 const int v2 = edges[i][1];
118 neighbors[neighbor_offsets[v1].start() + used_slots[v1]] = v2;
119 neighbors[neighbor_offsets[v2].start() + used_slots[v2]] = v1;
120 used_slots[v1]++;
121 used_slots[v2]++;
122 }
123
124 Vector<int> vert_indices;
125 vert_indices.reserve(edges.size());
126 Vector<int> curve_offsets;
127
128 /* Now use the neighbor group offsets calculated above to count used edges at each vertex. */
129 Array<int> unused_edges = std::move(used_slots);
130
131 for (const int start_vert : IndexRange(verts_num)) {
132 /* Don't start at vertices with two neighbors, which may become part of cyclic curves. */
133 if (neighbor_offsets[start_vert].size() == 2) {
134 continue;
135 }
136
137 /* The vertex has no connected edges, or they were already used. */
138 if (unused_edges[start_vert] == 0) {
139 continue;
140 }
141
142 for (const int neighbor : neighbors.as_span().slice(neighbor_offsets[start_vert])) {
143 int current_vert = start_vert;
144 int next_vert = neighbor;
145
146 if (unused_edges[next_vert] == 0) {
147 continue;
148 }
149
150 /* Start a new curve in the output. */
151 curve_offsets.append(vert_indices.size());
152 vert_indices.append(current_vert);
153
154 /* Follow connected edges until we read a vertex with more than two connected edges. */
155 while (true) {
156 int last_vert = current_vert;
157 current_vert = next_vert;
158
159 vert_indices.append(current_vert);
160 unused_edges[current_vert]--;
161 unused_edges[last_vert]--;
162
163 if (neighbor_offsets[current_vert].size() != 2) {
164 break;
165 }
166
167 const int offset = neighbor_offsets[current_vert].start();
168 const int next_a = neighbors[offset];
169 const int next_b = neighbors[offset + 1];
170 next_vert = (last_vert == next_a) ? next_b : next_a;
171 }
172 }
173 }
174
175 /* All curves added after this are cyclic. */
176 const int cyclic_start = curve_offsets.size();
177
178 /* All remaining edges are part of cyclic curves because
179 * we skipped starting at vertices with two edges before. */
180 for (const int start_vert : IndexRange(verts_num)) {
181 if (unused_edges[start_vert] != 2) {
182 continue;
183 }
184
185 int current_vert = start_vert;
186 int next_vert = neighbors[neighbor_offsets[current_vert].start()];
187
188 curve_offsets.append(vert_indices.size());
189 vert_indices.append(current_vert);
190
191 /* Follow connected edges until we loop back to the start vertex. */
192 while (next_vert != start_vert) {
193 const int last_vert = current_vert;
194 current_vert = next_vert;
195
196 vert_indices.append(current_vert);
197 unused_edges[current_vert]--;
198 unused_edges[last_vert]--;
199
200 const int offset = neighbor_offsets[current_vert].start();
201 const int next_a = neighbors[offset];
202 const int next_b = neighbors[offset + 1];
203 next_vert = (last_vert == next_a) ? next_b : next_a;
204 }
205 }
206
207 const IndexRange cyclic_curves = curve_offsets.index_range().drop_front(cyclic_start);
208
209 return {std::move(vert_indices), std::move(curve_offsets), cyclic_curves};
210}
211
213 const Mesh &mesh, const Span<int2> edges, const bke::AttributeFilter &attribute_filter)
214{
217 output.vert_indices,
218 output.curve_offsets,
219 output.cyclic_curves,
220 attribute_filter);
221}
222
224 const IndexMask &selection,
225 const bke::AttributeFilter &attribute_filter)
226{
227 const Span<int2> edges = mesh.edges();
228 if (selection.size() == edges.size()) {
229 return edges_to_curves_convert(mesh, edges, attribute_filter);
230 }
231 Array<int2> selected_edges(selection.size());
232 array_utils::gather(edges, selection, selected_edges.as_mutable_span());
233 return edges_to_curves_convert(mesh, selected_edges, attribute_filter);
234}
235
238 const IndexMask &selection)
239{
240 bke::CurvesGeometry curves;
241 if (selection.size() == faces.size()) {
243 mesh.runtime->face_offsets_sharing_info,
244 &curves.curve_offsets,
245 &curves.runtime->curve_offsets_sharing_info);
246 curves.curve_num = faces.size();
247 curves.resize(mesh.corners_num, faces.size());
248 }
249 else {
250 curves.resize(0, selection.size());
252 curves.resize(curves.offsets().last(), curves.curves_num());
253 }
254
256 curves.cyclic_for_write().fill(true);
258 return curves;
259}
260
263 const OffsetIndices<int> points_by_curve,
264 const IndexMask &selection,
265 Array<int> &map_data)
266{
267 if (selection.size() == faces.size()) {
268 return mesh.corner_verts();
269 }
270 map_data.reinitialize(points_by_curve.total_size());
272 faces, points_by_curve, selection, mesh.corner_verts(), map_data.as_mutable_span());
273 return map_data;
274}
275
277 const IndexMask &selection,
278 const bke::AttributeFilter &attribute_filter)
279{
280 const OffsetIndices faces = mesh.faces();
281 const bke::AttributeAccessor src_attributes = mesh.attributes();
282
283 bke::CurvesGeometry curves = create_curves_for_faces(mesh, faces, selection);
284 const OffsetIndices points_by_curve = curves.points_by_curve();
285 bke::MutableAttributeAccessor dst_attributes = curves.attributes_for_write();
286
287 Array<int> point_to_vert_data;
288 const Span<int> point_to_vert_map = create_point_to_vert_map(
289 mesh, faces, points_by_curve, selection, point_to_vert_data);
290
291 Set<StringRef> skip_storage;
292 const auto attribute_filter_with_skip = filter_builtin_attributes(
293 src_attributes, dst_attributes, skip_storage, attribute_filter);
294
295 bke::gather_attributes(src_attributes,
298 attribute_filter_with_skip,
299 point_to_vert_map,
300 dst_attributes);
301
302 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
303 if (iter.domain != bke::AttrDomain::Edge) {
304 return;
305 }
306 if (iter.data_type == bke::AttrType::String) {
307 return;
308 }
309 if (attribute_filter_with_skip.allow_skip(iter.name)) {
310 return;
311 }
312 const GVArray src = *iter.get(bke::AttrDomain::Point);
315 if (!dst) {
316 return;
317 }
318 bke::attribute_math::gather(src, point_to_vert_map, dst.span);
319 dst.finish();
320 });
321
322 bke::gather_attributes(src_attributes,
325 attribute_filter_with_skip,
326 selection,
327 dst_attributes);
328
332 attribute_filter_with_skip,
333 faces,
334 points_by_curve,
335 selection,
336 dst_attributes);
337
338 return curves;
339}
340
341} // namespace blender::geometry
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
#define BLI_NOINLINE
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
AttributeSet attributes
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
constexpr bool is_empty() const
constexpr IndexRange drop_front(int64_t n) const
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool add(const Key &key)
Definition BLI_set.hh:248
Span< NewT > constexpr cast() const
Definition BLI_span.hh:418
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
bool is_builtin(const StringRef attribute_id) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
Set< StringRefNull > all_ids() const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
MutableAttributeAccessor attributes_for_write()
void resize(int points_num, int curves_num)
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
MutableSpan< bool > cyclic_for_write()
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
#define output
static char faces[256]
void gather_group_to_group(const OffsetIndices< int > src_offsets, const OffsetIndices< int > dst_offsets, const IndexMask &selection, const Span< T > src, MutableSpan< T > dst)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
void gather_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static Span< int > create_point_to_vert_map(const Mesh &mesh, const OffsetIndices< int > faces, const OffsetIndices< int > points_by_curve, const IndexMask &selection, Array< int > &map_data)
static auto filter_builtin_attributes(const bke::AttributeAccessor &mesh_attributes, const bke::AttributeAccessor &curves_attributes, Set< StringRef > &storage, const bke::AttributeFilter &attribute_filter)
static bke::CurvesGeometry create_curves_for_faces(const Mesh &mesh, const OffsetIndices< int > faces, const IndexMask &selection)
static BLI_NOINLINE CurveFromEdgesOutput edges_to_curve_point_indices(const int verts_num, const Span< int2 > edges)
bke::CurvesGeometry mesh_edges_to_curves_convert(const Mesh &mesh, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
bke::CurvesGeometry create_curve_from_vert_indices(const bke::AttributeAccessor &mesh_attributes, Span< int > vert_indices, Span< int > curve_offsets, IndexRange cyclic_curves, const bke::AttributeFilter &attribute_filter)
void debug_randomize_curve_order(bke::CurvesGeometry *curves)
Definition randomize.cc:257
static BLI_NOINLINE bke::CurvesGeometry edges_to_curves_convert(const Mesh &mesh, const Span< int2 > edges, const bke::AttributeFilter &attribute_filter)
bke::CurvesGeometry mesh_faces_to_curves_convert(const Mesh &mesh, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
void copy_shared_pointer(T *src_ptr, const ImplicitSharingInfo *src_sharing_info, T **r_dst_ptr, const ImplicitSharingInfo **r_dst_sharing_info)
void build_reverse_offsets(Span< int > indices, MutableSpan< int > offsets)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
CurvesGeometryRuntimeHandle * runtime
ListBase vertex_group_names
int corners_num
MeshRuntimeHandle * runtime
ListBase vertex_group_names
int * face_offset_indices
int verts_num
i
Definition text_draw.cc:230