Point Cloud: Implement separate operator
Similar to existing operators, moves the selected geometry to a new object.
This commit is contained in:
@@ -5814,6 +5814,7 @@ def km_edit_point_cloud(params):
|
||||
*_template_items_select_actions(params, "point_cloud.select_all"),
|
||||
("point_cloud.delete", {"type": 'X', "value": 'PRESS'}, None),
|
||||
("point_cloud.delete", {"type": 'DEL', "value": 'PRESS'}, None),
|
||||
("point_cloud.separate", {"type": 'P', "value": 'PRESS'}, None),
|
||||
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("mode", 'CURVE_SHRINKFATTEN')]}),
|
||||
])
|
||||
|
||||
@@ -5905,6 +5905,7 @@ class VIEW3D_MT_edit_pointcloud(Menu):
|
||||
layout.separator()
|
||||
layout.operator("point_cloud.attribute_set")
|
||||
layout.operator("point_cloud.delete")
|
||||
layout.operator("point_cloud.separate")
|
||||
layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label)
|
||||
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ IndexMask retrieve_selected_points(const PointCloud &pointcloud, IndexMaskMemory
|
||||
* \returns true if any point was removed.
|
||||
*/
|
||||
bool remove_selection(PointCloud &point_cloud);
|
||||
PointCloud *copy_selection(const PointCloud &src, const IndexMask &mask);
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -140,6 +141,7 @@ bool editable_point_cloud_in_edit_mode_poll(bContext *C);
|
||||
|
||||
void POINT_CLOUD_OT_attribute_set(wmOperatorType *ot);
|
||||
void POINT_CLOUD_OT_duplicate(wmOperatorType *ot);
|
||||
void POINT_CLOUD_OT_separate(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ set(SRC
|
||||
intern/point_cloud_ops.cc
|
||||
intern/point_cloud_selection.cc
|
||||
intern/point_cloud_undo.cc
|
||||
intern/separate.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
@@ -67,8 +67,6 @@ void POINT_CLOUD_OT_duplicate(wmOperatorType *ot)
|
||||
ot->poll = editable_point_cloud_in_edit_mode_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::point_cloud
|
||||
|
||||
@@ -9,31 +9,40 @@
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "ED_point_cloud.hh"
|
||||
|
||||
namespace blender::ed::point_cloud {
|
||||
|
||||
PointCloud *copy_selection(const PointCloud &src, const IndexMask &mask)
|
||||
{
|
||||
if (mask.size() == src.totpoint) {
|
||||
return BKE_pointcloud_copy_for_eval(&src);
|
||||
}
|
||||
PointCloud *dst = BKE_pointcloud_new_nomain(mask.size());
|
||||
bke::gather_attributes(src.attributes(),
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
{},
|
||||
mask,
|
||||
dst->attributes_for_write());
|
||||
pointcloud_copy_parameters(src, *dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
bool remove_selection(PointCloud &point_cloud)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = point_cloud.attributes();
|
||||
const VArray<bool> selection = *attributes.lookup_or_default<bool>(
|
||||
".selection", bke::AttrDomain::Point, true);
|
||||
const int domain_size_orig = point_cloud.totpoint;
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_bools_inverse(selection, memory);
|
||||
if (mask.size() == point_cloud.totpoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PointCloud *point_cloud_new = BKE_pointcloud_new_nomain(mask.size());
|
||||
bke::gather_attributes(attributes,
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
{},
|
||||
mask,
|
||||
point_cloud_new->attributes_for_write());
|
||||
pointcloud_copy_parameters(point_cloud, *point_cloud_new);
|
||||
PointCloud *point_cloud_new = copy_selection(point_cloud, mask);
|
||||
BKE_pointcloud_nomain_to_pointcloud(point_cloud_new, &point_cloud);
|
||||
return point_cloud.totpoint != domain_size_orig;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::point_cloud
|
||||
|
||||
@@ -174,6 +174,7 @@ void operatortypes_point_cloud()
|
||||
WM_operatortype_append(POINT_CLOUD_OT_delete);
|
||||
WM_operatortype_append(POINT_CLOUD_OT_duplicate);
|
||||
WM_operatortype_append(POINT_CLOUD_OT_select_all);
|
||||
WM_operatortype_append(POINT_CLOUD_OT_separate);
|
||||
}
|
||||
|
||||
void operatormacros_point_cloud()
|
||||
|
||||
112
source/blender/editors/point_cloud/intern/separate.cc
Normal file
112
source/blender/editors/point_cloud/intern/separate.cc
Normal file
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_point_cloud.hh"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
namespace blender::ed::point_cloud {
|
||||
|
||||
static int separate_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(
|
||||
scene, view_layer, CTX_wm_view3d(C));
|
||||
|
||||
VectorSet<PointCloud *> src_pointclouds;
|
||||
for (Base *base_src : bases) {
|
||||
src_pointclouds.add(static_cast<PointCloud *>(base_src->object->data));
|
||||
}
|
||||
|
||||
/* Modify new point clouds and generate new point clouds in parallel. */
|
||||
Array<PointCloud *> dst_pointclouds(src_pointclouds.size());
|
||||
threading::parallel_for(dst_pointclouds.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask selection = retrieve_selected_points(*src_pointclouds[i], memory);
|
||||
if (selection.is_empty()) {
|
||||
dst_pointclouds[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
dst_pointclouds[i] = copy_selection(*src_pointclouds[i], selection);
|
||||
const IndexMask inverse = selection.complement(IndexRange(src_pointclouds[i]->totpoint),
|
||||
memory);
|
||||
BKE_pointcloud_nomain_to_pointcloud(copy_selection(*src_pointclouds[i], inverse),
|
||||
src_pointclouds[i]);
|
||||
}
|
||||
});
|
||||
|
||||
/* Move new point clouds into main data-base. */
|
||||
for (const int i : dst_pointclouds.index_range()) {
|
||||
if (PointCloud *dst = dst_pointclouds[i]) {
|
||||
dst_pointclouds[i] = BKE_pointcloud_add(bmain, BKE_id_name(src_pointclouds[i]->id));
|
||||
pointcloud_copy_parameters(*src_pointclouds[i], *dst_pointclouds[i]);
|
||||
BKE_pointcloud_nomain_to_pointcloud(dst, dst_pointclouds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip processing objects with no selected elements. */
|
||||
bases.remove_if([&](Base *base) {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(base->object->data);
|
||||
return dst_pointclouds[src_pointclouds.index_of(pointcloud)] == nullptr;
|
||||
});
|
||||
|
||||
if (bases.is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Add new objects for the new point clouds. */
|
||||
for (Base *base_src : bases) {
|
||||
PointCloud *src = static_cast<PointCloud *>(base_src->object->data);
|
||||
PointCloud *dst = dst_pointclouds[src_pointclouds.index_of(src)];
|
||||
|
||||
Base *base_dst = object::add_duplicate(
|
||||
bmain, scene, view_layer, base_src, eDupli_ID_Flags(U.dupflag) & USER_DUP_ACT);
|
||||
Object *object_dst = base_dst->object;
|
||||
object_dst->mode = OB_MODE_OBJECT;
|
||||
object_dst->data = dst;
|
||||
|
||||
DEG_id_tag_update(&src->id, ID_RECALC_GEOMETRY);
|
||||
DEG_id_tag_update(&dst->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, base_src->object);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, object_dst);
|
||||
}
|
||||
|
||||
DEG_relations_tag_update(bmain);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POINT_CLOUD_OT_separate(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Separate";
|
||||
ot->idname = "POINT_CLOUD_OT_separate";
|
||||
ot->description = "Separate selected geometry into a new point cloud";
|
||||
|
||||
ot->exec = separate_exec;
|
||||
ot->poll = editable_point_cloud_in_edit_mode_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::point_cloud
|
||||
Reference in New Issue
Block a user