Blender V5.0
separate_geometry.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BKE_curves.hh"
8#include "BKE_customdata.hh"
10#include "BKE_grease_pencil.hh"
11#include "BKE_instances.hh"
12#include "BKE_mesh.hh"
13#include "BKE_pointcloud.hh"
14
16
18
19namespace blender::geometry {
20
21using bke::AttrDomain;
22
24static std::optional<bke::CurvesGeometry> separate_curves_selection(
25 const bke::CurvesGeometry &src_curves,
26 const fn::FieldContext &field_context,
27 const fn::Field<bool> &selection_field,
28 const AttrDomain domain,
29 const bke::AttributeFilter &attribute_filter)
30{
31 const int domain_size = src_curves.attributes().domain_size(domain);
32 fn::FieldEvaluator evaluator{field_context, domain_size};
33 evaluator.set_selection(selection_field);
34 evaluator.evaluate();
35 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
36 if (selection.size() == domain_size) {
37 return std::nullopt;
38 }
39 if (selection.is_empty()) {
40 return bke::CurvesGeometry();
41 }
42
43 if (domain == AttrDomain::Point) {
44 return bke::curves_copy_point_selection(src_curves, selection, attribute_filter);
45 }
46 if (domain == AttrDomain::Curve) {
47 return bke::curves_copy_curve_selection(src_curves, selection, attribute_filter);
48 }
50 return std::nullopt;
51}
52
54static std::optional<PointCloud *> separate_pointcloud_selection(
55 const PointCloud &src_pointcloud,
56 const fn::Field<bool> &selection_field,
57 const bke::AttributeFilter &attribute_filter)
58{
59 const bke::PointCloudFieldContext context{src_pointcloud};
60 fn::FieldEvaluator evaluator{context, src_pointcloud.totpoint};
61 evaluator.set_selection(selection_field);
62 evaluator.evaluate();
63 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
64 if (selection.size() == src_pointcloud.totpoint) {
65 return std::nullopt;
66 }
67 if (selection.is_empty()) {
68 return nullptr;
69 }
70
71 PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
72 bke::gather_attributes(src_pointcloud.attributes(),
73 AttrDomain::Point,
74 AttrDomain::Point,
75 attribute_filter,
76 selection,
77 pointcloud->attributes_for_write());
78 return pointcloud;
79}
80
82 const fn::Field<bool> &selection_field,
83 const bke::AttributeFilter &attribute_filter)
84{
85 bke::Instances &instances = *geometry_set.get_instances_for_write();
86 bke::InstancesFieldContext field_context{instances};
87
88 fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
89 evaluator.set_selection(selection_field);
90 evaluator.evaluate();
91 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
92 if (selection.is_empty()) {
93 geometry_set.remove<bke::InstancesComponent>();
94 return;
95 }
96
97 instances.remove(selection, attribute_filter);
98}
99
100static std::optional<Mesh *> separate_mesh_selection(const Mesh &mesh,
101 const fn::Field<bool> &selection_field,
102 const AttrDomain selection_domain,
104 const bke::AttributeFilter &attribute_filter)
105{
106 const bke::AttributeAccessor attributes = mesh.attributes();
107 const bke::MeshFieldContext context(mesh, selection_domain);
108 fn::FieldEvaluator evaluator(context, attributes.domain_size(selection_domain));
109 evaluator.add(selection_field);
110 evaluator.evaluate();
111 const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
112
113 switch (mode) {
115 return mesh_copy_selection(mesh, selection, selection_domain, attribute_filter);
117 return mesh_copy_selection_keep_verts(mesh, selection, selection_domain, attribute_filter);
119 return mesh_copy_selection_keep_edges(mesh, selection, selection_domain, attribute_filter);
120 }
121 return nullptr;
122}
123
124static std::optional<GreasePencil *> separate_grease_pencil_layer_selection(
125 const GreasePencil &src_grease_pencil,
126 const fn::Field<bool> &selection_field,
127 const bke::AttributeFilter &attribute_filter)
128{
129 const bke::AttributeAccessor attributes = src_grease_pencil.attributes();
130 const bke::GeometryFieldContext context(src_grease_pencil);
131
132 fn::FieldEvaluator evaluator(context, attributes.domain_size(AttrDomain::Layer));
133 evaluator.set_selection(selection_field);
134 evaluator.evaluate();
135
136 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
137 if (selection.size() == attributes.domain_size(AttrDomain::Layer)) {
138 return std::nullopt;
139 }
140 if (selection.is_empty()) {
141 return nullptr;
142 }
143
144 const int dst_layers_num = selection.size();
145
146 GreasePencil *dst_grease_pencil = BKE_grease_pencil_new_nomain();
147 BKE_grease_pencil_copy_parameters(src_grease_pencil, *dst_grease_pencil);
148 dst_grease_pencil->add_layers_with_empty_drawings_for_eval(dst_layers_num);
149
150 selection.foreach_index([&](const int src_layer_i, const int dst_layer_i) {
151 const bke::greasepencil::Layer &src_layer = src_grease_pencil.layer(src_layer_i);
152 const bke::greasepencil::Drawing *src_drawing = src_grease_pencil.get_eval_drawing(src_layer);
153
154 bke::greasepencil::Layer &dst_layer = dst_grease_pencil->layer(dst_layer_i);
155 bke::greasepencil::Drawing &dst_drawing = *dst_grease_pencil->get_eval_drawing(dst_layer);
156
157 BKE_grease_pencil_copy_layer_parameters(src_layer, dst_layer);
158 dst_layer.set_name(src_layer.name());
159
160 if (src_drawing) {
161 dst_drawing = *src_drawing;
162 }
163 });
164
165 bke::gather_attributes(src_grease_pencil.attributes(),
166 AttrDomain::Layer,
167 AttrDomain::Layer,
168 attribute_filter,
169 selection,
170 dst_grease_pencil->attributes_for_write());
171
172 return dst_grease_pencil;
173}
174
176 const AttrDomain domain,
178 const fn::Field<bool> &selection,
179 const bke::AttributeFilter &attribute_filter,
180 bool &r_is_error)
181{
182 bool some_valid_domain = false;
183 if (const PointCloud *points = geometry_set.get_pointcloud()) {
184 if (domain == AttrDomain::Point) {
185 std::optional<PointCloud *> dst_points = separate_pointcloud_selection(
186 *points, selection, attribute_filter);
187 if (dst_points) {
188 geometry_set.replace_pointcloud(*dst_points);
189 }
190 some_valid_domain = true;
191 }
192 }
193 if (const Mesh *mesh = geometry_set.get_mesh()) {
194 if (ELEM(domain, AttrDomain::Point, AttrDomain::Edge, AttrDomain::Face)) {
195 std::optional<Mesh *> dst_mesh = separate_mesh_selection(
196 *mesh, selection, domain, mode, attribute_filter);
197 if (dst_mesh) {
198 if (*dst_mesh) {
199 const char *active_layer = CustomData_get_active_layer_name(&mesh->corner_data,
201 if (active_layer != nullptr) {
203 &((*dst_mesh)->corner_data), CD_PROP_FLOAT2, active_layer);
204 if (id >= 0) {
205 CustomData_set_layer_active(&((*dst_mesh)->corner_data), CD_PROP_FLOAT2, id);
206 }
207 }
208
209 const char *render_layer = CustomData_get_render_layer_name(&mesh->corner_data,
211 if (render_layer != nullptr) {
213 &((*dst_mesh)->corner_data), CD_PROP_FLOAT2, render_layer);
214 if (id >= 0) {
215 CustomData_set_layer_render(&((*dst_mesh)->corner_data), CD_PROP_FLOAT2, id);
216 }
217 }
218 }
219 geometry_set.replace_mesh(*dst_mesh);
220 }
221 some_valid_domain = true;
222 }
223 }
224 if (const Curves *src_curves_id = geometry_set.get_curves()) {
225 if (ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) {
226 const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
227 const bke::CurvesFieldContext field_context{*src_curves_id, domain};
228 std::optional<bke::CurvesGeometry> dst_curves = separate_curves_selection(
229 src_curves, field_context, selection, domain, attribute_filter);
230 if (dst_curves) {
231 if (dst_curves->is_empty()) {
232 geometry_set.remove<bke::CurveComponent>();
233 }
234 else {
235 Curves *dst_curves_id = bke::curves_new_nomain(*dst_curves);
236 bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
237 geometry_set.replace_curves(dst_curves_id);
238 }
239 }
240 some_valid_domain = true;
241 }
242 }
243 if (geometry_set.get_grease_pencil()) {
244 using namespace blender::bke::greasepencil;
245 if (domain == AttrDomain::Layer) {
246 const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
247 std::optional<GreasePencil *> dst_grease_pencil = separate_grease_pencil_layer_selection(
248 grease_pencil, selection, attribute_filter);
249 if (dst_grease_pencil) {
250 geometry_set.replace_grease_pencil(*dst_grease_pencil);
251 }
252 some_valid_domain = true;
253 }
254 else if (ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) {
255 GreasePencil &grease_pencil = *geometry_set.get_grease_pencil_for_write();
256 for (const int layer_index : grease_pencil.layers().index_range()) {
257 Drawing *drawing = grease_pencil.get_eval_drawing(grease_pencil.layer(layer_index));
258 if (drawing == nullptr) {
259 continue;
260 }
261 const bke::CurvesGeometry &src_curves = drawing->strokes();
262 const bke::GreasePencilLayerFieldContext field_context(grease_pencil, domain, layer_index);
263 std::optional<bke::CurvesGeometry> dst_curves = separate_curves_selection(
264 src_curves, field_context, selection, domain, attribute_filter);
265 if (!dst_curves) {
266 continue;
267 }
268 drawing->strokes_for_write() = std::move(*dst_curves);
269 drawing->tag_topology_changed();
270 some_valid_domain = true;
271 }
272 }
273 }
274 if (geometry_set.has_instances()) {
275 if (domain == AttrDomain::Instance) {
276 delete_selected_instances(geometry_set, selection, attribute_filter);
277 some_valid_domain = true;
278 }
279 }
280 r_is_error = !some_valid_domain && geometry_set.has_realized_data();
281}
282
283} // namespace blender::geometry
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_set_layer_render(CustomData *data, eCustomDataType type, int n)
const char * CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
void CustomData_set_layer_active(CustomData *data, eCustomDataType type, int n)
Low-level operations for grease pencil.
void BKE_grease_pencil_copy_parameters(const GreasePencil &src, GreasePencil &dst)
GreasePencil * BKE_grease_pencil_new_nomain()
void BKE_grease_pencil_copy_layer_parameters(const blender::bke::greasepencil::Layer &src, blender::bke::greasepencil::Layer &dst)
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define ELEM(...)
@ CD_PROP_FLOAT2
GeometryNodeDeleteGeometryMode
@ GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE
@ GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE
@ GEO_NODE_DELETE_GEOMETRY_MODE_ALL
AttributeSet attributes
int domain_size(const AttrDomain domain) const
AttributeAccessor attributes() const
void remove(const IndexMask &mask, const AttributeFilter &attribute_filter)
Definition instances.cc:285
int instances_num() const
Definition instances.cc:393
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void set_selection(Field< bool > selection)
Definition FN_field.hh:383
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
void foreach_index(Fn &&fn) const
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
CurvesGeometry curves_copy_point_selection(const CurvesGeometry &curves, const IndexMask &points_to_copy, const AttributeFilter &attribute_filter)
void curves_copy_parameters(const Curves &src, Curves &dst)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
Curves * curves_new_nomain(int points_num, int curves_num)
static std::optional< bke::CurvesGeometry > separate_curves_selection(const bke::CurvesGeometry &src_curves, const fn::FieldContext &field_context, const fn::Field< bool > &selection_field, const AttrDomain domain, const bke::AttributeFilter &attribute_filter)
std::optional< Mesh * > mesh_copy_selection_keep_edges(const Mesh &mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
std::optional< Mesh * > mesh_copy_selection_keep_verts(const Mesh &src_mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
static std::optional< GreasePencil * > separate_grease_pencil_layer_selection(const GreasePencil &src_grease_pencil, const fn::Field< bool > &selection_field, const bke::AttributeFilter &attribute_filter)
static void delete_selected_instances(bke::GeometrySet &geometry_set, const fn::Field< bool > &selection_field, const bke::AttributeFilter &attribute_filter)
static std::optional< Mesh * > separate_mesh_selection(const Mesh &mesh, const fn::Field< bool > &selection_field, const AttrDomain selection_domain, const GeometryNodeDeleteGeometryMode mode, const bke::AttributeFilter &attribute_filter)
void separate_geometry(bke::GeometrySet &geometry_set, bke::AttrDomain domain, GeometryNodeDeleteGeometryMode mode, const fn::Field< bool > &selection_field, const bke::AttributeFilter &attribute_filter, bool &r_is_error)
std::optional< Mesh * > mesh_copy_selection(const Mesh &src_mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
static std::optional< PointCloud * > separate_pointcloud_selection(const PointCloud &src_pointcloud, const fn::Field< bool > &selection_field, const bke::AttributeFilter &attribute_filter)
Instances * get_instances_for_write()
const GreasePencil * get_grease_pencil() const
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const Curves * get_curves() const
void replace_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const PointCloud * get_pointcloud() const
const Mesh * get_mesh() const
void remove(const GeometryComponent::Type component_type)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GreasePencil * get_grease_pencil_for_write()
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)