Point Cloud: Select Random
Move the mask_random function to BLI_index_mask.hh, so it can be shared between curves, grease pencil and point cloud. Copied/inspired by the curves select code. Pull Request: https://projects.blender.org/blender/blender/pulls/134624
This commit is contained in:
@@ -2325,6 +2325,10 @@ class VIEW3D_MT_select_edit_pointcloud(Menu):
|
||||
layout.operator("pointcloud.select_all", text="None").action = 'DESELECT'
|
||||
layout.operator("pointcloud.select_all", text="Invert").action = 'INVERT'
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("pointcloud.select_random")
|
||||
|
||||
layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label)
|
||||
|
||||
|
||||
|
||||
@@ -1112,6 +1112,26 @@ inline void index_range_to_mask_segments(const IndexRange range,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mask of random points or curves.
|
||||
*
|
||||
* \param mask: (optional) The elements that should be used in the resulting mask.
|
||||
* \param universe_size: The size of the mask.
|
||||
* \param random_seed: The seed for the \a RandomNumberGenerator.
|
||||
* \param probability: Determines how likely a point/curve will be chosen.
|
||||
* If set to 0.0, nothing will be in the mask, if set to 1.0 everything will be in the mask.
|
||||
*/
|
||||
IndexMask random_mask(const IndexMask &mask,
|
||||
const int64_t universe_size,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
IndexMask random_mask(const int64_t universe_size,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
} // namespace blender::index_mask
|
||||
|
||||
namespace blender {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "BLI_index_mask_expression.hh"
|
||||
#include "BLI_index_ranges_builder.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_rand.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_sort.hh"
|
||||
#include "BLI_task.hh"
|
||||
@@ -1227,4 +1228,29 @@ template IndexMask IndexMask::from_ranges(OffsetIndices<int64_t>,
|
||||
const IndexMask &,
|
||||
IndexMaskMemory &);
|
||||
|
||||
IndexMask random_mask(const IndexMask &mask,
|
||||
const int64_t universe_size,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
RandomNumberGenerator rng{random_seed};
|
||||
const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
|
||||
|
||||
Array<bool> random(universe_size, false);
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { random[i] = next_bool_random_value(); });
|
||||
|
||||
return IndexMask::from_bools(IndexRange(universe_size), random, memory);
|
||||
}
|
||||
|
||||
IndexMask random_mask(const int64_t universe_size,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
const IndexRange selection(universe_size);
|
||||
return random_mask(selection, universe_size, random_seed, probability, memory);
|
||||
}
|
||||
|
||||
} // namespace blender::index_mask
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_rand.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_curves.hh"
|
||||
@@ -65,33 +64,4 @@ IndexMask end_points(const bke::CurvesGeometry &curves,
|
||||
return end_points(curves, curves.curves_range(), amount_start, amount_end, inverted, memory);
|
||||
}
|
||||
|
||||
IndexMask random_mask(const bke::CurvesGeometry &curves,
|
||||
const IndexMask &mask,
|
||||
const bke::AttrDomain selection_domain,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
RandomNumberGenerator rng{random_seed};
|
||||
const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
|
||||
|
||||
const int64_t domain_size = curves.attributes().domain_size(selection_domain);
|
||||
|
||||
Array<bool> random(domain_size, false);
|
||||
mask.foreach_index_optimized<int64_t>(
|
||||
[&](const int64_t i) { random[i] = next_bool_random_value(); });
|
||||
|
||||
return IndexMask::from_bools(IndexRange(domain_size), random, memory);
|
||||
}
|
||||
|
||||
IndexMask random_mask(const bke::CurvesGeometry &curves,
|
||||
const bke::AttrDomain selection_domain,
|
||||
const uint32_t random_seed,
|
||||
const float probability,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
const IndexRange selection(curves.attributes().domain_size(selection_domain));
|
||||
return random_mask(curves, selection, selection_domain, random_seed, probability, memory);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
|
||||
@@ -914,8 +914,7 @@ static int select_random_exec(bContext *C, wmOperator *op)
|
||||
const int domain_size = curves.attributes().domain_size(selection_domain);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask inv_random_elements = random_mask(
|
||||
curves, selection_domain, seed, probability, memory)
|
||||
const IndexMask inv_random_elements = random_mask(domain_size, seed, probability, memory)
|
||||
.complement(IndexRange(domain_size), memory);
|
||||
|
||||
const bool was_anything_selected = has_anything_selected(curves);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
@@ -501,12 +502,11 @@ static int select_random_exec(bContext *C, wmOperator *op)
|
||||
if (selectable_elements.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
return ed::curves::random_mask(info.drawing.strokes(),
|
||||
selectable_elements,
|
||||
selection_domain,
|
||||
blender::get_default_hash<int>(seed, info.layer_index),
|
||||
ratio,
|
||||
memory);
|
||||
return random_mask(selectable_elements,
|
||||
info.drawing.strokes().points_num(),
|
||||
blender::get_default_hash<int>(seed, info.layer_index),
|
||||
ratio,
|
||||
memory);
|
||||
});
|
||||
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
||||
|
||||
@@ -176,26 +176,6 @@ IndexMask end_points(const bke::CurvesGeometry &curves,
|
||||
bool inverted,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
/**
|
||||
* Return a mask of random points or curves.
|
||||
*
|
||||
* \param mask: (optional) The elements that should be used in the resulting mask. This mask should
|
||||
* be in the same domain as the \a selection_domain. \param random_seed: The seed for the \a
|
||||
* RandomNumberGenerator. \param probability: Determines how likely a point/curve will be chosen.
|
||||
* If set to 0.0, nothing will be in the mask, if set to 1.0 everything will be in the mask.
|
||||
*/
|
||||
IndexMask random_mask(const bke::CurvesGeometry &curves,
|
||||
bke::AttrDomain selection_domain,
|
||||
uint32_t random_seed,
|
||||
float probability,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask random_mask(const bke::CurvesGeometry &curves,
|
||||
const IndexMask &mask,
|
||||
bke::AttrDomain selection_domain,
|
||||
uint32_t random_seed,
|
||||
float probability,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -56,6 +56,7 @@ VectorSet<PointCloud *> get_unique_editable_pointclouds(const bContext &C);
|
||||
* helpful utilities on top of that.
|
||||
* \{ */
|
||||
|
||||
void fill_selection_true(GMutableSpan span);
|
||||
void fill_selection_false(GMutableSpan selection, const IndexMask &mask);
|
||||
void fill_selection_true(GMutableSpan selection, const IndexMask &mask);
|
||||
|
||||
|
||||
@@ -139,6 +139,74 @@ static void POINTCLOUD_OT_select_all(wmOperatorType *ot)
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
static int 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;
|
||||
|
||||
uiItemR(layout, op->ptr, "seed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
uiItemR(layout, 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 int delete_exec(bContext *C, wmOperator * /*op*/)
|
||||
@@ -173,6 +241,7 @@ void operatortypes_pointcloud()
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,11 @@ void fill_selection_false(GMutableSpan selection, const IndexMask &mask)
|
||||
}
|
||||
}
|
||||
|
||||
void fill_selection_true(GMutableSpan selection)
|
||||
{
|
||||
fill_selection_true(selection, IndexMask(selection.size()));
|
||||
}
|
||||
|
||||
void fill_selection_true(GMutableSpan selection, const IndexMask &mask)
|
||||
{
|
||||
if (selection.type().is<bool>()) {
|
||||
|
||||
Reference in New Issue
Block a user