Blender V4.3
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 else 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_point_cloud_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 GreasePencil *dst_grease_pencil = BKE_grease_pencil_new_nomain();
145 BKE_grease_pencil_duplicate_drawing_array(&src_grease_pencil, dst_grease_pencil);
146 BKE_grease_pencil_copy_parameters(src_grease_pencil, *dst_grease_pencil);
147 selection.foreach_index([&](const int index) {
148 const bke::greasepencil::Layer &src_layer = src_grease_pencil.layer(index);
149 dst_grease_pencil->duplicate_layer(src_layer);
150 });
151 dst_grease_pencil->remove_drawings_with_no_users();
152
153 bke::gather_attributes(src_grease_pencil.attributes(),
154 AttrDomain::Layer,
155 AttrDomain::Layer,
156 attribute_filter,
157 selection,
158 dst_grease_pencil->attributes_for_write());
159
160 return dst_grease_pencil;
161}
162
164 const AttrDomain domain,
166 const fn::Field<bool> &selection,
167 const bke::AttributeFilter &attribute_filter,
168 bool &r_is_error)
169{
170 bool some_valid_domain = false;
171 if (const PointCloud *points = geometry_set.get_pointcloud()) {
172 if (domain == AttrDomain::Point) {
173 std::optional<PointCloud *> dst_points = separate_point_cloud_selection(
174 *points, selection, attribute_filter);
175 if (dst_points) {
176 geometry_set.replace_pointcloud(*dst_points);
177 }
178 some_valid_domain = true;
179 }
180 }
181 if (const Mesh *mesh = geometry_set.get_mesh()) {
182 if (ELEM(domain, AttrDomain::Point, AttrDomain::Edge, AttrDomain::Face)) {
183 std::optional<Mesh *> dst_mesh = separate_mesh_selection(
184 *mesh, selection, domain, mode, attribute_filter);
185 if (dst_mesh) {
186 if (*dst_mesh) {
187 const char *active_layer = CustomData_get_active_layer_name(&mesh->corner_data,
189 if (active_layer != nullptr) {
191 &((*dst_mesh)->corner_data), CD_PROP_FLOAT2, active_layer);
192 if (id >= 0) {
193 CustomData_set_layer_active(&((*dst_mesh)->corner_data), CD_PROP_FLOAT2, id);
194 }
195 }
196
197 const char *render_layer = CustomData_get_render_layer_name(&mesh->corner_data,
199 if (render_layer != nullptr) {
201 &((*dst_mesh)->corner_data), CD_PROP_FLOAT2, render_layer);
202 if (id >= 0) {
203 CustomData_set_layer_render(&((*dst_mesh)->corner_data), CD_PROP_FLOAT2, id);
204 }
205 }
206 }
207 geometry_set.replace_mesh(*dst_mesh);
208 }
209 some_valid_domain = true;
210 }
211 }
212 if (const Curves *src_curves_id = geometry_set.get_curves()) {
213 if (ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) {
214 const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
215 const bke::CurvesFieldContext field_context{*src_curves_id, domain};
216 std::optional<bke::CurvesGeometry> dst_curves = separate_curves_selection(
217 src_curves, field_context, selection, domain, attribute_filter);
218 if (dst_curves) {
219 if (dst_curves->points_num() == 0) {
220 geometry_set.remove<bke::CurveComponent>();
221 }
222 else {
223 Curves *dst_curves_id = bke::curves_new_nomain(*dst_curves);
224 bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
225 geometry_set.replace_curves(dst_curves_id);
226 }
227 }
228 some_valid_domain = true;
229 }
230 }
231 if (geometry_set.get_grease_pencil()) {
232 using namespace blender::bke::greasepencil;
233 if (domain == AttrDomain::Layer) {
234 const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
235 std::optional<GreasePencil *> dst_grease_pencil = separate_grease_pencil_layer_selection(
236 grease_pencil, selection, attribute_filter);
237 if (dst_grease_pencil) {
238 geometry_set.replace_grease_pencil(*dst_grease_pencil);
239 }
240 some_valid_domain = true;
241 }
242 else if (ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) {
243 GreasePencil &grease_pencil = *geometry_set.get_grease_pencil_for_write();
244 for (const int layer_index : grease_pencil.layers().index_range()) {
245 Drawing *drawing = grease_pencil.get_eval_drawing(grease_pencil.layer(layer_index));
246 if (drawing == nullptr) {
247 continue;
248 }
249 const bke::CurvesGeometry &src_curves = drawing->strokes();
250 const bke::GreasePencilLayerFieldContext field_context(grease_pencil, domain, layer_index);
251 std::optional<bke::CurvesGeometry> dst_curves = separate_curves_selection(
252 src_curves, field_context, selection, domain, attribute_filter);
253 if (!dst_curves) {
254 continue;
255 }
256 drawing->strokes_for_write() = std::move(*dst_curves);
257 drawing->tag_topology_changed();
258 some_valid_domain = true;
259 }
260 }
261 }
262 if (geometry_set.has_instances()) {
263 if (domain == AttrDomain::Instance) {
264 delete_selected_instances(geometry_set, selection, attribute_filter);
265 some_valid_domain = true;
266 }
267 }
268 r_is_error = !some_valid_domain && geometry_set.has_realized_data();
269}
270
271} // 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)
void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil_src, GreasePencil *grease_pencil_dst)
GreasePencil * BKE_grease_pencil_new_nomain()
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#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
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void set_selection(Field< bool > selection)
Definition FN_field.hh:385
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:822
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:450
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< PointCloud * > separate_point_cloud_selection(const PointCloud &src_pointcloud, const fn::Field< bool > &selection_field, const bke::AttributeFilter &attribute_filter)
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={})
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)