Curves: Reimplement random selection with mask

Instead of manipulating the selection attribute directly, we get an `IndexMask` of the random elements, and then apply the changes to the selection attribute in the operator.

Pull Request: https://projects.blender.org/blender/blender/pulls/109366
This commit is contained in:
Falk David
2023-07-05 13:48:22 +02:00
committed by Falk David
parent d55cdb1b7a
commit 4d3574a4a5
5 changed files with 91 additions and 67 deletions

View File

@@ -6,6 +6,8 @@
* \ingroup edcurves
*/
#include "BLI_rand.hh"
#include "BKE_curves.hh"
#include "ED_curves.h"
@@ -32,4 +34,23 @@ IndexMask end_points(const bke::CurvesGeometry &curves,
return IndexMask::from_bools(end_points, memory);
}
IndexMask random_mask(const bke::CurvesGeometry &curves,
const eAttrDomain 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);
for (const int i : IndexRange(domain_size)) {
random[i] = next_bool_random_value();
}
return IndexMask::from_bools(random, memory);
}
} // namespace blender::ed::curves

View File

@@ -890,7 +890,20 @@ static int select_random_exec(bContext *C, wmOperator *op)
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_random(curves, eAttrDomain(curves_id->selection_domain), uint32_t(seed), probability);
const eAttrDomain selection_domain = eAttrDomain(curves_id->selection_domain);
IndexMaskMemory memory;
const IndexMask random_elements = random_mask(curves, selection_domain, seed, probability, memory);
const bool was_anything_selected = has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
curves::fill_selection_false(selection.span, random_elements);
selection.finish();
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */

View File

@@ -126,6 +126,26 @@ void fill_selection_true(GMutableSpan selection)
}
}
void fill_selection_false(GMutableSpan selection, const IndexMask &mask)
{
if (selection.type().is<bool>()) {
index_mask::masked_fill(selection.typed<bool>(), false, mask);
}
else if (selection.type().is<float>()) {
index_mask::masked_fill(selection.typed<float>(), 0.0f, mask);
}
}
void fill_selection_true(GMutableSpan selection, const IndexMask &mask)
{
if (selection.type().is<bool>()) {
index_mask::masked_fill(selection.typed<bool>(), true, mask);
}
else if (selection.type().is<float>()) {
index_mask::masked_fill(selection.typed<float>(), 1.0f, mask);
}
}
static bool contains(const VArray<bool> &varray, const IndexRange range_to_check, const bool value)
{
const CommonVArrayInfo info = varray.common_info();
@@ -369,55 +389,6 @@ void select_adjacent(bke::CurvesGeometry &curves, const bool deselect)
selection.finish();
}
void select_random(bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
uint32_t random_seed,
float probability)
{
RandomNumberGenerator rng{random_seed};
const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
const bool was_anything_selected = has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
selection.span.type().to_static_type_tag<bool, float>([&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_void_v<T>) {
BLI_assert_unreachable();
}
else {
MutableSpan<T> selection_typed = selection.span.typed<T>();
switch (selection_domain) {
case ATTR_DOMAIN_POINT: {
for (const int point_i : selection_typed.index_range()) {
const bool random_value = next_bool_random_value();
if (!random_value) {
selection_typed[point_i] = T(0);
}
}
break;
}
case ATTR_DOMAIN_CURVE: {
for (const int curve_i : curves.curves_range()) {
const bool random_value = next_bool_random_value();
if (!random_value) {
selection_typed[curve_i] = T(0);
}
}
break;
}
default:
BLI_assert_unreachable();
}
}
});
selection.finish();
}
void apply_selection_operation_at_index(GMutableSpan selection,
const int index,
const eSelectOp sel_op)

View File

@@ -158,6 +158,7 @@ static void GREASE_PENCIL_OT_select_linked(wmOperatorType *ot)
static int select_random_exec(bContext *C, wmOperator *op)
{
using namespace blender;
const float ratio = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
Scene *scene = CTX_data_scene(C);
@@ -166,11 +167,26 @@ static int select_random_exec(bContext *C, wmOperator *op)
eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(C);
grease_pencil.foreach_editable_drawing(
scene->r.cfra, [&](int drawing_index, blender::bke::greasepencil::Drawing &drawing) {
blender::ed::curves::select_random(drawing.strokes_for_write(),
selection_domain,
blender::get_default_hash_2<int>(seed, drawing_index),
ratio);
scene->r.cfra, [&](int drawing_index, bke::greasepencil::Drawing &drawing) {
bke::CurvesGeometry &curves = drawing.strokes_for_write();
IndexMaskMemory memory;
const IndexMask random_elements = ed::curves::random_mask(
curves,
selection_domain,
blender::get_default_hash_2<int>(seed, drawing_index),
ratio,
memory);
const bool was_anything_selected = ed::curves::has_anything_selected(curves);
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
curves::fill_selection_false(selection.span, random_elements);
selection.finish();
});
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic

View File

@@ -105,6 +105,19 @@ IndexMask end_points(const bke::CurvesGeometry &curves,
bool inverted,
IndexMaskMemory &memory);
/**
* Return a mask of random points or curves.
*
* \param random_seed: The seed for the \a RandomNumberGenerator.
* \param probability: Determins 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,
eAttrDomain selection_domain,
uint32_t random_seed,
float probability,
IndexMaskMemory &memory);
/** \} */
/* -------------------------------------------------------------------- */
@@ -122,6 +135,8 @@ IndexMask end_points(const bke::CurvesGeometry &curves,
void fill_selection_false(GMutableSpan span);
void fill_selection_true(GMutableSpan span);
void fill_selection_false(GMutableSpan selection, const IndexMask &mask);
void fill_selection_true(GMutableSpan selection, const IndexMask &mask);
/**
* Return true if any element is selected, on either domain with either type.
@@ -180,18 +195,6 @@ void select_alternate(bke::CurvesGeometry &curves, const bool deselect_ends);
*/
void select_adjacent(bke::CurvesGeometry &curves, bool deselect);
/**
* Select random points or curves.
*
* \param random_seed: The seed for the \a RandomNumberGenerator.
* \param probability: Determins how likely a point/curve will be selected. If set to 0.0, nothing
* will be selected, if set to 1.0 everything will be selected.
*/
void select_random(bke::CurvesGeometry &curves,
eAttrDomain selection_domain,
uint32_t random_seed,
float probability);
/**
* Helper struct for `closest_elem_find_screen_space`.
*/