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:
Diffstat (limited to 'source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc')
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc822
1 files changed, 573 insertions, 249 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 6e45411a9af..8479d61a890 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include <numeric>
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
#include "BLI_task.hh"
@@ -29,10 +33,39 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__);
- data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ data->spline_type = CURVE_TYPE_POLY;
node->storage = data;
}
+/**
+ * This function answers the question about possible conversion method for NURBS-to-Bezier. In
+ * general for 3rd degree NURBS curves there is one-to-one relation with 3rd degree Bezier curves
+ * that can be exploit for conversion - Bezier handles sit on NURBS hull segments and in the middle
+ * between those handles are Bezier anchor points.
+ */
+static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
+{
+ if (ELEM(knots_mode, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * As an optimization, just change the types on a mutable curves data-block when the conversion is
+ * simple. This could be expanded to more cases where the number of points doesn't change in the
+ * future, though that might require properly initializing some attributes, or removing others.
+ */
+static bool conversion_can_change_point_num(const CurveType dst_type)
+{
+ if (ELEM(dst_type, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_POLY)) {
+ /* The conversion to Catmull Rom or Poly should never change the number of points, no matter
+ * the source type (Bezier to Catmull Rom conversion cannot maintain the same shape anyway). */
+ return false;
+ }
+ return true;
+}
+
template<typename T>
static void scale_input_assign(const Span<T> src,
const int scale,
@@ -44,32 +77,97 @@ static void scale_input_assign(const Span<T> src,
}
}
-template<typename T>
-static void scale_output_assign(const Span<T> src,
- const int scale,
- const int offset,
- MutableSpan<T> dst)
+/**
+ * The Bezier control point and its handles become three control points on the NURBS curve,
+ * so each attribute value is duplicated three times.
+ */
+template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
{
for (const int i : src.index_range()) {
- dst[i * scale + offset] = src[i];
+ dst[i * 3] = src[i];
+ dst[i * 3 + 1] = src[i];
+ dst[i * 3 + 2] = src[i];
+ }
+}
+
+static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
+ });
+}
+
+static void bezier_positions_to_nurbs(const Span<float3> src_positions,
+ const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ MutableSpan<float3> dst_positions)
+{
+ for (const int i : src_positions.index_range()) {
+ dst_positions[i * 3] = src_handles_l[i];
+ dst_positions[i * 3 + 1] = src_positions[i];
+ dst_positions[i * 3 + 2] = src_handles_r[i];
+ }
+}
+
+static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
+ const bool cyclic,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r)
+{
+ /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
+ * This constant defines the portion of the distance between the next/previous points to use for
+ * the length of the handles. */
+ constexpr float handle_scale = 1.0f / 6.0f;
+
+ if (src_positions.size() == 1) {
+ dst_handles_l.first() = src_positions.first();
+ dst_handles_r.first() = src_positions.first();
+ return;
+ }
+
+ const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
+ src_positions[1] - src_positions[0];
+ dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
+ dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
+
+ const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
+ src_positions.last() - src_positions.last(1);
+ dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
+ dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
+
+ for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
+ const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
+ dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
+
+ const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
+ dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
}
}
+static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
+ const bool cyclic,
+ MutableSpan<float3> dst_positions)
+{
+ /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
+ * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
+ Array<float3, 32> bezier_handles_l(src_positions.size());
+ Array<float3, 32> bezier_handles_r(src_positions.size());
+ catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
+ bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
+}
+
template<typename T>
static void nurbs_to_bezier_assign(const Span<T> src,
const MutableSpan<T> dst,
const KnotsMode knots_mode)
{
switch (knots_mode) {
- case NURBS_KNOT_MODE_BEZIER:
- scale_input_assign<T>(src, 3, 1, dst);
- break;
case NURBS_KNOT_MODE_NORMAL:
for (const int i : dst.index_range()) {
dst[i] = src[(i + 1) % src.size()];
}
break;
- case NURBS_KNOT_MODE_ENDPOINT_BEZIER:
case NURBS_KNOT_MODE_ENDPOINT:
for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
dst[i] = src[i + 1];
@@ -77,54 +175,28 @@ static void nurbs_to_bezier_assign(const Span<T> src,
dst.first() = src.first();
dst.last() = src.last();
break;
+ default:
+ /* Every 3rd NURBS position (starting from index 1) should have its attributes transferred.
+ */
+ scale_input_assign<T>(src, 3, 1, dst);
}
}
-template<typename CopyFn>
-static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, GMutableSpan dst)
{
- input_spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
- BLI_assert(src);
- if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
- BLI_assert_unreachable();
- return false;
- }
- std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
- if (!dst) {
- BLI_assert_unreachable();
- return false;
- }
-
- copy_fn(*src, *dst);
-
- return true;
- },
- ATTR_DOMAIN_POINT);
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
+ });
}
static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions,
const KnotsMode knots_mode)
{
- const int nurbs_positions_size = nurbs_positions.size();
+ const int nurbs_positions_num = nurbs_positions.size();
Vector<float3> handle_positions;
- if (knots_mode == NURBS_KNOT_MODE_BEZIER) {
- for (const int i : IndexRange(nurbs_positions_size)) {
- if (i % 3 == 1) {
- continue;
- }
- handle_positions.append(nurbs_positions[i]);
- }
- if (nurbs_positions_size % 3 == 1) {
- handle_positions.pop_last();
- }
- else if (nurbs_positions_size % 3 == 2) {
- const int last_index = nurbs_positions_size - 1;
- handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
- }
- }
- else {
+
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL;
if (is_periodic) {
handle_positions.append(nurbs_positions[1] +
@@ -134,11 +206,15 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
handle_positions.append(nurbs_positions[1]);
}
- const int segments_size = nurbs_positions_size - 1;
- const bool ignore_interior_segment = segments_size == 3 && is_periodic == false;
+
+ /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
+ * endpoints, midpoints or 1/3 of the distance of a hull segment. */
+ const int segments_num = nurbs_positions_num - 1;
+ const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
if (ignore_interior_segment == false) {
- const float mid_offset = (float)(segments_size - 1) / 2.0f;
- for (const int i : IndexRange(1, segments_size - 2)) {
+ const float mid_offset = (float)(segments_num - 1) / 2.0f;
+ for (const int i : IndexRange(1, segments_num - 2)) {
+ /* Divisor can have values: 1, 2 or 3. */
const int divisor = is_periodic ?
3 :
std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f));
@@ -151,7 +227,8 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
}
}
}
- const int last_index = nurbs_positions_size - 1;
+
+ const int last_index = nurbs_positions_num - 1;
if (is_periodic) {
handle_positions.append(
nurbs_positions[last_index - 1] +
@@ -162,199 +239,433 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
}
}
+ else {
+ for (const int i : IndexRange(nurbs_positions_num)) {
+ if (i % 3 == 1) {
+ continue;
+ }
+ handle_positions.append(nurbs_positions[i]);
+ }
+ if (nurbs_positions_num % 3 == 1) {
+ handle_positions.pop_last();
+ }
+ else if (nurbs_positions_num % 3 == 2) {
+ const int last_index = nurbs_positions_num - 1;
+ handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
+ }
+ }
+
return handle_positions;
}
-static Array<float3> create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
- const Span<float3> handle_positions,
- const KnotsMode knots_mode)
+static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
+ const Span<float3> handle_positions,
+ const KnotsMode knots_mode,
+ MutableSpan<float3> bezier_positions)
{
- if (knots_mode == NURBS_KNOT_MODE_BEZIER) {
- /* Every third NURBS position (starting from index 1) should be converted to Bezier position */
- const int scale = 3;
- const int offset = 1;
- Array<float3> bezier_positions((nurbs_positions.size() + offset) / scale);
- scale_input_assign(nurbs_positions, scale, offset, bezier_positions.as_mutable_span());
- return bezier_positions;
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ for (const int i : bezier_positions.index_range()) {
+ bezier_positions[i] = math::interpolate(
+ handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
+ }
}
-
- Array<float3> bezier_positions(handle_positions.size() / 2);
- for (const int i : IndexRange(bezier_positions.size())) {
- bezier_positions[i] = math::interpolate(
- handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
+ else {
+ /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
+ scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
}
- return bezier_positions;
}
-static SplinePtr convert_to_poly_spline(const Spline &input)
+static int to_bezier_size(const CurveType src_type,
+ const bool cyclic,
+ const KnotsMode knots_mode,
+ const int src_size)
{
- std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
- output->resize(input.positions().size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- Spline::copy_base_settings(input, *output);
- output->attributes = input.attributes;
- return output;
+ switch (src_type) {
+ case CURVE_TYPE_NURBS: {
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ return cyclic ? src_size : src_size - 2;
+ }
+ return (src_size + 1) / 3;
+ }
+ default:
+ return src_size;
+ }
}
-static SplinePtr poly_to_nurbs(const Spline &input)
+static int to_nurbs_size(const CurveType src_type, const int src_size)
{
- std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
- output->resize(input.positions().size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- output->weights().fill(1.0f);
- output->set_resolution(12);
- output->set_order(4);
- Spline::copy_base_settings(input, *output);
- output->knots_mode = NURBS_KNOT_MODE_BEZIER;
- output->attributes = input.attributes;
- return output;
+ switch (src_type) {
+ case CURVE_TYPE_BEZIER:
+ case CURVE_TYPE_CATMULL_ROM:
+ return src_size * 3;
+ default:
+ return src_size;
+ }
}
-static SplinePtr bezier_to_nurbs(const Spline &input)
+static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<int> sizes)
{
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
- std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
- output->resize(input.size() * 3);
-
- scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
- scale_output_assign(input.radii(), 3, 0, output->radii());
- scale_output_assign(input.tilts(), 3, 0, output->tilts());
-
- scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
- scale_output_assign(input.radii(), 3, 1, output->radii());
- scale_output_assign(input.tilts(), 3, 1, output->tilts());
-
- scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
- scale_output_assign(input.radii(), 3, 2, output->radii());
- scale_output_assign(input.tilts(), 3, 2, output->tilts());
-
- Spline::copy_base_settings(input, *output);
- output->weights().fill(1.0f);
- output->set_resolution(12);
- output->set_order(4);
- output->set_cyclic(input.is_cyclic());
- output->knots_mode = NURBS_KNOT_MODE_BEZIER;
- output->attributes.reallocate(output->size());
- copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
- scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
- scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
- });
+ threading::parallel_for(curves.curves_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ sizes[i] = curves.points_for_curve(i).size();
+ }
});
- return output;
}
-static SplinePtr poly_to_bezier(const Spline &input)
+struct GenericAttributes : NonCopyable, NonMovable {
+ Vector<GSpan> src;
+ Vector<GMutableSpan> dst;
+
+ Vector<OutputAttribute> attributes;
+};
+
+static void retrieve_generic_point_attributes(const CurveComponent &src_component,
+ CurveComponent &dst_component,
+ GenericAttributes &attributes)
{
- std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
- output->resize(input.size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
- output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
- output->set_resolution(12);
- Spline::copy_base_settings(input, *output);
- output->attributes = input.attributes;
- return output;
+ src_component.attribute_foreach(
+ [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (meta_data.domain != ATTR_DOMAIN_POINT) {
+ /* Curve domain attributes are all copied directly to the result in one step. */
+ return true;
+ }
+ if (src_component.attribute_is_builtin(id)) {
+ if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
+ return true;
+ }
+ }
+
+ GVArray src_attribute = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
+ BLI_assert(src_attribute);
+ attributes.src.append(src_attribute.get_internal_span());
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ id, ATTR_DOMAIN_POINT, meta_data.data_type);
+ attributes.dst.append(dst_attribute.as_span());
+ attributes.attributes.append(std::move(dst_attribute));
+
+ return true;
+ });
}
-static SplinePtr nurbs_to_bezier(const Spline &input)
+static void convert_to_bezier(const CurveComponent &src_component,
+ const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ CurveComponent &dst_component,
+ bke::CurvesGeometry &dst_curves)
{
- const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
- Span<float3> nurbs_positions;
- Vector<float3> nurbs_positions_vector;
- KnotsMode knots_mode;
- if (nurbs_spline.is_cyclic()) {
- nurbs_positions_vector = nurbs_spline.positions();
- nurbs_positions_vector.append(nurbs_spline.positions()[0]);
- nurbs_positions_vector.append(nurbs_spline.positions()[1]);
- nurbs_positions = nurbs_positions_vector;
- knots_mode = NURBS_KNOT_MODE_NORMAL;
+ const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
+ const VArray<int8_t> src_types = src_curves.curve_types();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+ const Span<float3> src_positions = src_curves.positions();
+
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+ retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ dst_offsets[i] = to_bezier_size(
+ CurveType(src_types[i]), src_cyclic[i], KnotsMode(src_knot_modes[i]), dst_offsets[i]);
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ GenericAttributes attributes;
+ retrieve_generic_point_attributes(src_component, dst_component, attributes);
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+ MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
+ MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
+ MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
+ MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
+ MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
+
+ auto catmull_rom_to_bezier = [&](IndexMask selection) {
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ catmull_rom_to_bezier_handles(src_positions.slice(src_points),
+ src_cyclic[i],
+ dst_handles_l.slice(dst_points),
+ dst_handles_r.slice(dst_points));
+ }
+ });
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto poly_to_bezier = [&](IndexMask selection) {
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
+ dst_curves.calculate_bezier_auto_handles();
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto bezier_to_bezier = [&](IndexMask selection) {
+ const VArray_Span<int8_t> src_types_l = src_curves.handle_types_left();
+ const VArray_Span<int8_t> src_types_r = src_curves.handle_types_right();
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_l, dst_handles_l);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_r, dst_handles_r);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_l, dst_types_l);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_r, dst_types_r);
+
+ dst_curves.calculate_bezier_auto_handles();
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto nurbs_to_bezier = [&](IndexMask selection) {
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
+ bke::curves::fill_points<float>(dst_curves, selection, 0.0f, dst_weights);
+
+ threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ const Span<float3> src_curve_positions = src_positions.slice(src_points);
+
+ KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
+ Span<float3> nurbs_positions = src_curve_positions;
+ Vector<float3> nurbs_positions_vector;
+ if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
+ * second point which will act as a skeleton for placing Bezier handles. */
+ nurbs_positions_vector.extend(src_curve_positions);
+ nurbs_positions_vector.append(src_curve_positions[0]);
+ nurbs_positions_vector.append(src_curve_positions[1]);
+ nurbs_positions = nurbs_positions_vector;
+ knots_mode = NURBS_KNOT_MODE_NORMAL;
+ }
+
+ const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
+ knots_mode);
+
+ scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
+ scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
+
+ create_nurbs_to_bezier_positions(
+ nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
+ }
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points),
+ KnotsMode(src_knot_modes[i]),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ selection,
+ catmull_rom_to_bezier,
+ poly_to_bezier,
+ bezier_to_bezier,
+ nurbs_to_bezier);
+
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range());
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
- else {
- nurbs_positions = nurbs_spline.positions();
- knots_mode = nurbs_spline.knots_mode;
+
+ for (OutputAttribute &attribute : attributes.attributes) {
+ attribute.save();
}
- const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
- knots_mode);
- BLI_assert(handle_positions.size() % 2 == 0);
- const Array<float3> bezier_positions = create_nurbs_to_bezier_positions(
- nurbs_positions, handle_positions.as_span(), knots_mode);
- BLI_assert(handle_positions.size() == bezier_positions.size() * 2);
-
- std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
- output->resize(bezier_positions.size());
- output->positions().copy_from(bezier_positions);
- nurbs_to_bezier_assign(nurbs_spline.radii(), output->radii(), knots_mode);
- nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode);
- scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left());
- scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right());
- output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
- output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
- output->set_resolution(nurbs_spline.resolution());
- Spline::copy_base_settings(nurbs_spline, *output);
- output->attributes.reallocate(output->size());
- copy_attributes(nurbs_spline, *output, [knots_mode](GSpan src, GMutableSpan dst) {
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
- });
- });
- return output;
}
-static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+static void convert_to_nurbs(const CurveComponent &src_component,
+ const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ CurveComponent &dst_component,
+ bke::CurvesGeometry &dst_curves)
{
- switch (input.type()) {
- case CURVE_TYPE_BEZIER:
- return input.copy();
- case CURVE_TYPE_POLY:
- return poly_to_bezier(input);
- case CURVE_TYPE_NURBS:
- if (input.size() < 4) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("NURBS must have minimum of 4 points for Bezier Conversion"));
- return input.copy();
+ const VArray<int8_t> src_types = src_curves.curve_types();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+ const Span<float3> src_positions = src_curves.positions();
+
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+ retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), dst_offsets[i]);
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ GenericAttributes attributes;
+ retrieve_generic_point_attributes(src_component, dst_component, attributes);
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ auto fill_weights_if_necessary = [&](const IndexMask selection) {
+ if (!src_curves.nurbs_weights().is_empty()) {
+ bke::curves::fill_points(dst_curves, selection, 1.0f, dst_curves.nurbs_weights_for_write());
+ }
+ };
+
+ auto catmull_rom_to_nurbs = [&](IndexMask selection) {
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
+ fill_weights_if_necessary(selection);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ catmull_rom_to_nurbs_positions(
+ src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
}
- return nurbs_to_bezier(input);
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- return {};
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ auto poly_to_nurbs = [&](IndexMask selection) {
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ fill_weights_if_necessary(selection);
+
+ /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
+ * start/end. */
+ if (src_cyclic.is_single()) {
+ dst_curves.nurbs_knots_modes_for_write().fill_indices(
+ selection,
+ src_cyclic.get_internal_single() ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT);
+ }
+ else {
+ VArray_Span<bool> cyclic{src_cyclic};
+ MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
+ }
+ });
+ }
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ attributes.src[i_attribute],
+ attributes.dst[i_attribute]);
+ }
+ };
+
+ auto bezier_to_nurbs = [&](IndexMask selection) {
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
+ fill_weights_if_necessary(selection);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_positions_to_nurbs(src_positions.slice(src_points),
+ src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ dst_positions.slice(dst_points));
+ }
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ auto nurbs_to_nurbs = [&](IndexMask selection) {
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+
+ if (!src_curves.nurbs_weights().is_empty()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ src_curves.nurbs_weights(),
+ dst_curves.nurbs_weights_for_write());
+ }
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ attributes.src[i_attribute],
+ attributes.dst[i_attribute]);
}
+ };
+
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ selection,
+ catmull_rom_to_nurbs,
+ poly_to_nurbs,
+ bezier_to_nurbs,
+ nurbs_to_nurbs);
+
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range());
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
- BLI_assert_unreachable();
- return {};
-}
-static SplinePtr convert_to_nurbs(const Spline &input)
-{
- switch (input.type()) {
- case CURVE_TYPE_NURBS:
- return input.copy();
- case CURVE_TYPE_BEZIER:
- return bezier_to_nurbs(input);
- case CURVE_TYPE_POLY:
- return poly_to_nurbs(input);
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- return {};
+ for (OutputAttribute &attribute : attributes.attributes) {
+ attribute.save();
}
- BLI_assert_unreachable();
- return {};
}
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSplineType &storage = node_storage(params.node());
- const GeometryNodeSplineType dst_type = (const GeometryNodeSplineType)storage.spline_type;
+ const CurveType dst_type = CurveType(storage.spline_type);
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
@@ -363,45 +674,58 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
+ const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
+ const Curves &src_curves_id = *src_component.get_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
+ if (src_curves.is_single_type(dst_type)) {
+ return;
+ }
- const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *curve_component->get_for_read());
- GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
- const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
-
- Span<SplinePtr> src_splines = curve->splines();
-
- 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);
-
- std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
- new_curve->resize(src_splines.size());
-
- threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) {
- for (const int i : range) {
- if (selection[i]) {
- switch (dst_type) {
- case GEO_NODE_SPLINE_TYPE_POLY:
- new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]);
- break;
- case GEO_NODE_SPLINE_TYPE_BEZIER:
- new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params);
- break;
- case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]);
- break;
- }
- }
- else {
- new_curve->splines()[i] = src_splines[i]->copy();
- }
- }
- });
- new_curve->attributes = curve->attributes;
- geometry_set.replace_curves(curve_eval_to_curves(*new_curve));
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ const int domain_size = src_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ if (!conversion_can_change_point_num(dst_type)) {
+ CurveComponent &dst_component = geometry_set.get_component_for_write<CurveComponent>();
+ Curves &curves_id = *dst_component.get_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ curves.fill_curve_types(selection, dst_type);
+ curves.remove_attributes_based_on_types();
+ return;
+ }
+
+ Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
+ bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+ CurveComponent dst_component;
+ dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ /* Directly copy curve attributes, since they stay the same (except for curve types). */
+ CustomData_copy(&src_curves.curve_data,
+ &dst_curves.curve_data,
+ CD_MASK_ALL,
+ CD_DUPLICATE,
+ src_curves.curves_num());
+
+ dst_curves.fill_curve_types(selection, dst_type);
+
+ switch (dst_type) {
+ case CURVE_TYPE_CATMULL_ROM:
+ case CURVE_TYPE_POLY:
+ /* Converting to Catmull Rom curves or poly curves should be handled
+ * above by the optimization to avoid changing the point count. */
+ BLI_assert_unreachable();
+ break;
+ case CURVE_TYPE_BEZIER:
+ convert_to_bezier(src_component, src_curves, selection, dst_component, dst_curves);
+ break;
+ case CURVE_TYPE_NURBS:
+ convert_to_nurbs(src_component, src_curves, selection, dst_component, dst_curves);
+ break;
+ }
+
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", std::move(geometry_set));