This converts the public `uiItemR` function to an object oriented API (`uiLayout::prop`), matching the python API. This reduces the difference between the C++ API with the python version, its also helps while converting code from python to C++ code (or vice-versa), making it almost seamless. Part of: #117604 Pull Request: https://projects.blender.org/blender/blender/pulls/138617
271 lines
7.7 KiB
C++
271 lines
7.7 KiB
C++
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup edpointcloud
|
|
* Implements the Point Cloud operators.
|
|
*/
|
|
|
|
#include "BKE_attribute.hh"
|
|
#include "BKE_context.hh"
|
|
#include "BKE_lib_id.hh"
|
|
|
|
#include "ED_pointcloud.hh"
|
|
#include "ED_screen.hh"
|
|
#include "ED_select_utils.hh"
|
|
|
|
#include "DNA_pointcloud_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "DEG_depsgraph.hh"
|
|
|
|
#include "RNA_access.hh"
|
|
#include "RNA_define.hh"
|
|
|
|
#include "UI_interface.hh"
|
|
|
|
#include "WM_api.hh"
|
|
|
|
namespace blender::ed::pointcloud {
|
|
|
|
static bool object_has_editable_pointcloud(const Main &bmain, const Object &object)
|
|
{
|
|
if (object.type != OB_POINTCLOUD) {
|
|
return false;
|
|
}
|
|
if (object.mode != OB_MODE_EDIT) {
|
|
return false;
|
|
}
|
|
if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool pointcloud_poll_impl(bContext *C,
|
|
const bool check_editable,
|
|
const bool check_edit_mode)
|
|
{
|
|
Object *object = CTX_data_active_object(C);
|
|
if (object == nullptr || object->type != OB_POINTCLOUD) {
|
|
return false;
|
|
}
|
|
if (check_editable) {
|
|
if (!ED_operator_object_active_editable_ex(C, object)) {
|
|
return false;
|
|
}
|
|
}
|
|
if (check_edit_mode) {
|
|
if ((object->mode & OB_MODE_EDIT) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool editable_pointcloud_poll(bContext *C)
|
|
{
|
|
return pointcloud_poll_impl(C, false, false);
|
|
}
|
|
|
|
bool editable_pointcloud_in_edit_mode_poll(bContext *C)
|
|
{
|
|
return pointcloud_poll_impl(C, true, true);
|
|
}
|
|
|
|
VectorSet<PointCloud *> get_unique_editable_pointclouds(const bContext &C)
|
|
{
|
|
VectorSet<PointCloud *> unique_points;
|
|
|
|
const Main &bmain = *CTX_data_main(&C);
|
|
|
|
Object *object = CTX_data_active_object(&C);
|
|
if (object && object_has_editable_pointcloud(bmain, *object)) {
|
|
unique_points.add_new(static_cast<PointCloud *>(object->data));
|
|
}
|
|
|
|
CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
|
|
if (object_has_editable_pointcloud(bmain, *object)) {
|
|
unique_points.add(static_cast<PointCloud *>(object->data));
|
|
}
|
|
}
|
|
CTX_DATA_END;
|
|
|
|
return unique_points;
|
|
}
|
|
|
|
static bool has_anything_selected(const Span<PointCloud *> pointclouds)
|
|
{
|
|
return std::any_of(pointclouds.begin(), pointclouds.end(), [](const PointCloud *pointcloud) {
|
|
return has_anything_selected(*pointcloud);
|
|
});
|
|
}
|
|
|
|
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
|
|
{
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
VectorSet<PointCloud *> unique_pointcloud = get_unique_editable_pointclouds(*C);
|
|
|
|
if (action == SEL_TOGGLE) {
|
|
action = has_anything_selected(unique_pointcloud) ? SEL_DESELECT : SEL_SELECT;
|
|
}
|
|
|
|
for (PointCloud *pointcloud : unique_pointcloud) {
|
|
/* (De)select all the curves. */
|
|
select_all(*pointcloud, action);
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
* attribute for now. */
|
|
DEG_id_tag_update(&pointcloud->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, pointcloud);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static void POINTCLOUD_OT_select_all(wmOperatorType *ot)
|
|
{
|
|
ot->name = "(De)select All";
|
|
ot->idname = "POINTCLOUD_OT_select_all";
|
|
ot->description = "(De)select all point cloud";
|
|
|
|
ot->exec = select_all_exec;
|
|
ot->poll = editable_pointcloud_poll;
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
WM_operator_properties_select_all(ot);
|
|
}
|
|
|
|
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
|
|
{
|
|
const int seed = RNA_int_get(op->ptr, "seed");
|
|
const float probability = RNA_float_get(op->ptr, "probability");
|
|
|
|
for (PointCloud *pointcloud : get_unique_editable_pointclouds(*C)) {
|
|
IndexMaskMemory memory;
|
|
const IndexMask inv_random_elements = random_mask(
|
|
pointcloud->totpoint, seed, probability, memory)
|
|
.complement(IndexRange(pointcloud->totpoint),
|
|
memory);
|
|
const bool was_anything_selected = has_anything_selected(*pointcloud);
|
|
bke::GSpanAttributeWriter selection = ensure_selection_attribute(*pointcloud, CD_PROP_BOOL);
|
|
if (!was_anything_selected) {
|
|
pointcloud::fill_selection_true(selection.span);
|
|
}
|
|
|
|
pointcloud::fill_selection_false(selection.span, inv_random_elements);
|
|
selection.finish();
|
|
|
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
|
* attribute for now. */
|
|
DEG_id_tag_update(&pointcloud->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, pointcloud);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static void select_random_ui(bContext * /*C*/, wmOperator *op)
|
|
{
|
|
uiLayout *layout = op->layout;
|
|
|
|
layout->prop(op->ptr, "seed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
|
layout->prop(op->ptr, "probability", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
|
|
}
|
|
|
|
static void POINTCLOUD_OT_select_random(wmOperatorType *ot)
|
|
{
|
|
ot->name = "Select Random";
|
|
ot->idname = __func__;
|
|
ot->description = "Randomizes existing selection or create new random selection";
|
|
|
|
ot->exec = select_random_exec;
|
|
ot->poll = editable_pointcloud_poll;
|
|
ot->ui = select_random_ui;
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_int(ot->srna,
|
|
"seed",
|
|
0,
|
|
INT32_MIN,
|
|
INT32_MAX,
|
|
"Seed",
|
|
"Source of randomness",
|
|
INT32_MIN,
|
|
INT32_MAX);
|
|
RNA_def_float(ot->srna,
|
|
"probability",
|
|
0.5f,
|
|
0.0f,
|
|
1.0f,
|
|
"Probability",
|
|
"Chance of every point being included in the selection",
|
|
0.0f,
|
|
1.0f);
|
|
}
|
|
|
|
namespace pointcloud_delete {
|
|
|
|
static wmOperatorStatus delete_exec(bContext *C, wmOperator * /*op*/)
|
|
{
|
|
for (PointCloud *pointcloud : get_unique_editable_pointclouds(*C)) {
|
|
if (remove_selection(*pointcloud)) {
|
|
DEG_id_tag_update(&pointcloud->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &pointcloud);
|
|
}
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
} // namespace pointcloud_delete
|
|
|
|
static void POINTCLOUD_OT_delete(wmOperatorType *ot)
|
|
{
|
|
ot->name = "Delete";
|
|
ot->idname = __func__;
|
|
ot->description = "Remove selected points";
|
|
|
|
ot->exec = pointcloud_delete::delete_exec;
|
|
ot->poll = editable_pointcloud_in_edit_mode_poll;
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
void operatortypes_pointcloud()
|
|
{
|
|
WM_operatortype_append(POINTCLOUD_OT_attribute_set);
|
|
WM_operatortype_append(POINTCLOUD_OT_delete);
|
|
WM_operatortype_append(POINTCLOUD_OT_duplicate);
|
|
WM_operatortype_append(POINTCLOUD_OT_select_all);
|
|
WM_operatortype_append(POINTCLOUD_OT_select_random);
|
|
WM_operatortype_append(POINTCLOUD_OT_separate);
|
|
}
|
|
|
|
void operatormacros_pointcloud()
|
|
{
|
|
wmOperatorType *ot;
|
|
wmOperatorTypeMacro *otmacro;
|
|
|
|
ot = WM_operatortype_append_macro("POINTCLOUD_OT_duplicate_move",
|
|
"Duplicate",
|
|
"Make copies of selected elements and move them",
|
|
OPTYPE_UNDO | OPTYPE_REGISTER);
|
|
WM_operatortype_macro_define(ot, "POINTCLOUD_OT_duplicate");
|
|
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
|
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
|
RNA_boolean_set(otmacro->ptr, "mirror", false);
|
|
}
|
|
|
|
void keymap_pointcloud(wmKeyConfig *keyconf)
|
|
{
|
|
/* Only set in editmode point cloud, by space_view3d listener. */
|
|
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Point Cloud", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
|
keymap->poll = editable_pointcloud_in_edit_mode_poll;
|
|
}
|
|
|
|
} // namespace blender::ed::pointcloud
|