Blender V4.3
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 "BLI_array.hh"
6#include "BLI_array_utils.hh"
7#include "BLI_set.hh"
8#include "BLI_task.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
21 const bke::AttributeAccessor &mesh_attributes,
22 const Span<int> vert_indices,
23 const Span<int> curve_offsets,
24 const IndexRange cyclic_curves,
25 const bke::AttributeFilter &attribute_filter)
26{
27 bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
28 curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
29 curves.offsets_for_write().last() = vert_indices.size();
30 curves.fill_curve_types(CURVE_TYPE_POLY);
31
32 bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
33
34 if (!cyclic_curves.is_empty()) {
35 curves.cyclic_for_write().slice(cyclic_curves).fill(true);
36 }
37
38 /* Don't copy attributes that are built-in on meshes but not on curves. */
40 for (const StringRef id : mesh_attributes.all_ids()) {
41 if (mesh_attributes.is_builtin(id) && !curves_attributes.is_builtin(id)) {
42 skip.add(id);
43 }
44 }
45 const auto attribute_filter_with_skip = bke::attribute_filter_with_skip_ref(attribute_filter,
46 skip);
47
48 bke::gather_attributes(mesh_attributes,
51 attribute_filter_with_skip,
52 vert_indices,
53 curves_attributes);
54
55 mesh_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
56 if (iter.domain == bke::AttrDomain::Point) {
57 return;
58 }
59 if (iter.data_type == CD_PROP_STRING) {
60 return;
61 }
62 if (attribute_filter_with_skip.allow_skip(iter.name)) {
63 return;
64 }
65
67 /* Some attributes might not exist if they were builtin on domains that don't have
68 * any elements, i.e. a face attribute on the output of the line primitive node. */
69 if (!src) {
70 return;
71 }
74 if (!dst) {
75 return;
76 }
77 bke::attribute_math::gather(*src, vert_indices, dst.span);
78 dst.finish();
79 });
80
82
83 return curves;
84}
85
94
96 const Span<int2> edges)
97{
98 /* Compute the number of edges connecting to each vertex. */
99 Array<int> neighbor_offsets_data(verts_num + 1, 0);
100 offset_indices::build_reverse_offsets(edges.cast<int>(), neighbor_offsets_data);
101 const OffsetIndices<int> neighbor_offsets(neighbor_offsets_data);
102
103 /* Use as an index into the "neighbor group" for each vertex. */
104 Array<int> used_slots(verts_num, 0);
105 /* Calculate the indices of each vertex's neighboring edges. */
106 Array<int> neighbors(edges.size() * 2);
107 for (const int i : edges.index_range()) {
108 const int v1 = edges[i][0];
109 const int v2 = edges[i][1];
110 neighbors[neighbor_offsets[v1].start() + used_slots[v1]] = v2;
111 neighbors[neighbor_offsets[v2].start() + used_slots[v2]] = v1;
112 used_slots[v1]++;
113 used_slots[v2]++;
114 }
115
116 Vector<int> vert_indices;
117 vert_indices.reserve(edges.size());
118 Vector<int> curve_offsets;
119
120 /* Now use the neighbor group offsets calculated above to count used edges at each vertex. */
121 Array<int> unused_edges = std::move(used_slots);
122
123 for (const int start_vert : IndexRange(verts_num)) {
124 /* Don't start at vertices with two neighbors, which may become part of cyclic curves. */
125 if (neighbor_offsets[start_vert].size() == 2) {
126 continue;
127 }
128
129 /* The vertex has no connected edges, or they were already used. */
130 if (unused_edges[start_vert] == 0) {
131 continue;
132 }
133
134 for (const int neighbor : neighbors.as_span().slice(neighbor_offsets[start_vert])) {
135 int current_vert = start_vert;
136 int next_vert = neighbor;
137
138 if (unused_edges[next_vert] == 0) {
139 continue;
140 }
141
142 /* Start a new curve in the output. */
143 curve_offsets.append(vert_indices.size());
144 vert_indices.append(current_vert);
145
146 /* Follow connected edges until we read a vertex with more than two connected edges. */
147 while (true) {
148 int last_vert = current_vert;
149 current_vert = next_vert;
150
151 vert_indices.append(current_vert);
152 unused_edges[current_vert]--;
153 unused_edges[last_vert]--;
154
155 if (neighbor_offsets[current_vert].size() != 2) {
156 break;
157 }
158
159 const int offset = neighbor_offsets[current_vert].start();
160 const int next_a = neighbors[offset];
161 const int next_b = neighbors[offset + 1];
162 next_vert = (last_vert == next_a) ? next_b : next_a;
163 }
164 }
165 }
166
167 /* All curves added after this are cyclic. */
168 const int cyclic_start = curve_offsets.size();
169
170 /* All remaining edges are part of cyclic curves because
171 * we skipped starting at vertices with two edges before. */
172 for (const int start_vert : IndexRange(verts_num)) {
173 if (unused_edges[start_vert] != 2) {
174 continue;
175 }
176
177 int current_vert = start_vert;
178 int next_vert = neighbors[neighbor_offsets[current_vert].start()];
179
180 curve_offsets.append(vert_indices.size());
181 vert_indices.append(current_vert);
182
183 /* Follow connected edges until we loop back to the start vertex. */
184 while (next_vert != start_vert) {
185 const int last_vert = current_vert;
186 current_vert = next_vert;
187
188 vert_indices.append(current_vert);
189 unused_edges[current_vert]--;
190 unused_edges[last_vert]--;
191
192 const int offset = neighbor_offsets[current_vert].start();
193 const int next_a = neighbors[offset];
194 const int next_b = neighbors[offset + 1];
195 next_vert = (last_vert == next_a) ? next_b : next_a;
196 }
197 }
198
199 const IndexRange cyclic_curves = curve_offsets.index_range().drop_front(cyclic_start);
200
201 return {std::move(vert_indices), std::move(curve_offsets), cyclic_curves};
202}
203
205 const Mesh &mesh, const Span<int2> edges, const bke::AttributeFilter &attribute_filter)
206{
207 CurveFromEdgesOutput output = edges_to_curve_point_indices(mesh.verts_num, edges);
208 return create_curve_from_vert_indices(mesh.attributes(),
209 output.vert_indices,
210 output.curve_offsets,
211 output.cyclic_curves,
212 attribute_filter);
213}
214
216 const IndexMask &selection,
217 const bke::AttributeFilter &attribute_filter)
218{
219 const Span<int2> edges = mesh.edges();
220 if (selection.size() == edges.size()) {
221 return edges_to_curves_convert(mesh, edges, attribute_filter);
222 }
223 Array<int2> selected_edges(selection.size());
224 array_utils::gather(edges, selection, selected_edges.as_mutable_span());
225 return edges_to_curves_convert(mesh, selected_edges, attribute_filter);
226}
227
228} // namespace blender::geometry
Low-level operations for curves.
#define BLI_NOINLINE
@ CURVE_TYPE_POLY
@ CD_PROP_STRING
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
constexpr bool is_empty() const
constexpr IndexRange drop_front(int64_t n) const
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:253
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
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
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)
static BLI_NOINLINE CurveFromEdgesOutput edges_to_curve_point_indices(const int verts_num, const Span< int2 > edges)
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:196
static BLI_NOINLINE bke::CurvesGeometry edges_to_curves_convert(const Mesh &mesh, const Span< int2 > edges, const bke::AttributeFilter &attribute_filter)
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
void build_reverse_offsets(Span< int > indices, MutableSpan< int > offsets)