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:
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user