Curves: Support boolean attribute selection type, simplifications

Use the same `".selection"` attribute for both curve and point domains,
instead of a different name for each. The attribute can now have
either boolean or float type. Some tools create boolean selections.
Other tools create float selections. Some tools "upgrade" the attribute
from boolean to float.

Edit mode tools that create selections from scratch can create boolean
selections, but edit mode should generally be able to handle both
selection types. Sculpt mode should be able to read boolean selections,
but can also and write float values between zero and one.

Theoretically we could just always use floats to store selections,
but the type-agnosticism doesn't cost too much complexity given the
existing APIs for dealing with it, and being able to use booleans is
clearer in edit mode, and may allow future optimizations like more
efficient ways to store boolean attributes.

The attribute API is usually used directly for accessing the selection
attribute. We rely on implicit type conversion and domain interpolation
to simplify the rest of the code.

Differential Revision: https://developer.blender.org/D16057
This commit is contained in:
Hans Goudey 2023-01-03 22:59:25 -05:00
parent 80639a93a8
commit c26616b2c1
30 changed files with 441 additions and 399 deletions

View File

@ -287,11 +287,6 @@ class CurvesGeometry : public ::CurvesGeometry {
Span<float2> surface_uv_coords() const;
MutableSpan<float2> surface_uv_coords_for_write();
VArray<float> selection_point_float() const;
MutableSpan<float> selection_point_float_for_write();
VArray<float> selection_curve_float() const;
MutableSpan<float> selection_curve_float_for_write();
/**
* Calculate the largest and smallest position values, only including control points
* (rather than evaluated points). The existing values of `min` and `max` are taken into account.

View File

@ -38,8 +38,6 @@ static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right";
static const std::string ATTR_NURBS_ORDER = "nurbs_order";
static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
static const std::string ATTR_SELECTION_POINT_FLOAT = ".selection_point_float";
static const std::string ATTR_SELECTION_CURVE_FLOAT = ".selection_curve_float";
static const std::string ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
/* -------------------------------------------------------------------- */
@ -433,26 +431,6 @@ MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
}
VArray<float> CurvesGeometry::selection_point_float() const
{
return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
}
MutableSpan<float> CurvesGeometry::selection_point_float_for_write()
{
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
}
VArray<float> CurvesGeometry::selection_curve_float() const
{
return get_varray_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
}
MutableSpan<float> CurvesGeometry::selection_curve_float_for_write()
{
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -112,4 +112,6 @@ inline void gather(const VArray<T> &src,
});
}
void invert_booleans(MutableSpan<bool> span);
} // namespace blender::array_utils

View File

@ -33,4 +33,13 @@ void gather(const GSpan src, const IndexMask indices, GMutableSpan dst, const in
gather(GVArray::ForSpan(src), indices, dst, grain_size);
}
void invert_booleans(MutableSpan<bool> span)
{
threading::parallel_for(span.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
span[i] = !span[i];
}
});
}
} // namespace blender::array_utils

View File

@ -50,6 +50,7 @@
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_curve.h"
#include "BKE_curves.hh"
#include "BKE_data_transfer.h"
#include "BKE_deform.h"
#include "BKE_fcurve.h"
@ -3850,5 +3851,9 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) {
curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
}
LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) {
BKE_id_attribute_rename(&curves_id->id, ".selection_point_float", ".selection", nullptr);
BKE_id_attribute_rename(&curves_id->id, ".selection_curve_float", ".selection", nullptr);
}
}
}

View File

@ -10,6 +10,7 @@
#include "draw_cache_impl.h"
#include "overlay_private.hh"
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata)
@ -31,18 +32,11 @@ void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata)
static bool everything_selected(const Curves &curves_id)
{
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
curves_id.geometry);
blender::VArray<float> selection;
switch (curves_id.selection_domain) {
case ATTR_DOMAIN_POINT:
selection = curves.selection_point_float();
break;
case ATTR_DOMAIN_CURVE:
selection = curves.selection_curve_float();
break;
}
return selection.is_single() && selection.get_internal_single() == 1.0f;
using namespace blender;
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
const VArray<bool> selection = curves.attributes().lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
return selection.is_single() && selection.get_internal_single();
}
void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *object)
@ -56,12 +50,9 @@ void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *object)
}
/* Retrieve the location of the texture. */
const char *name = curves->selection_domain == ATTR_DOMAIN_POINT ? ".selection_point_float" :
".selection_curve_float";
bool is_point_domain;
GPUVertBuf **texture = DRW_curves_texture_for_evaluated_attribute(
curves, name, &is_point_domain);
curves, ".selection", &is_point_domain);
if (texture == nullptr) {
return;
}

View File

@ -11,6 +11,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_devirtualize_parameters.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vec_types.hh"
@ -334,17 +335,16 @@ static void curves_batch_cache_ensure_edit_points_data(const Curves &curves_id,
GPU_vertbuf_init_with_format(cache.edit_points_data, &format_data);
GPU_vertbuf_data_alloc(cache.edit_points_data, curves.points_num());
VArray<float> selection;
const VArray<bool> selection = curves.attributes().lookup_or_default<bool>(
".selection", eAttrDomain(curves_id.selection_domain), true);
switch (curves_id.selection_domain) {
case ATTR_DOMAIN_POINT:
selection = curves.selection_point_float();
for (const int point_i : selection.index_range()) {
const float point_selection = (selection[point_i] > 0.0f) ? 1.0f : 0.0f;
GPU_vertbuf_attr_set(cache.edit_points_data, color, point_i, &point_selection);
}
break;
case ATTR_DOMAIN_CURVE:
selection = curves.selection_curve_float();
for (const int curve_i : curves.curves_range()) {
const float curve_selection = (selection[curve_i] > 0.0f) ? 1.0f : 0.0f;
const IndexRange points = curves.points_for_curve(curve_i);

View File

@ -22,6 +22,7 @@ set(INC
set(SRC
intern/curves_add.cc
intern/curves_ops.cc
intern/curves_selection.cc
)
set(LIB

View File

@ -6,7 +6,9 @@
#include <atomic>
#include "BLI_array_utils.hh"
#include "BLI_devirtualize_parameters.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_utildefines.h"
#include "BLI_vector_set.hh"
@ -748,7 +750,6 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
continue;
}
const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain);
curves_id->selection_domain = domain;
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
@ -756,18 +757,21 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
if (curves.points_num() == 0) {
continue;
}
if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
VArray<float> curve_selection = curves.adapt_domain(
curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
curve_selection.materialize(curves.selection_curve_float_for_write());
attributes.remove(".selection_point_float");
const GVArray src = attributes.lookup(".selection", domain);
if (src.is_empty()) {
continue;
}
else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
VArray<float> point_selection = curves.adapt_domain(
curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
point_selection.materialize(curves.selection_point_float_for_write());
attributes.remove(".selection_curve_float");
const CPPType &type = src.type();
void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
src.materialize(dst);
attributes.remove(".selection");
if (!attributes.add(".selection",
domain,
bke::cpp_type_to_custom_data_type(type),
bke::AttributeInitMoveArray(dst))) {
MEM_freeN(dst);
}
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
@ -801,46 +805,54 @@ static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
}
static bool varray_contains_nonzero(const VArray<float> &data)
static bool contains(const VArray<bool> &varray, const bool value)
{
bool contains_nonzero = false;
devirtualize_varray(data, [&](const auto array) {
for (const int i : data.index_range()) {
if (array[i] != 0.0f) {
contains_nonzero = true;
break;
}
}
});
return contains_nonzero;
const CommonVArrayInfo info = varray.common_info();
if (info.type == CommonVArrayInfo::Type::Single) {
return *static_cast<const bool *>(info.data) == value;
}
if (info.type == CommonVArrayInfo::Type::Span) {
const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
return threading::parallel_reduce(
span.index_range(),
4096,
false,
[&](const IndexRange range, const bool init) {
return init || span.slice(range).contains(value);
},
[&](const bool a, const bool b) { return a || b; });
}
return threading::parallel_reduce(
varray.index_range(),
2048,
false,
[&](const IndexRange range, const bool init) {
if (init) {
return init;
}
/* Alternatively, this could use #materialize to retrieve many values at once. */
for (const int64_t i : range) {
if (varray[i] == value) {
return true;
}
}
return false;
},
[&](const bool a, const bool b) { return a || b; });
}
bool has_anything_selected(const Curves &curves_id)
{
const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
switch (curves_id.selection_domain) {
case ATTR_DOMAIN_POINT:
return varray_contains_nonzero(curves.selection_point_float());
case ATTR_DOMAIN_CURVE:
return varray_contains_nonzero(curves.selection_curve_float());
}
BLI_assert_unreachable();
return false;
const VArray<bool> selection = curves.attributes().lookup<bool>(".selection");
return !selection || contains(selection, true);
}
static bool any_point_selected(const CurvesGeometry &curves)
static bool has_anything_selected(const Span<Curves *> curves_ids)
{
return varray_contains_nonzero(curves.selection_point_float());
}
static bool any_point_selected(const Span<Curves *> curves_ids)
{
for (const Curves *curves_id : curves_ids) {
if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) {
return true;
}
}
return false;
return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) {
return has_anything_selected(*curves_id);
});
}
namespace select_all {
@ -854,6 +866,16 @@ static void invert_selection(MutableSpan<float> selection)
});
}
static void invert_selection(GMutableSpan selection)
{
if (selection.type().is<bool>()) {
array_utils::invert_booleans(selection.typed<bool>());
}
else if (selection.type().is<float>()) {
invert_selection(selection.typed<float>());
}
}
static int select_all_exec(bContext *C, wmOperator *op)
{
int action = RNA_enum_get(op->ptr, "action");
@ -861,27 +883,34 @@ static int select_all_exec(bContext *C, wmOperator *op)
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
if (action == SEL_TOGGLE) {
action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
}
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
if (action == SEL_SELECT) {
/* As an optimization, just remove the selection attributes when everything is selected. */
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
attributes.remove(".selection_point_float");
attributes.remove(".selection_curve_float");
attributes.remove(".selection");
}
else if (!attributes.contains(".selection")) {
BLI_assert(ELEM(action, SEL_INVERT, SEL_DESELECT));
/* If the attribute doesn't exist and it's either deleted or inverted, create
* it with nothing selected, since that means everything was selected before. */
attributes.add(".selection",
eAttrDomain(curves_id->selection_domain),
CD_PROP_BOOL,
bke::AttributeInitDefaultValue());
}
else {
MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
curves.selection_point_float_for_write() :
curves.selection_curve_float_for_write();
bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection");
if (action == SEL_DESELECT) {
selection.fill(0.0f);
fill_selection_false(selection.span);
}
else if (action == SEL_INVERT) {
invert_selection(selection);
invert_selection(selection.span);
}
selection.finish();
}
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic

View File

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edcurves
*/
#include "BLI_index_mask_ops.hh"
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "ED_curves.h"
#include "ED_object.h"
namespace blender::ed::curves {
static IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves,
Vector<int64_t> &r_indices)
{
const IndexRange curves_range = curves.curves_range();
const bke::AttributeAccessor attributes = curves.attributes();
/* Interpolate from points to curves manually as a performance improvement, since we are only
* interested in whether any point in each curve is selected. Retrieve meta data since
* #lookup_or_default from the attribute API doesn't give the domain of the attribute. */
std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(".selection");
if (meta_data && meta_data->domain == ATTR_DOMAIN_POINT) {
/* Avoid the interpolation from interpolating the attribute to the
* curve domain by retrieving the point domain values directly. */
const VArray<bool> selection = attributes.lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
if (selection.is_single()) {
return selection.get_internal_single() ? IndexMask(curves_range) : IndexMask();
}
return index_mask_ops::find_indices_based_on_predicate(
curves_range, 512, r_indices, [&](const int64_t curve_i) {
const IndexRange points = curves.points_for_curve(curve_i);
/* The curve is selected if any of its points are selected. */
Array<bool, 32> point_selection(points.size());
selection.materialize_compressed(points, point_selection);
return point_selection.as_span().contains(true);
});
}
const VArray<bool> selection = attributes.lookup_or_default<bool>(
".selection", ATTR_DOMAIN_CURVE, true);
return index_mask_ops::find_indices_from_virtual_array(curves_range, selection, 2048, r_indices);
}
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices)
{
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
return retrieve_selected_curves(curves, r_indices);
}
static IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
Vector<int64_t> &r_indices)
{
return index_mask_ops::find_indices_from_virtual_array(
curves.points_range(),
curves.attributes().lookup_or_default<bool>(".selection", ATTR_DOMAIN_POINT, true),
2048,
r_indices);
}
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
{
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
return retrieve_selected_points(curves, r_indices);
}
void ensure_selection_attribute(Curves &curves_id, const eCustomDataType create_type)
{
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
if (attributes.contains(".selection")) {
return;
}
const eAttrDomain domain = eAttrDomain(curves_id.selection_domain);
const int domain_size = attributes.domain_size(domain);
switch (create_type) {
case CD_PROP_BOOL:
attributes.add(".selection",
domain,
CD_PROP_BOOL,
bke::AttributeInitVArray(VArray<bool>::ForSingle(true, domain_size)));
break;
case CD_PROP_FLOAT:
attributes.add(".selection",
domain,
CD_PROP_FLOAT,
bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, domain_size)));
break;
default:
BLI_assert_unreachable();
}
}
void fill_selection_false(GMutableSpan selection)
{
if (selection.type().is<bool>()) {
selection.typed<bool>().fill(false);
}
else if (selection.type().is<float>()) {
selection.typed<float>().fill(0.0f);
}
}
void fill_selection_true(GMutableSpan selection)
{
if (selection.type().is<bool>()) {
selection.typed<bool>().fill(true);
}
else if (selection.type().is<float>()) {
selection.typed<float>().fill(1.0f);
}
}
} // namespace blender::ed::curves

View File

@ -20,20 +20,69 @@ void ED_operatortypes_curves(void);
#ifdef __cplusplus
# include "BKE_curves.hh"
# include "BKE_attribute.hh"
# include "BLI_index_mask.hh"
# include "BLI_vector.hh"
# include "BLI_vector_set.hh"
# include "BKE_curves.hh"
namespace blender::ed::curves {
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
bool has_anything_selected(const Curves &curves_id);
VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob);
/* -------------------------------------------------------------------- */
/** \name Poll Functions
* \{ */
bool editable_curves_with_surface_poll(bContext *C);
bool curves_with_surface_poll(bContext *C);
bool editable_curves_poll(bContext *C);
bool curves_poll(bContext *C);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Selection
*
* Selection on curves can be stored on either attribute domain: either per-curve or per-point. It
* can be stored with a float or boolean data-type. The boolean data-type is faster, smaller, and
* corresponds better to edit-mode selections, but the float data type is useful for soft selection
* (like masking) in sculpt mode.
*
* The attribute API is used to do the necessary type and domain conversions when necessary, and
* can handle most interaction with the selection attribute, but these functions implement some
* helpful utilities on top of that.
* \{ */
void fill_selection_false(GMutableSpan span);
void fill_selection_true(GMutableSpan span);
/**
* Return true if any element is selected, on either domain with either type.
*/
bool has_anything_selected(const Curves &curves_id);
/**
* Find curves that have any point selected (a selection factor greater than zero),
* or curves that have their own selection factor greater than zero.
*/
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
/**
* Find points that are selected (a selection factor greater than zero),
* or points in curves with a selection factor greater than zero).
*/
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
/**
* If the ".selection" attribute doesn't exist, create it with the requested type (bool or float).
*/
void ensure_selection_attribute(Curves &curves_id, const eCustomDataType create_type);
/** \} */
} // namespace blender::ed::curves
#endif

View File

@ -17,26 +17,3 @@ void ED_operatortypes_sculpt_curves(void);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
# include "BLI_index_mask.hh"
# include "BLI_vector.hh"
namespace blender::ed::sculpt_paint {
/**
* Find curves that have any point selected (a selection factor greater than zero),
* or curves that have their own selection factor greater than zero.
*/
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
/**
* Find points that are selected (a selection factor greater than zero),
* or points in curves with a selection factor greater than zero).
*/
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
} // namespace blender::ed::sculpt_paint
#endif

View File

@ -241,6 +241,13 @@ struct AddOperationExecutor {
const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
*curves_orig_, add_inputs);
bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
curves::fill_selection_true(selection.span.slice(selection.domain == ATTR_DOMAIN_POINT ?
add_outputs.new_points_range :
add_outputs.new_curves_range));
selection.finish();
}
if (add_outputs.uv_error) {
report_invalid_uv_map(stroke_extension.reports);

View File

@ -132,8 +132,9 @@ struct CombOperationExecutor {
transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
point_factors_ = get_point_selection(*curves_id_orig_);
curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
point_factors_ = curves_orig_->attributes().lookup_or_default<float>(
".selection", ATTR_DOMAIN_POINT, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
brush_pos_prev_re_ = self_->brush_pos_last_re_;
brush_pos_re_ = stroke_extension.mouse_position;

View File

@ -97,7 +97,7 @@ struct DeleteOperationExecutor {
curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
selected_curve_indices_.clear();
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);

View File

@ -286,6 +286,13 @@ struct DensityAddOperationExecutor {
const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
*curves_orig_, add_inputs);
bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
curves::fill_selection_true(selection.span.slice(selection.domain == ATTR_DOMAIN_POINT ?
add_outputs.new_points_range :
add_outputs.new_curves_range));
selection.finish();
}
if (add_outputs.uv_error) {
report_invalid_uv_map(stroke_extension.reports);
@ -562,7 +569,7 @@ struct DensitySubtractOperationExecutor {
minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance;
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(

View File

@ -280,8 +280,9 @@ struct CurvesEffectOperationExecutor {
return;
}
curve_selection_factors_ = get_curves_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
curve_selection_factors_ = curves_->attributes().lookup_or_default(
".selection", ATTR_DOMAIN_CURVE, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
const CurvesSculpt &curves_sculpt = *ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint);

View File

@ -11,10 +11,11 @@
#include "BLI_vector.hh"
#include "BLI_virtual_array.hh"
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "ED_curves.h"
#include "ED_curves_sculpt.h"
struct ARegion;
@ -92,15 +93,7 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry);
/**
* Get the floating point selection on the curve domain, averaged from points if necessary.
*/
VArray<float> get_curves_selection(const Curves &curves_id);
/**
* Get the floating point selection on the curve domain, copied from curves if necessary.
*/
VArray<float> get_point_selection(const Curves &curves_id);
bke::SpanAttributeWriter<float> float_selection_ensure(Curves &curves_id);
/** See #move_last_point_and_resample. */
struct MoveAndResampleBuffers {

View File

@ -363,12 +363,14 @@ static int select_random_exec(bContext *C, wmOperator *op)
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
const bool was_anything_selected = curves::has_anything_selected(*curves_id);
bke::SpanAttributeWriter<float> attribute = float_selection_ensure(*curves_id);
MutableSpan<float> selection = attribute.span;
if (!was_anything_selected) {
selection.fill(1.0f);
}
switch (curves_id->selection_domain) {
case ATTR_DOMAIN_POINT: {
MutableSpan<float> selection = curves.selection_point_float_for_write();
if (!was_anything_selected) {
selection.fill(1.0f);
}
if (partial) {
if (constant_per_curve) {
for (const int curve_i : curves.curves_range()) {
@ -408,10 +410,6 @@ static int select_random_exec(bContext *C, wmOperator *op)
break;
}
case ATTR_DOMAIN_CURVE: {
MutableSpan<float> selection = curves.selection_curve_float_for_write();
if (!was_anything_selected) {
selection.fill(1.0f);
}
if (partial) {
for (const int curve_i : curves.curves_range()) {
const float random_value = next_partial_random_value();
@ -429,9 +427,6 @@ static int select_random_exec(bContext *C, wmOperator *op)
break;
}
}
MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
curves.selection_point_float_for_write() :
curves.selection_curve_float_for_write();
const bool was_any_selected = std::any_of(
selection.begin(), selection.end(), [](const float v) { return v > 0.0f; });
if (was_any_selected) {
@ -445,6 +440,8 @@ static int select_random_exec(bContext *C, wmOperator *op)
}
}
attribute.finish();
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
@ -541,22 +538,35 @@ static int select_end_exec(bContext *C, wmOperator *op)
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const bool was_anything_selected = curves::has_anything_selected(*curves_id);
MutableSpan<float> selection = curves.selection_point_float_for_write();
curves::ensure_selection_attribute(*curves_id, CD_PROP_BOOL);
bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection");
if (!was_anything_selected) {
selection.fill(1.0f);
curves::fill_selection_true(selection.span);
}
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves.points_for_curve(curve_i);
if (end_points) {
selection.slice(points.drop_back(amount)).fill(0.0f);
}
else {
selection.slice(points.drop_front(amount)).fill(0.0f);
}
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>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves.points_for_curve(curve_i);
if (end_points) {
selection_typed.slice(points.drop_back(amount)).fill(T(0));
}
else {
selection_typed.slice(points.drop_front(amount)).fill(T(0));
}
}
});
}
});
selection.finish();
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@ -592,12 +602,14 @@ namespace select_grow {
struct GrowOperatorDataPerCurve : NonCopyable, NonMovable {
Curves *curves_id;
Vector<int> selected_points;
Vector<int> unselected_points;
Vector<int64_t> selected_point_indices;
Vector<int64_t> unselected_point_indices;
IndexMask selected_points;
IndexMask unselected_points;
Array<float> distances_to_selected;
Array<float> distances_to_unselected;
Array<float> original_selection;
GArray<> original_selection;
float pixel_to_distance_factor;
};
@ -621,7 +633,7 @@ static void update_points_selection(const GrowOperatorDataPerCurve &data,
}
});
threading::parallel_for(data.selected_points.index_range(), 512, [&](const IndexRange range) {
for (const int point_i : data.selected_points.as_span().slice(range)) {
for (const int point_i : data.selected_points.slice(range)) {
points_selection[point_i] = 1.0f;
}
});
@ -637,7 +649,7 @@ static void update_points_selection(const GrowOperatorDataPerCurve &data,
});
threading::parallel_for(
data.unselected_points.index_range(), 512, [&](const IndexRange range) {
for (const int point_i : data.unselected_points.as_span().slice(range)) {
for (const int point_i : data.unselected_points.slice(range)) {
points_selection[point_i] = 0.0f;
}
});
@ -653,18 +665,19 @@ static int select_grow_update(bContext *C, wmOperator *op, const float mouse_dif
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x;
bke::SpanAttributeWriter<float> selection = float_selection_ensure(curves_id);
/* Grow or shrink selection based on precomputed distances. */
switch (curves_id.selection_domain) {
switch (selection.domain) {
case ATTR_DOMAIN_POINT: {
MutableSpan<float> points_selection = curves.selection_point_float_for_write();
update_points_selection(*curve_op_data, distance, points_selection);
update_points_selection(*curve_op_data, distance, selection.span);
break;
}
case ATTR_DOMAIN_CURVE: {
Array<float> new_points_selection(curves.points_num());
update_points_selection(*curve_op_data, distance, new_points_selection);
/* Propagate grown point selection to the curve selection. */
MutableSpan<float> curves_selection = curves.selection_curve_float_for_write();
MutableSpan<float> curves_selection = selection.span;
for (const int curve_i : curves.curves_range()) {
const IndexRange points = curves.points_for_curve(curve_i);
const Span<float> points_selection = new_points_selection.as_span().slice(points);
@ -674,8 +687,12 @@ static int select_grow_update(bContext *C, wmOperator *op, const float mouse_dif
}
break;
}
default:
BLI_assert_unreachable();
}
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(&curves_id.id, ID_RECALC_GEOMETRY);
@ -685,57 +702,28 @@ static int select_grow_update(bContext *C, wmOperator *op, const float mouse_dif
return OPERATOR_FINISHED;
}
static void select_grow_invoke_per_curve(Curves &curves_id,
Object &curves_ob,
static void select_grow_invoke_per_curve(const Curves &curves_id,
const Object &curves_ob,
const ARegion &region,
const View3D &v3d,
const RegionView3D &rv3d,
GrowOperatorDataPerCurve &curve_op_data)
{
curve_op_data.curves_id = &curves_id;
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
const Span<float3> positions = curves.positions();
/* Find indices of selected and unselected points. */
switch (curves_id.selection_domain) {
case ATTR_DOMAIN_POINT: {
const VArray<float> points_selection = curves.selection_point_float();
curve_op_data.original_selection.reinitialize(points_selection.size());
points_selection.materialize(curve_op_data.original_selection);
for (const int point_i : points_selection.index_range()) {
const float point_selection = points_selection[point_i];
if (point_selection > 0.0f) {
curve_op_data.selected_points.append(point_i);
}
else {
curve_op_data.unselected_points.append(point_i);
}
}
break;
}
case ATTR_DOMAIN_CURVE: {
const VArray<float> curves_selection = curves.selection_curve_float();
curve_op_data.original_selection.reinitialize(curves_selection.size());
curves_selection.materialize(curve_op_data.original_selection);
for (const int curve_i : curves_selection.index_range()) {
const float curve_selection = curves_selection[curve_i];
const IndexRange points = curves.points_for_curve(curve_i);
if (curve_selection > 0.0f) {
for (const int point_i : points) {
curve_op_data.selected_points.append(point_i);
}
}
else {
for (const int point_i : points) {
curve_op_data.unselected_points.append(point_i);
}
}
}
break;
}
if (const bke::GAttributeReader original_selection = curves.attributes().lookup(".selection")) {
curve_op_data.original_selection = GArray<>(original_selection.varray.type(),
original_selection.varray.size());
original_selection.varray.materialize(curve_op_data.original_selection.data());
}
/* Find indices of selected and unselected points. */
curve_op_data.selected_points = curves::retrieve_selected_points(
curves_id, curve_op_data.selected_point_indices);
curve_op_data.unselected_points = curve_op_data.selected_points.invert(
curves.points_range(), curve_op_data.unselected_point_indices);
threading::parallel_invoke(
1024 < curve_op_data.selected_points.size() + curve_op_data.unselected_points.size(),
[&]() {
@ -838,6 +826,7 @@ static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Curves &curves_id = *static_cast<Curves *>(active_ob->data);
auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>();
curve_op_data->curves_id = &curves_id;
select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data);
op_data->per_curve.append(std::move(curve_op_data));
@ -865,17 +854,15 @@ static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event)
for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
Curves &curves_id = *curve_op_data->curves_id;
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
switch (curves_id.selection_domain) {
case ATTR_DOMAIN_POINT: {
MutableSpan<float> points_selection = curves.selection_point_float_for_write();
points_selection.copy_from(curve_op_data->original_selection);
break;
}
case ATTR_DOMAIN_CURVE: {
MutableSpan<float> curves_seletion = curves.selection_curve_float_for_write();
curves_seletion.copy_from(curve_op_data->original_selection);
break;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
attributes.remove(".selection");
if (!curve_op_data->original_selection.is_empty()) {
attributes.add(
".selection",
eAttrDomain(curves_id.selection_domain),
bke::cpp_type_to_custom_data_type(curve_op_data->original_selection.type()),
bke::AttributeInitVArray(GVArray::ForSpan(curve_op_data->original_selection)));
}
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic

View File

@ -105,8 +105,9 @@ struct PinchOperationExecutor {
transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
point_factors_ = get_point_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
point_factors_ = curves_->attributes().lookup_or_default<float>(
".selection", ATTR_DOMAIN_POINT, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
brush_pos_re_ = stroke_extension.mouse_position;
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(

View File

@ -102,8 +102,9 @@ struct PuffOperationExecutor {
brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
brush_pos_re_ = stroke_extension.mouse_position;
point_factors_ = get_point_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
point_factors_ = curves_->attributes().lookup_or_default<float>(
".selection", ATTR_DOMAIN_POINT, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);

View File

@ -10,132 +10,34 @@
namespace blender::ed::sculpt_paint {
static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
bke::SpanAttributeWriter<float> float_selection_ensure(Curves &curves_id)
{
switch (domain) {
case ATTR_DOMAIN_CURVE:
return curves.selection_curve_float();
case ATTR_DOMAIN_POINT:
return curves.adapt_domain(
curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
default:
BLI_assert_unreachable();
return {};
}
}
/* TODO: Use a generic attribute conversion utility instead of this function. */
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
VArray<float> get_curves_selection(const Curves &curves_id)
{
return get_curves_selection(CurvesGeometry::wrap(curves_id.geometry),
eAttrDomain(curves_id.selection_domain));
}
if (const auto meta_data = attributes.lookup_meta_data(".selection")) {
if (meta_data->data_type == CD_PROP_BOOL) {
const VArray<float> selection = attributes.lookup<float>(".selection");
float *dst = static_cast<float *>(
MEM_malloc_arrayN(selection.size(), sizeof(float), __func__));
selection.materialize({dst, selection.size()});
static VArray<float> get_point_selection(const CurvesGeometry &curves, const eAttrDomain domain)
{
switch (domain) {
case ATTR_DOMAIN_CURVE:
return curves.adapt_domain(
curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
case ATTR_DOMAIN_POINT:
return curves.selection_point_float();
default:
BLI_assert_unreachable();
return {};
}
}
VArray<float> get_point_selection(const Curves &curves_id)
{
return get_point_selection(CurvesGeometry::wrap(curves_id.geometry),
eAttrDomain(curves_id.selection_domain));
}
static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
const eAttrDomain domain,
Vector<int64_t> &r_indices)
{
switch (domain) {
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
const Span<float> point_selection_span = selection.get_internal_span();
return index_mask_ops::find_indices_based_on_predicate(
curves.curves_range(), 512, r_indices, [&](const int curve_i) {
for (const int i : curves.points_for_curve(curve_i)) {
if (point_selection_span[i] > 0.0f) {
return true;
}
}
return false;
});
attributes.remove(".selection");
attributes.add(
".selection", meta_data->domain, CD_PROP_FLOAT, bke::AttributeInitMoveArray(dst));
}
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
curves.curves_range(), 2048, r_indices, [&](const int i) {
return selection[i] > 0.0f;
});
}
default:
BLI_assert_unreachable();
return {};
}
}
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices)
{
return retrieve_selected_curves(CurvesGeometry::wrap(curves_id.geometry),
eAttrDomain(curves_id.selection_domain),
r_indices);
}
static IndexMask retrieve_selected_points(const CurvesGeometry &curves,
const eAttrDomain domain,
Vector<int64_t> &r_indices)
{
switch (domain) {
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.points_num());
}
return index_mask_ops::find_indices_based_on_predicate(
curves.points_range(), 2048, r_indices, [&](const int i) {
return selection[i] > 0.0f;
});
}
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.points_num());
}
const VArray<float> point_selection = curves.adapt_domain(
selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
return index_mask_ops::find_indices_based_on_predicate(
curves.points_range(), 2048, r_indices, [&](const int i) {
return point_selection[i] > 0.0f;
});
}
default:
BLI_assert_unreachable();
return {};
else {
const eAttrDomain domain = eAttrDomain(curves_id.selection_domain);
const int64_t size = attributes.domain_size(domain);
attributes.add(".selection",
domain,
CD_PROP_FLOAT,
bke::AttributeInitVArray(VArray<float>::ForSingle(size, 1.0f)));
}
}
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
{
return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry),
eAttrDomain(curves_id.selection_domain),
r_indices);
return curves.attributes_for_write().lookup_for_write_span<float>(".selection");
}
} // namespace blender::ed::sculpt_paint

View File

@ -8,6 +8,7 @@
#include "DNA_brush_types.h"
#include "BKE_attribute.hh"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
@ -57,6 +58,8 @@ struct SelectionPaintOperationExecutor {
Curves *curves_id_ = nullptr;
CurvesGeometry *curves_ = nullptr;
bke::SpanAttributeWriter<float> selection_;
const Brush *brush_ = nullptr;
float brush_radius_base_re_;
float brush_radius_factor_;
@ -84,6 +87,10 @@ struct SelectionPaintOperationExecutor {
if (curves_->curves_num() == 0) {
return;
}
selection_ = float_selection_ensure(*curves_id_);
if (!selection_) {
return;
}
brush_ = BKE_paint_brush_for_read(&ctx_.scene->toolsettings->curves_sculpt->paint);
brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_);
@ -94,12 +101,7 @@ struct SelectionPaintOperationExecutor {
if (self.clear_selection_) {
if (stroke_extension.is_first) {
if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) {
curves_->selection_point_float_for_write().fill(0.0f);
}
else if (curves_id_->selection_domain == ATTR_DOMAIN_CURVE) {
curves_->selection_curve_float_for_write().fill(0.0f);
}
curves::fill_selection_false(selection_.span);
}
}
@ -116,25 +118,25 @@ struct SelectionPaintOperationExecutor {
}
}
if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) {
MutableSpan<float> selection = curves_->selection_point_float_for_write();
if (selection_.domain == ATTR_DOMAIN_POINT) {
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
this->paint_point_selection_projected_with_symmetry(selection);
this->paint_point_selection_projected_with_symmetry(selection_.span);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
this->paint_point_selection_spherical_with_symmetry(selection);
this->paint_point_selection_spherical_with_symmetry(selection_.span);
}
}
else {
MutableSpan<float> selection = curves_->selection_curve_float_for_write();
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
this->paint_curve_selection_projected_with_symmetry(selection);
this->paint_curve_selection_projected_with_symmetry(selection_.span);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
this->paint_curve_selection_spherical_with_symmetry(selection);
this->paint_curve_selection_spherical_with_symmetry(selection_.span);
}
}
selection_.finish();
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because
* selection is handled as a generic attribute for now. */
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);

View File

@ -157,8 +157,9 @@ struct SlideOperationExecutor {
brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
curve_factors_ = get_curves_selection(*curves_id_orig_);
curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
curve_factors_ = curves_orig_->attributes().lookup_or_default(
".selection", ATTR_DOMAIN_CURVE, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
brush_pos_re_ = stroke_extension.mouse_position;

View File

@ -78,8 +78,9 @@ struct SmoothOperationExecutor {
brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
brush_pos_re_ = stroke_extension.mouse_position;
point_factors_ = get_point_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
point_factors_ = curves_->attributes().lookup_or_default<float>(
".selection", ATTR_DOMAIN_POINT, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(

View File

@ -125,8 +125,9 @@ struct SnakeHookOperatorExecutor {
transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
curve_factors_ = get_curves_selection(*curves_id_);
curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
curve_factors_ = curves_->attributes().lookup_or_default(
".selection", ATTR_DOMAIN_CURVE, 1.0f);
curve_selection_ = curves::retrieve_selected_curves(*curves_id_, selected_curve_indices_);
brush_pos_prev_re_ = self.last_mouse_position_re_;
brush_pos_re_ = stroke_extension.mouse_position;

View File

@ -25,7 +25,7 @@
#include "DEG_depsgraph_query.h"
#include "ED_curves_sculpt.h"
#include "ED_curves.h"
#include "ED_spreadsheet.h"
#include "NOD_geometry_nodes_lazy_function.hh"
@ -265,7 +265,7 @@ bool GeometryDataSource::has_selection_filter() const
if (object_orig->type != OB_CURVES) {
return false;
}
if (object_orig->mode != OB_MODE_SCULPT_CURVES) {
if (!ELEM(object_orig->mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
return false;
}
return true;
@ -339,9 +339,9 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
const Curves &curves_id = *component.get_for_read();
switch (domain_) {
case ATTR_DOMAIN_POINT:
return sculpt_paint::retrieve_selected_points(curves_id, indices);
return curves::retrieve_selected_points(curves_id, indices);
case ATTR_DOMAIN_CURVE:
return sculpt_paint::retrieve_selected_curves(curves_id, indices);
return curves::retrieve_selected_curves(curves_id, indices);
default:
BLI_assert_unreachable();
}

View File

@ -47,6 +47,8 @@ struct AddCurvesOnMeshInputs {
struct AddCurvesOnMeshOutputs {
bool uv_error = false;
IndexRange new_curves_range;
IndexRange new_points_range;
};
/**

View File

@ -303,6 +303,10 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
curves.resize(new_points_num, new_curves_num);
MutableSpan<float3> positions_cu = curves.positions_for_write();
/* The new elements are added at the end of the arrays. */
outputs.new_points_range = curves.points_range().drop_front(old_points_num);
outputs.new_curves_range = curves.curves_range().drop_front(old_curves_num);
/* Initialize attachment information. */
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs);
@ -338,18 +342,6 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
}
});
/* Update selection arrays when available. */
const VArray<float> points_selection = curves.selection_point_float();
if (points_selection.is_span()) {
MutableSpan<float> points_selection_span = curves.selection_point_float_for_write();
points_selection_span.drop_front(old_points_num).fill(1.0f);
}
const VArray<float> curves_selection = curves.selection_curve_float();
if (curves_selection.is_span()) {
MutableSpan<float> curves_selection_span = curves.selection_curve_float_for_write();
curves_selection_span.slice(new_curves_range).fill(1.0f);
}
/* Initialize position attribute. */
if (inputs.interpolate_shape) {
interpolate_position_with_interpolation(curves,
@ -374,24 +366,20 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM);
/* Explicitly set all other attributes besides those processed above to default values. */
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
Set<std::string> attributes_to_skip{{"position",
"curve_type",
"surface_uv_coordinate",
".selection_point_float",
".selection_curve_float"}};
/* Explicitly set all other attributes besides those processed above to default values. */
Set<std::string> attributes_to_skip{{"position", "curve_type", "surface_uv_coordinate"}};
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
if (id.is_named() && attributes_to_skip.contains(id.name())) {
return true;
}
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
/* The new elements are added at the end of the array. */
const int old_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? old_points_num :
old_curves_num;
const CPPType &type = attribute.span.type();
GMutableSpan new_data = attribute.span.drop_front(old_elements_num);
GMutableSpan new_data = attribute.span.slice(attribute.domain == ATTR_DOMAIN_POINT ?
outputs.new_points_range :
outputs.new_curves_range);
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
attribute.finish();
return true;

View File

@ -9,6 +9,7 @@
#include "BLI_utildefines.h"
#include "BLI_array_utils.hh"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@ -134,13 +135,6 @@ static void compute_vertex_mask__vertex_group_mode(const MDeformVert *dvert,
}
}
static void invert_boolean_array(MutableSpan<bool> array)
{
for (bool &value : array) {
value = !value;
}
}
static void compute_masked_verts(Span<bool> vertex_mask,
MutableSpan<int> r_vertex_map,
uint *r_verts_masked_num)
@ -685,7 +679,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext * /*ctx*/, M
}
if (invert_mask) {
invert_boolean_array(vertex_mask);
blender::array_utils::invert_booleans(vertex_mask);
}
Array<int> vertex_map(mesh->totvert);