Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-03-18 20:24:05 +0300
committerHans Goudey <h.goudey@me.com>2022-03-18 20:24:05 +0300
commit298d8a7b4aedb76600b3774727ec203e6c4d0586 (patch)
treee963b54d67627258818b4f1ef1fa993d6a982407 /source/blender
parentd2726e46262a4f8cecc9a060ecc26ffc5928854c (diff)
Curves: Port reverse curves node to the new data-block
Create a function on CurvesGeometry that can also be used for an edit mode operator in the future. Dealing with CustomData directly means the code is a bit more verbose than would be ideal, but this would be a simple thing to clean up in the future if we get an attribute API here. Also change the reverse node to first work on a read-only geometry component, and only get write access if there is a curve selected. Differential Revision: https://developer.blender.org/D14375
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_curves.hh6
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc100
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc19
3 files changed, 114 insertions, 11 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index c28886e8a52..93b98a01fce 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -277,6 +277,12 @@ class CurvesGeometry : public ::CurvesGeometry {
void remove_curves(IndexMask curves_to_delete);
+ /**
+ * Change the direction of selected curves (switch the start and end) without changing their
+ * shape.
+ */
+ void reverse_curves(IndexMask curves_to_reverse);
+
/* --------------------------------------------------------------------
* Attributes.
*/
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 1fcdcd2f4e9..ff24720e5e5 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -860,6 +860,106 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
*this = copy_with_removed_curves(*this, curves_to_delete);
}
+template<typename T>
+static void reverse_curve_point_data(const CurvesGeometry &curves,
+ const IndexMask curve_selection,
+ MutableSpan<T> data)
+{
+ threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
+ for (const int curve_i : curve_selection.slice(range)) {
+ data.slice(curves.range_for_curve(curve_i)).reverse();
+ }
+ });
+}
+
+template<typename T>
+static void reverse_swap_curve_point_data(const CurvesGeometry &curves,
+ const IndexMask curve_selection,
+ MutableSpan<T> data_a,
+ MutableSpan<T> data_b)
+{
+ threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
+ for (const int curve_i : curve_selection.slice(range)) {
+ const IndexRange points = curves.range_for_curve(curve_i);
+ MutableSpan<T> a = data_a.slice(points);
+ MutableSpan<T> b = data_b.slice(points);
+ for (const int i : IndexRange(points.size() / 2)) {
+ const int end_index = points.size() - 1 - i;
+ std::swap(a[end_index], b[i]);
+ std::swap(b[end_index], a[i]);
+ }
+ }
+ });
+}
+
+static bool layer_matches_name_and_type(const CustomDataLayer &layer,
+ const StringRef name,
+ const CustomDataType type)
+{
+ if (layer.type != type) {
+ return false;
+ }
+ return layer.name == name;
+}
+
+void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
+{
+ CustomData_duplicate_referenced_layers(&this->point_data, this->points_size());
+
+ /* Collect the Bezier handle attributes while iterating through the point custom data layers;
+ * they need special treatment later. */
+ MutableSpan<float3> positions_left;
+ MutableSpan<float3> positions_right;
+ MutableSpan<int8_t> types_left;
+ MutableSpan<int8_t> types_right;
+
+ for (const int layer_i : IndexRange(this->point_data.totlayer)) {
+ CustomDataLayer &layer = this->point_data.layers[layer_i];
+
+ if (positions_left.is_empty() &&
+ layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_LEFT, CD_PROP_FLOAT3)) {
+ positions_left = {static_cast<float3 *>(layer.data), this->points_size()};
+ continue;
+ }
+ if (positions_right.is_empty() &&
+ layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_RIGHT, CD_PROP_FLOAT3)) {
+ positions_right = {static_cast<float3 *>(layer.data), this->points_size()};
+ continue;
+ }
+ if (types_left.is_empty() &&
+ layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_LEFT, CD_PROP_INT8)) {
+ types_left = {static_cast<int8_t *>(layer.data), this->points_size()};
+ continue;
+ }
+ if (types_right.is_empty() &&
+ layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_RIGHT, CD_PROP_INT8)) {
+ types_right = {static_cast<int8_t *>(layer.data), this->points_size()};
+ continue;
+ }
+
+ const CustomDataType data_type = static_cast<CustomDataType>(layer.type);
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ reverse_curve_point_data<T>(
+ *this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_size()});
+ });
+ }
+
+ /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
+ * values for the left and right must swap. Use a utility to swap and reverse at the same time,
+ * to avoid loading the attribute twice. Generally we can expect the right layer to exist when
+ * the left does, but there's no need to count on it, so check for both attributes. */
+
+ if (!positions_left.is_empty() && !positions_right.is_empty()) {
+ reverse_swap_curve_point_data(*this, curves_to_reverse, positions_left, positions_right);
+ }
+ if (!types_left.is_empty() && !types_right.is_empty()) {
+ reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right);
+ }
+
+ this->tag_topology_changed();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index 8393f9615aa..de29735bd2d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -2,7 +2,7 @@
#include "BLI_task.hh"
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "node_geometry_util.hh"
@@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
@@ -33,16 +33,13 @@ static void node_geo_exec(GeoNodeExecParams params)
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return;
+ }
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write());
- MutableSpan<SplinePtr> splines = curve->splines();
- threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- splines[selection[i]]->reverse();
- }
- });
-
- component.replace(curve_eval_to_curves(*curve));
+ Curves &curves_id = *geometry_set.get_curves_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ curves.reverse_curves(selection);
});
params.set_output("Curve", std::move(geometry_set));