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-04-01 16:11:58 +0300
committerHans Goudey <h.goudey@me.com>2022-04-01 16:12:41 +0300
commit00ba51d37bf5b152176409b393eafbb0ad9333e6 (patch)
tree499a1503c59f3558101bba52cef10d64c39a57fd
parenta250d3d1b7d8d497c21a1ef845e64f07e68beda9 (diff)
Geometry Nodes: Port set handle nodes to new data-block
This commit ports the "Set Handle Positions" and "Set Hanle Type" nodes to use the new curves data-block. The nodes become simpler and likely much faster too, though they're usually not the bottleneck anyway. Most of the code is ported from `BezierSpline` directly. The majority of the complexity comes from the interaction between different automatically calculated handle types. In comparison `BezierSpline`, the calculation of auto handles is done eagerly-- mostly because it's simpler. Eventually lazy calculation might be good to add. Differential Revision: https://developer.blender.org/D14464
-rw-r--r--source/blender/blenkernel/BKE_curves.hh34
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc125
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc183
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc8
6 files changed, 335 insertions, 141 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 67671e46ad4..f097acc497f 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -338,6 +338,8 @@ class CurvesGeometry : public ::CurvesGeometry {
void translate(const float3 &translation);
void transform(const float4x4 &matrix);
+ void calculate_bezier_auto_handles();
+
void update_customdata_pointers();
void remove_curves(IndexMask curves_to_delete);
@@ -397,9 +399,37 @@ void calculate_evaluated_offsets(Span<int8_t> handle_types_left,
MutableSpan<int> evaluated_offsets);
/**
+ * Recalculate all auto (#BEZIER_HANDLE_AUTO) and vector (#BEZIER_HANDLE_VECTOR) handles with
+ * positions automatically derived from the neighboring control points, and update aligned
+ * (#BEZIER_HANDLE_ALIGN) handles to line up with neighboring non-aligned handles. The choices
+ * made here are relatively arbitrary, but having standardized behavior is essential.
+ */
+void calculate_auto_handles(bool cyclic,
+ Span<int8_t> types_left,
+ Span<int8_t> types_right,
+ Span<float3> positions,
+ MutableSpan<float3> positions_left,
+ MutableSpan<float3> positions_right);
+
+/**
+ * Change the handles of a single control point, aligning any aligned (#BEZIER_HANDLE_ALIGN)
+ * handles on the other side of the control point.
+ *
+ * \note This ignores the inputs if the handle types are automatically calculated,
+ * so the types should be updated before-hand to be editable.
+ */
+void set_handle_position(const float3 &position,
+ HandleType type,
+ HandleType type_other,
+ const float3 &new_handle,
+ float3 &handle,
+ float3 &handle_other);
+
+/**
* Evaluate a cubic Bezier segment, using the "forward differencing" method.
- * A generic Bezier curve is made up by four points, but in many cases the first and last points
- * are referred to as the control points, and the middle points are the corresponding handles.
+ * A generic Bezier curve is made up by four points, but in many cases the first and last
+ * points are referred to as the control points, and the middle points are the corresponding
+ * handles.
*/
void evaluate_segment(const float3 &point_0,
const float3 &point_1,
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index 0d3bb2e3a7d..30a5869c976 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -58,6 +58,131 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left,
evaluated_offsets.last() = offset;
}
+static float3 calculate_aligned_handle(const float3 &position,
+ const float3 &other_handle,
+ const float3 &aligned_handle)
+{
+ /* Keep track of the old length of the opposite handle. */
+ const float length = math::distance(aligned_handle, position);
+ /* Set the other handle to directly opposite from the current handle. */
+ const float3 dir = math::normalize(other_handle - position);
+ return position - dir * length;
+}
+
+static void calculate_point_handles(const HandleType type_left,
+ const HandleType type_right,
+ const float3 position,
+ const float3 prev_position,
+ const float3 next_position,
+ float3 &left,
+ float3 &right)
+{
+ if (ELEM(BEZIER_HANDLE_AUTO, type_left, type_right)) {
+ const float3 prev_diff = position - prev_position;
+ const float3 next_diff = next_position - position;
+ float prev_len = math::length(prev_diff);
+ float next_len = math::length(next_diff);
+ if (prev_len == 0.0f) {
+ prev_len = 1.0f;
+ }
+ if (next_len == 0.0f) {
+ next_len = 1.0f;
+ }
+ const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+ /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+ const float len = math::length(dir) * 2.5614f;
+ if (len != 0.0f) {
+ if (type_left == BEZIER_HANDLE_AUTO) {
+ const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+ left = position + dir * -(prev_len_clamped / len);
+ }
+ if (type_right == BEZIER_HANDLE_AUTO) {
+ const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+ right = position + dir * (next_len_clamped / len);
+ }
+ }
+ }
+
+ if (type_left == BEZIER_HANDLE_VECTOR) {
+ left = math::interpolate(position, prev_position, 1.0f / 3.0f);
+ }
+
+ if (type_right == BEZIER_HANDLE_VECTOR) {
+ right = math::interpolate(position, next_position, 1.0f / 3.0f);
+ }
+
+ /* When one of the handles is "aligned" handle, it must be aligned with the other, i.e. point in
+ * the opposite direction. Don't handle the case of two aligned handles, because code elsewhere
+ * should keep the pair consistent, and the relative locations aren't affected by other points
+ * anyway. */
+ if (type_left == BEZIER_HANDLE_ALIGN && type_right != BEZIER_HANDLE_ALIGN) {
+ left = calculate_aligned_handle(position, right, left);
+ }
+ else if (type_left != BEZIER_HANDLE_ALIGN && type_right == BEZIER_HANDLE_ALIGN) {
+ right = calculate_aligned_handle(position, left, right);
+ }
+}
+
+void set_handle_position(const float3 &position,
+ const HandleType type,
+ const HandleType type_other,
+ const float3 &new_handle,
+ float3 &handle,
+ float3 &handle_other)
+{
+ /* Don't bother when the handle positions are calculated automatically anyway. */
+ if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
+ return;
+ }
+
+ handle = new_handle;
+ if (type_other == BEZIER_HANDLE_ALIGN) {
+ handle_other = calculate_aligned_handle(position, handle, handle_other);
+ }
+}
+
+void calculate_auto_handles(const bool cyclic,
+ const Span<int8_t> types_left,
+ const Span<int8_t> types_right,
+ const Span<float3> positions,
+ MutableSpan<float3> positions_left,
+ MutableSpan<float3> positions_right)
+{
+ const int points_num = positions.size();
+ if (points_num == 1) {
+ return;
+ }
+
+ calculate_point_handles(HandleType(types_left.first()),
+ HandleType(types_right.first()),
+ positions.first(),
+ cyclic ? positions.last() : 2.0f * positions.first() - positions[1],
+ positions[1],
+ positions_left.first(),
+ positions_right.first());
+
+ threading::parallel_for(IndexRange(1, points_num - 2), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ calculate_point_handles(HandleType(types_left[i]),
+ HandleType(types_right[i]),
+ positions[i],
+ positions[i - 1],
+ positions[i + 1],
+ positions_left[i],
+ positions_right[i]);
+ }
+ });
+
+ calculate_point_handles(HandleType(types_left.last()),
+ HandleType(types_right.last()),
+ positions.last(),
+ positions.last(1),
+ cyclic ? positions.first() : 2.0f * positions.last() - positions.last(1),
+ positions_left.last(),
+ positions_right.last());
+}
+
void evaluate_segment(const float3 &point_0,
const float3 &point_1,
const float3 &point_2,
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 94402f0e548..66088714e63 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -851,6 +851,34 @@ static void transform_positions(MutableSpan<float3> positions, const float4x4 &m
});
}
+void CurvesGeometry::calculate_bezier_auto_handles()
+{
+ const VArray<int8_t> types = std::as_const(*this).curve_types();
+ if (types.is_single() && types.get_internal_single() != CURVE_TYPE_BEZIER) {
+ return;
+ }
+ const VArray<bool> cyclic = std::as_const(*this).cyclic();
+ const Span<int8_t> types_left = this->handle_types_left();
+ const Span<int8_t> types_right = this->handle_types_right();
+ const Span<float3> positions = this->positions();
+ MutableSpan<float3> positions_left = this->handle_positions_left();
+ MutableSpan<float3> positions_right = this->handle_positions_right();
+
+ threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
+ for (const int i_curve : range) {
+ if (types[i_curve] == CURVE_TYPE_BEZIER) {
+ const IndexRange points = this->points_for_curve(i_curve);
+ curves::bezier::calculate_auto_handles(cyclic[i_curve],
+ types_left.slice(points),
+ types_right.slice(points),
+ positions.slice(points),
+ positions_left.slice(points),
+ positions_right.slice(points));
+ }
+ }
+ });
+}
+
void CurvesGeometry::translate(const float3 &translation)
{
/* Use `as_const` because the non-const functions can add the handle attributes. */
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
index e8ba78816a5..169f808c473 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include <atomic>
+
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -49,6 +51,33 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
return BEZIER_HANDLE_AUTO;
}
+static void set_type_in_component(CurveComponent &component,
+ const GeometryNodeCurveHandleMode mode,
+ const HandleType new_handle_type,
+ const Field<bool> &selection_field)
+{
+ Curves &curves_id = *component.get_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ curves.handle_types_left().fill_indices(selection, new_handle_type);
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ curves.handle_types_right().fill_indices(selection, new_handle_type);
+ }
+
+ /* Eagerly calculate automatically derived handle positions if necessary. */
+ if (ELEM(new_handle_type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_ALIGN)) {
+ curves.calculate_bezier_auto_handles();
+ }
+}
+
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSetHandles &storage = node_storage(params.node());
@@ -58,62 +87,33 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- bool has_bezier_spline = false;
+ const HandleType new_handle_type = handle_type_from_input_type(type);
+
+ std::atomic<bool> has_curves = false;
+ std::atomic<bool> has_bezier = false;
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_curves()) {
return;
}
-
- /* Retrieve data for write access so we can avoid new allocations for the handles data. */
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
- MutableSpan<SplinePtr> splines = curve->splines();
-
- GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
- const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
-
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
-
- const HandleType new_handle_type = handle_type_from_input_type(type);
- int point_index = 0;
-
- for (SplinePtr &spline : splines) {
- if (spline->type() != CURVE_TYPE_BEZIER) {
- point_index += spline->positions().size();
- continue;
- }
-
- has_bezier_spline = true;
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
- if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) {
- /* In this case the automatically calculated handle types need to be "baked", because
- * they're possibly changing from a type that is calculated automatically to a type that
- * is positioned manually. */
- bezier_spline.ensure_auto_handles();
- }
-
- for (int i_point : IndexRange(bezier_spline.size())) {
- if (selection[point_index]) {
- if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- bezier_spline.handle_types_left()[i_point] = new_handle_type;
- }
- if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
- bezier_spline.handle_types_right()[i_point] = new_handle_type;
- }
- }
- point_index++;
- }
- bezier_spline.mark_cache_invalid();
+ has_curves = true;
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ if (!component.attribute_exists("handle_type_left") ||
+ !component.attribute_exists("handle_type_right")) {
+ return;
}
+ has_bezier = true;
- curve_component.replace(curve_eval_to_curves(*curve));
+ set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(),
+ mode,
+ new_handle_type,
+ selection_field);
});
- if (!has_bezier_spline) {
- params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
+
+ if (has_curves && !has_bezier) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Input curves do not have Bezier type"));
}
+
params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes::node_geo_curve_set_handle_type_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
index 271dd824d27..31b9f1765a5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include <atomic>
+
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -34,8 +36,40 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
- CurveComponent &component,
+static void update_handle_types_for_movement(int8_t &type, int8_t &other)
+{
+ switch (type) {
+ case BEZIER_HANDLE_FREE:
+ break;
+ case BEZIER_HANDLE_AUTO:
+ /* Converting auto handles to aligned handled instead of free handles is
+ * arbitrary, but expected and "standard" based on behavior in edit mode. */
+ if (other == BEZIER_HANDLE_AUTO) {
+ /* Convert pairs of auto handles to aligned handles when moving one side. */
+ type = BEZIER_HANDLE_ALIGN;
+ other = BEZIER_HANDLE_ALIGN;
+ }
+ else {
+ /* If the other handle isn't automatic, just make the handle free. */
+ type = BEZIER_HANDLE_FREE;
+ }
+ break;
+ case BEZIER_HANDLE_VECTOR:
+ type = BEZIER_HANDLE_FREE;
+ break;
+ case BEZIER_HANDLE_ALIGN:
+ /* The handle can stay aligned if the other handle is also aligned (in which case the other
+ * handle should be updated to be consistent). But otherwise the handle must be made free to
+ * avoid conflicting with its "aligned" type. */
+ if (other != BEZIER_HANDLE_ALIGN) {
+ type = BEZIER_HANDLE_FREE;
+ }
+ break;
+ }
+}
+
+static void set_position_in_component(CurveComponent &component,
+ const GeometryNodeCurveHandleMode mode,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
@@ -52,83 +86,44 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
evaluator.add(offset_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
-
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
-
- int current_point = 0;
- int current_mask = 0;
- for (const SplinePtr &spline : curve->splines()) {
- if (spline->type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier = static_cast<BezierSpline &>(*spline);
-
- bezier.ensure_auto_handles();
- for (const int i : bezier.positions().index_range()) {
- if (current_mask < selection.size() && selection[current_mask] == current_point) {
- if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) {
- bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE;
- }
- else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) {
- bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN;
- }
- }
- else {
- if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) {
- bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE;
- }
- else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) {
- bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN;
- }
- }
- current_mask++;
- }
- current_point++;
- }
+ const VArray<float3> &new_positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> &new_offsets = evaluator.get_evaluated<float3>(1);
+
+ Curves &curves_id = *component.get_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+
+ Span<float3> positions = curves.positions();
+
+ const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT;
+ MutableSpan<int8_t> handle_types = use_left ? curves.handle_types_left() :
+ curves.handle_types_right();
+ MutableSpan<int8_t> handle_types_other = use_left ? curves.handle_types_right() :
+ curves.handle_types_left();
+ MutableSpan<float3> handle_positions = use_left ? curves.handle_positions_left() :
+ curves.handle_positions_right();
+ MutableSpan<float3> handle_positions_other = use_left ? curves.handle_positions_right() :
+ curves.handle_positions_left();
+
+ threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ update_handle_types_for_movement(handle_types[i], handle_types_other[i]);
}
- else {
- for ([[maybe_unused]] int i : spline->positions().index_range()) {
- if (current_mask < selection.size() && selection[current_mask] == current_point) {
- current_mask++;
- }
- current_point++;
- }
- }
- }
+ });
- const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1);
-
- current_point = 0;
- current_mask = 0;
- for (const SplinePtr &spline : curve->splines()) {
- if (spline->type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier = static_cast<BezierSpline &>(*spline);
- for (const int i : bezier.positions().index_range()) {
- if (current_mask < selection.size() && selection[current_mask] == current_point) {
- if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- bezier.set_handle_position_left(
- i, positions_input[current_point] + offsets_input[current_point]);
- }
- else {
- bezier.set_handle_position_right(
- i, positions_input[current_point] + offsets_input[current_point]);
- }
- current_mask++;
- }
- current_point++;
- }
- }
- else {
- for ([[maybe_unused]] int i : spline->positions().index_range()) {
- if (current_mask < selection.size() && selection[current_mask] == current_point) {
- current_mask++;
- }
- current_point++;
- }
+ threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ bke::curves::bezier::set_handle_position(positions[i],
+ HandleType(handle_types[i]),
+ HandleType(handle_types_other[i]),
+ new_positions[i] + new_offsets[i],
+ handle_positions[i],
+ handle_positions_other[i]);
}
- }
+ });
- component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned);
+ curves.calculate_bezier_auto_handles();
+
+ curves.tag_positions_changed();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -141,24 +136,32 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
- bool has_bezier = false;
+ std::atomic<bool> has_curves = false;
+ std::atomic<bool> has_bezier = false;
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_for_read());
- has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER);
-
- set_position_in_component(mode,
- geometry_set.get_component_for_write<CurveComponent>(),
- selection_field,
- position_field,
- offset_field);
+ if (!geometry_set.has_curves()) {
+ return;
+ }
+ has_curves = true;
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ if (!component.attribute_exists("handle_left") ||
+ !component.attribute_exists("handle_right")) {
+ return;
}
+ has_bezier = true;
+
+ set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(),
+ mode,
+ selection_field,
+ position_field,
+ offset_field);
});
- if (!has_bezier) {
- params.error_message_add(NodeWarningType::Info,
- TIP_("The input geometry does not contain a Bezier spline"));
+
+ if (has_curves && !has_bezier) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Input curves do not have Bezier type"));
}
+
params.set_output("Curve", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index eb035aa9b6b..d2ff9753897 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -7,6 +7,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_position_cc {
@@ -62,6 +64,9 @@ static void set_computed_position_and_offset(GeometryComponent &component,
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
+ CurveComponent &curve_component = static_cast<CurveComponent &>(component);
+ Curves &curves_id = *curve_component.get_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
if (component.attribute_exists("handle_right") &&
component.attribute_exists("handle_left")) {
OutputAttribute_Typed<float3> handle_right_attribute =
@@ -90,6 +95,9 @@ static void set_computed_position_and_offset(GeometryComponent &component,
handle_right_attribute.save();
handle_left_attribute.save();
+
+ /* Automatic Bezier handles must be recalculated based on the new positions. */
+ curves.calculate_bezier_auto_handles();
break;
}
else {