From e7a800c52f0766b372bd3a035432eade165e4cb3 Mon Sep 17 00:00:00 2001 From: Angus Stanton Date: Sun, 18 Jul 2021 14:05:57 -0400 Subject: Geometry Nodes: Curve Trim Node This node implements shortening each spline in the curve based on either a length from the start of each spline, or a factor of the total length of each spline, similar to the "Start & End Mapping" panel of curve properties. For Bezier curves, the first and last control points are adjusted to maintain the shape of the curve, but NURB splines are currently implicitly converted to poly splines. The node is implemented to avoid copying where possible, so it outputs a changed version of the input curve rather than a new one. Differential Revision: https://developer.blender.org/D11901 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 10 + source/blender/makesrna/intern/rna_nodetree.c | 26 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/geometry/nodes/node_geo_curve_trim.cc | 407 +++++++++++++++++++++ 9 files changed, 449 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 1d616947db6..18e39a4d727 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -507,6 +507,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshToCurve"), NodeItem("GeometryNodeCurveToPoints"), NodeItem("GeometryNodeCurveEndpoints"), + NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), NodeItem("GeometryNodeCurveReverse"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 63b11a194ff..9b8eca77e6b 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1464,6 +1464,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 #define GEO_NODE_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 +#define GEO_NODE_CURVE_TRIM 1071 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 85d30fc8c8b..3d5973b1d5e 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5120,6 +5120,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_subdivide(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); + register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 94176d946b3..2b8e48557b6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1385,6 +1385,11 @@ typedef struct NodeGeometryCurveSubdivide { uint8_t cuts_type; } NodeGeometryCurveSubdivide; +typedef struct NodeGeometryCurveTrim { + /* GeometryNodeCurveInterpolateMode. */ + uint8_t mode; +} NodeGeometryCurveTrim; + typedef struct NodeGeometryCurveToPoints { /* GeometryNodeCurveSampleMode. */ uint8_t mode; @@ -1944,6 +1949,11 @@ typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, } GeometryNodeCurveSampleMode; +typedef enum GeometryNodeCurveInterpolateMode { + GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0, + GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1, +} GeometryNodeCurveInterpolateMode; + typedef enum GeometryNodeAttributeTransferMapMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 4712f4a0a0b..6998a7d0afe 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10039,6 +10039,32 @@ static void def_geo_curve_to_points(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_trim(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_INTERPOLATE_FACTOR, + "FACTOR", + 0, + "Factor", + "Find the endpoint positions using a factor of each spline's length"}, + {GEO_NODE_CURVE_INTERPOLATE_LENGTH, + "LENGTH", + 0, + "Length", + "Find the endpoint positions using a length from the start of each spline"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveTrim", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to find endpoint positions for the trimmed spline"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_transfer(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index dc19508be04..130032ccf71 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc + geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index ad3a838f4c0..29a1d2cc853 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -65,6 +65,7 @@ void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); +void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 73d4a002991..42a0454d3e6 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -302,6 +302,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRA DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc new file mode 100644 index 00000000000..91dd4af0de3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -0,0 +1,407 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_trim_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10000.0f, PROP_DISTANCE}, + {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_trim_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), + __func__); + + data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR; + node->storage = data; +} + +static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage; + const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) + node_storage.mode; + + bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *end_fac = start_fac->next; + bNodeSocket *start_len = end_fac->next; + bNodeSocket *end_len = start_len->next; + + nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); + nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); + nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); + nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); +} + +namespace blender::nodes { + +template +static void shift_slice_to_start(MutableSpan data, const int start_index, const int size) +{ + BLI_assert(start_index + size - 1 <= data.size()); + memcpy(data.data(), &data[start_index], sizeof(T) * size); +} + +/* Shift slice to start of span and modifies start and end data. */ +template +static void linear_trim_data(const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup, + MutableSpan input_data) +{ + const int size = end_lookup.next_evaluated_index - start_lookup.evaluated_index + 1; + + if (start_lookup.evaluated_index > 0) { + shift_slice_to_start(input_data, start_lookup.evaluated_index, size); + } + + const T start_data = blender::attribute_math::mix2( + start_lookup.factor, input_data.first(), input_data[1]); + const T end_data = blender::attribute_math::mix2( + end_lookup.factor, input_data[size - 2], input_data[size - 1]); + + input_data.first() = start_data; + input_data[size - 1] = end_data; +} + +/* Identical operation as #linear_trim_data, but opy data to a new MutableSpan rather than + * modifying the original data. */ +template +static void linear_trim_to_output_data(const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup, + Span input_data, + MutableSpan output_data) +{ + const int size = end_lookup.next_evaluated_index - start_lookup.evaluated_index + 1; + + const T start_data = blender::attribute_math::mix2( + start_lookup.factor, + input_data[start_lookup.evaluated_index], + input_data[start_lookup.next_evaluated_index]); + const T end_data = blender::attribute_math::mix2(end_lookup.factor, + input_data[end_lookup.evaluated_index], + input_data[end_lookup.next_evaluated_index]); + + output_data.copy_from(input_data.slice(start_lookup.evaluated_index, size)); + output_data.first() = start_data; + output_data.last() = end_data; +} + +/* Look up the control points to the left and right of factor, and get the factor between them. */ +static Spline::LookupResult lookup_control_point_position(Spline::LookupResult lookup, + Span control_point_offsets) +{ + const int *left_offset = std::lower_bound( + control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index); + const int index = left_offset - control_point_offsets.begin(); + const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index; + const int right = left + 1; + + const float factor = std::clamp( + (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) / + (control_point_offsets[right] - control_point_offsets[left]), + 0.0f, + 1.0f); + + return Spline::LookupResult{left, right, factor}; +} + +static void trim_poly_spline(Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + const int size = end_lookup.next_evaluated_index - start_lookup.evaluated_index + 1; + + linear_trim_data(start_lookup, end_lookup, spline.positions()); + linear_trim_data(start_lookup, end_lookup, spline.radii()); + linear_trim_data(start_lookup, end_lookup, spline.tilts()); + + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { + std::optional src = spline.attributes.get_for_write(name); + BLI_assert(src); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + linear_trim_data(start_lookup, end_lookup, src->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + spline.resize(size); +} + +/** + * Trim NURB splines by converting to a poly spline. + */ +static PolySpline trim_nurbs_spline(const Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + const int size = end_lookup.next_evaluated_index - start_lookup.evaluated_index + 1; + + /* Create poly spline and copy trimmed data to it. */ + PolySpline new_spline; + new_spline.resize(size); + + /* Copy generic attribute data. */ + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional src = spline.attributes.get_for_read(name); + BLI_assert(src); + if (!new_spline.attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional dst = new_spline.attributes.get_for_write(name); + BLI_assert(dst); + + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed eval_data = spline.interpolate_to_evaluated(src->typed()); + linear_trim_to_output_data( + start_lookup, end_lookup, eval_data->get_internal_span(), dst->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + linear_trim_to_output_data( + start_lookup, end_lookup, spline.evaluated_positions(), new_spline.positions()); + + GVArray_Typed evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + linear_trim_to_output_data( + start_lookup, end_lookup, evaluated_radii->get_internal_span(), new_spline.radii()); + + GVArray_Typed evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + linear_trim_to_output_data( + start_lookup, end_lookup, evaluated_tilts->get_internal_span(), new_spline.tilts()); + + return new_spline; +} + +/** + * Trim Bezier splines by adjusting the first and last handles + * and control points to maintain the original shape. + */ +static void trim_bezier_spline(Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + BezierSpline &bezier_spline = static_cast(spline); + Span control_offsets = bezier_spline.control_point_offsets(); + + const Spline::LookupResult start_control_lookup = lookup_control_point_position(start_lookup, + control_offsets); + Spline::LookupResult end_control_lookup = lookup_control_point_position(end_lookup, + control_offsets); + + /* The number of control points in the resulting spline. */ + const int size = end_control_lookup.next_evaluated_index - start_control_lookup.evaluated_index + + 1; + + /* Trim the spline attributes. Done before end_control_lookup.factor recalculation as it needs + * the original end_control_lookup.factor value. */ + linear_trim_data(start_control_lookup, end_control_lookup, bezier_spline.radii()); + linear_trim_data(start_control_lookup, end_control_lookup, bezier_spline.tilts()); + + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { + std::optional src = spline.attributes.get_for_write(name); + BLI_assert(src); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + linear_trim_data(start_control_lookup, end_control_lookup, src->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + /* Recalculate end_control_lookup.factor if the size is two, because the adjustment in the + * position of the control point of the spline to the left of the new end point will change the + * factor between them. */ + if (size == 2) { + if (start_lookup.factor == 1.0f) { + end_control_lookup.factor = 0.0f; + } + else { + end_control_lookup.factor = (end_lookup.evaluated_index + end_lookup.factor - + (start_lookup.evaluated_index + start_lookup.factor)) / + (control_offsets[end_control_lookup.next_evaluated_index] - + (start_lookup.evaluated_index + start_lookup.factor)); + end_control_lookup.factor = std::clamp(end_control_lookup.factor, 0.0f, 1.0f); + } + } + + BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion( + start_control_lookup.evaluated_index, + start_control_lookup.next_evaluated_index, + start_control_lookup.factor); + + /* Update the start control point parameters so that they are used in calculating the new end + * point. */ + bezier_spline.positions()[start_control_lookup.evaluated_index] = start_point.position; + bezier_spline.handle_positions_right()[start_control_lookup.evaluated_index] = + start_point.right_handle; + bezier_spline.handle_positions_left()[start_control_lookup.next_evaluated_index] = + start_point.handle_next; + + const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( + end_control_lookup.evaluated_index, + end_control_lookup.next_evaluated_index, + end_control_lookup.factor); + + /* If size is two, then the start point right handle needs to change to reflect the end point + * previous handle update. */ + if (size == 2) { + start_point.right_handle = end_point.handle_prev; + } + + /* Shift control point position data to start at beginning of array. */ + if (start_control_lookup.evaluated_index > 0) { + shift_slice_to_start(bezier_spline.positions(), start_control_lookup.evaluated_index, size); + shift_slice_to_start( + bezier_spline.handle_positions_left(), start_control_lookup.evaluated_index, size); + shift_slice_to_start( + bezier_spline.handle_positions_right(), start_control_lookup.evaluated_index, size); + } + + bezier_spline.positions().first() = start_point.position; + bezier_spline.positions()[size - 1] = end_point.position; + + bezier_spline.handle_positions_left().first() = start_point.left_handle; + bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + + bezier_spline.handle_positions_right().first() = start_point.right_handle; + bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + + /* If there is at least one control point between the endpoints, update the control + * point handle to the right of the start point and to the left of the end point. */ + if (size > 2) { + bezier_spline.handle_positions_left()[start_control_lookup.next_evaluated_index - + start_control_lookup.evaluated_index] = + start_point.handle_next; + bezier_spline.handle_positions_right()[end_control_lookup.evaluated_index - + start_control_lookup.evaluated_index] = + end_point.handle_prev; + } + + bezier_spline.resize(size); +} + +static void geo_node_curve_trim_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; + const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) + node_storage.mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", std::move(geometry_set)); + return; + } + + CurveComponent &curve_component = geometry_set.get_component_for_write(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan splines = curve.splines(); + + const float start = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? + params.extract_input("Start") : + params.extract_input("Start_001"); + const float end = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? + params.extract_input("End") : + params.extract_input("End_001"); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + Spline &spline = *splines[i]; + + /* Currently this node doesn't support cyclic splines, it could in the future though. */ + if (spline.is_cyclic()) { + continue; + } + + /* Return a spline with one point instead of implicitly + * reversing the sline or switching the parameters. */ + if (end < start) { + spline.resize(1); + continue; + } + + const Spline::LookupResult start_lookup = + (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); + const Spline::LookupResult end_lookup = + (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); + + switch (spline.type()) { + case Spline::Type::Bezier: + trim_bezier_spline(spline, start_lookup, end_lookup); + break; + case Spline::Type::Poly: + trim_poly_spline(spline, start_lookup, end_lookup); + break; + case Spline::Type::NURBS: + splines[i] = std::make_unique( + trim_nurbs_spline(spline, start_lookup, end_lookup)); + break; + } + splines[i]->mark_cache_invalid(); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_trim() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_trim_in, geo_node_curve_trim_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; + ntype.draw_buttons = geo_node_curve_trim_layout; + node_type_storage( + &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_trim_init); + node_type_update(&ntype, geo_node_curve_trim_update); + nodeRegisterType(&ntype); +} -- cgit v1.2.3