[Bf-blender-cvs] [298d8a7b4ae] master: Curves: Port reverse curves node to the new data-block

Hans Goudey noreply at git.blender.org
Fri Mar 18 18:24:14 CET 2022


Commit: 298d8a7b4aedb76600b3774727ec203e6c4d0586
Author: Hans Goudey
Date:   Fri Mar 18 12:24:05 2022 -0500
Branches: master
https://developer.blender.org/rB298d8a7b4aedb76600b3774727ec203e6c4d0586

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

===================================================================

M	source/blender/blenkernel/BKE_curves.hh
M	source/blender/blenkernel/intern/curves_geometry.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc

===================================================================

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));



More information about the Bf-blender-cvs mailing list