Geometry Nodes: Trim curve node selection input and corrections

Correct trim for cyclical curves mentioned in T101379, splitting the
curves if the start/endpoint is at the 'loop point'.

Correct implementation based on comments in D14481, request was made to
use 'foreach_curve_by_type' to computing the point lookups.

Included corrections from D16066 as it may not be a adopted solution.

Exposed selection input by adding it as input to the node.
Note: This is disabled for 3.4 to avoid making UI changes in Bcon3.

Differential Revision: https://developer.blender.org/D16161
This commit is contained in:
Mattias Fredriksson 2022-11-09 10:50:51 -06:00 committed by Hans Goudey
parent d01187c963
commit 11f6c65e61
Notes: blender-bot 2023-02-14 03:59:42 +01:00
Referenced by issue #103632, Regression: Trim Curve at 100% Causing Jumping of vertices away from the original position
Referenced by issue #101379, Regression: Trim curve make spline disapear.
5 changed files with 667 additions and 759 deletions

View File

@ -67,86 +67,228 @@ struct CurvePoint : public CurveSegment {
};
/**
* Cyclical index range. Iterates the interval [start, end).
* Cyclical index range. Allows iteration over a plain 'IndexRange' interval on form [start, end)
* while also supporting treating the underlying array as a cyclic array where the last index is
* followed by the first nidex in the 'cyclical' range. The cyclical index range can then be
* considered a combination of the intervals separated by the last index of the underlying array,
* namely [start, range_size) and [0, end) where start/end is the indices iterated between and
* range_size is the size of the underlying array. To cycle the underlying array the interval
* [0, range_size) can be iterated over an arbitrary amount of times inbetween.
*/
class IndexRangeCyclic {
/* Index to the start and end of the iterated range.
*/
int64_t start_ = 0;
int64_t end_ = 0;
/* Index for the start and end of the entire iterable range which contains the iterated range
* (e.g. the point range for an individual spline/curve within the entire Curves point domain).
int start_ = 0;
int end_ = 0;
/* Size of the underlying iterable range.
*/
int64_t range_start_ = 0;
int64_t range_end_ = 0;
int range_size_ = 0;
/* Number of times the range end is passed when the range is iterated.
*/
int64_t cycles_ = 0;
constexpr IndexRangeCyclic(int64_t begin,
int64_t end,
int64_t iterable_range_start,
int64_t iterable_range_end,
int64_t cycles)
: start_(begin),
end_(end),
range_start_(iterable_range_start),
range_end_(iterable_range_end),
cycles_(cycles)
{
}
int cycles_ = 0;
public:
constexpr IndexRangeCyclic() = default;
~IndexRangeCyclic() = default;
constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range, int64_t cycles)
: start_(start),
end_(end),
range_start_(iterable_range.first()),
range_end_(iterable_range.one_after_last()),
cycles_(cycles)
constexpr IndexRangeCyclic(const int start,
const int end,
const int iterable_range_size,
const int cycles)
: start_(start), end_(end), range_size_(iterable_range_size), cycles_(cycles)
{
}
/**
* Create an iterator over the cyclical interval [start_index, end_index).
*/
constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range)
constexpr IndexRangeCyclic(const int start, const int end, const int iterable_range_size)
: start_(start),
end_(end == iterable_range.one_after_last() ? iterable_range.first() : end),
range_start_(iterable_range.first()),
range_end_(iterable_range.one_after_last()),
end_(end == iterable_range_size ? 0 : end),
range_size_(iterable_range_size),
cycles_(end < start)
{
}
/**
* Increment the range by adding the given number of indices to the beginning of the range.
* Create a cyclical iterator of the specified size.
*
* \param start_point: Point on the curve that define the starting point of the interval.
* \param iterator_size: Number of elements to iterate (size of the iterated cyclical range).
* \param iterable_range_size: Size of the underlying range (superset to the cyclical range).
*/
constexpr IndexRangeCyclic push_forward(int n)
static IndexRangeCyclic get_range_from_size(const int start_index,
const int iterator_size,
const int iterable_range_size)
{
BLI_assert(n >= 0);
int64_t nstart = start_ - n;
int64_t cycles = cycles_;
if (nstart < range_start_) {
BLI_assert(start_index >= 0);
BLI_assert(iterator_size >= 0);
BLI_assert(iterable_range_size > 0);
const int num_until_loop = iterable_range_size - start_index;
if (iterator_size < num_until_loop) {
return IndexRangeCyclic(start_index, start_index + iterator_size, iterable_range_size, 0);
}
cycles += (int64_t)(n / (range_end_ - range_start_)) + (end_ < nstart) - (end_ < start_);
}
return {nstart, end_, range_start_, range_end_, cycles};
const int num_remaining = iterator_size - num_until_loop;
const int num_full_cycles = num_remaining /
iterable_range_size; /* Integer division (rounded down). */
const int end_index = num_remaining - num_full_cycles * iterable_range_size;
return IndexRangeCyclic(start_index, end_index, iterable_range_size, num_full_cycles + 1);
}
/**
* Increment the range by adding the given number of indices to the end of the range.
* Create a cyclical iterator for all control points within the interval [start_point, end_point]
* including any control point at the start or end point.
*
* \param start_point: Point on the curve that define the starting point of the interval.
* \param end_point: Point on the curve that define the end point of the interval (included).
* \param iterable_range_size: Size of the underlying range (superset to the cyclical range).
*/
constexpr IndexRangeCyclic push_backward(int n)
static IndexRangeCyclic get_range_between_endpoints(const CurvePoint start_point,
const CurvePoint end_point,
const int iterable_range_size)
{
BLI_assert(iterable_range_size > 0);
const int start_index = start_point.parameter == 0.0 ? start_point.index :
start_point.next_index;
int end_index = end_point.parameter == 0.0 ? end_point.index : end_point.next_index;
int cycles;
if (end_point.is_controlpoint()) {
BLI_assert(end_index < iterable_range_size);
++end_index;
if (end_index == iterable_range_size) {
end_index = 0;
}
/* end_point < start_point but parameter is irrelevant (end_point is controlpoint), and loop
* when equal due to increment. */
cycles = end_index <= start_index;
}
else {
cycles = end_point < start_point || end_index < start_index;
}
return IndexRangeCyclic(start_index, end_index, iterable_range_size, cycles);
}
/**
* Next index within the iterable range.
*/
template<typename IndexT> constexpr IndexT next_index(const IndexT index, const bool cyclic)
{
static_assert((is_same_any_v<IndexT, int, int>), "Expected signed integer type.");
const IndexT next_index = index + 1;
if (next_index == this->size_range()) {
return cyclic ? 0 : index;
}
return next_index;
}
/**
* Previous index within the iterable range.
*/
template<typename IndexT> constexpr IndexT previous_index(const IndexT index, const bool cyclic)
{
static_assert((is_same_any_v<IndexT, int, int64_t>), "Expected signed integer type.");
const IndexT prev_index = index - 1;
if (prev_index < 0) {
return cyclic ? this->size_range() - 1 : 0;
}
return prev_index;
}
/**
* Increment the range by adding `n` loops to the range. This invokes undefined behavior when n
* is negative.
*/
constexpr IndexRangeCyclic push_loop(const int n = 1) const
{
return {this->start_, this->end_, this->range_size_, this->cycles_ + n};
}
/**
* Increment the range by adding the given number of indices to the beginning of the iterated
* range. This invokes undefined behavior when n is negative.
*/
constexpr IndexRangeCyclic push_front(const int n = 1) const
{
BLI_assert(n >= 0);
int64_t new_end = end_ + n;
int64_t cycles = cycles_;
if (range_end_ <= new_end) {
cycles += (int64_t)(n / (range_end_ - range_start_)) + (new_end < start_) - (end_ < start_);
int new_start = this->start_ - n;
int num_cycles = this->cycles_;
if (new_start < 0) {
const int new_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_start + this->size_range() * new_cycles;
const bool underflow = remainder < 0;
new_start = remainder + (underflow ? this->size_range() : 0);
num_cycles += new_cycles + int(underflow);
}
return {start_, new_end, range_start_, range_end_, cycles};
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 ||
(new_start <= this->end_ || (this->end_ == 0 && new_start < this->size_range())));
return {new_start, this->end_, this->range_size_, num_cycles};
}
/**
* Increment the range by adding the given number of indices to the end of the iterated range.
* This invokes undefined behavior when n is negative.
*/
constexpr IndexRangeCyclic push_back(const int n = 1) const
{
BLI_assert(n >= 0);
int new_end = this->end_ + n;
int num_cycles = this->cycles_;
if (this->size_range() <= new_end) {
const int new_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_end - this->size_range() * new_cycles;
const bool overflow = remainder >= this->size_range();
new_end = remainder - (overflow ? this->size_range() : 0);
num_cycles += new_cycles + int(overflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 || (this->start_ <= new_end || new_end == 0));
return {this->start_, new_end, this->range_size_, num_cycles};
}
/**
* Returns a new range with n indices removed from the beginning of the range.
* This invokes undefined behavior.
*/
constexpr IndexRangeCyclic drop_front(const int n = 1) const
{
BLI_assert(n >= 0);
int new_start = this->start_ + n;
int num_cycles = this->cycles_;
if (this->size_range() <= new_start) {
const int dropped_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_start - this->size_range() * dropped_cycles;
const bool overflow = remainder >= this->size_range();
new_start = remainder - (overflow ? this->size_range() : 0);
num_cycles -= dropped_cycles + int(overflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 ||
(new_start <= this->end_ || (this->end_ == 0 && new_start < this->size_range())));
return {new_start, this->end_, this->range_size_, num_cycles};
}
/**
* Returns a new range with n indices removed from the end of the range.
* This invokes undefined behavior when n is negative or n is larger then the underlying range.
*/
constexpr IndexRangeCyclic drop_back(const int n = 1) const
{
BLI_assert(n >= 0);
int new_end = this->end_ - n;
int num_cycles = this->cycles_;
if (0 >= new_end) {
const int dropped_cycles = n / this->size_range(); /* Integer division (floor) */
const int remainder = new_end + this->size_range() * dropped_cycles;
const bool underflow = remainder < 0;
new_end = remainder + (underflow ? this->size_range() : 0);
num_cycles -= dropped_cycles + int(underflow);
}
BLI_assert(num_cycles >= 0);
BLI_assert(num_cycles > 0 || (this->start_ <= new_end || new_end == 0));
return {this->start_, new_end, this->range_size_, num_cycles};
}
/**
@ -154,7 +296,7 @@ class IndexRangeCyclic {
*/
constexpr IndexRange curve_range() const
{
return IndexRange(range_start_, total_size());
return IndexRange(0, this->size_range());
}
/**
@ -162,7 +304,7 @@ class IndexRangeCyclic {
*/
constexpr IndexRange range_before_loop() const
{
return IndexRange(start_, size_before_loop());
return IndexRange(this->start_, this->size_before_loop());
}
/**
@ -170,88 +312,104 @@ class IndexRangeCyclic {
*/
constexpr IndexRange range_after_loop() const
{
return IndexRange(range_start_, size_after_loop());
return IndexRange(0, this->size_after_loop());
}
/**
* Size of the entire iterable range.
* Number of elements in the underlying iterable range.
*/
constexpr int64_t total_size() const
constexpr int size_range() const
{
return range_end_ - range_start_;
return this->range_size_;
}
/**
* Number of elements between the first element in the range up to the last element in the curve.
*/
constexpr int64_t size_before_loop() const
constexpr int size_before_loop() const
{
return range_end_ - start_;
return this->range_size_ - this->start_;
}
/**
* Number of elements between the first element in the iterable range up to the last element in
* the range.
*/
constexpr int64_t size_after_loop() const
constexpr int size_after_loop() const
{
return end_ - range_start_;
return this->end_;
}
/**
* Get number of elements iterated by the cyclical index range.
* Number of elements iterated by the cyclical index range.
*/
constexpr int64_t size() const
constexpr int size() const
{
if (cycles_ > 0) {
return size_before_loop() + end_ + (cycles_ - 1) * (range_end_ - range_start_);
if (this->cycles_ > 0) {
return this->size_before_loop() + this->end_ + (this->cycles_ - 1) * this->range_size_;
}
else {
return end_ - start_;
return int(this->end_ - this->start_);
}
}
/**
* Return the number of times the iterator will cycle before ending.
*/
constexpr int64_t cycles() const
constexpr int cycles() const
{
return cycles_;
return this->cycles_;
}
constexpr int64_t first() const
constexpr int first() const
{
return start_;
return this->start_;
}
constexpr int64_t one_after_last() const
constexpr int last() const
{
return end_;
BLI_assert(this->size() > 0);
return int(this->end_ - 1);
}
constexpr int one_after_last() const
{
return this->end_;
}
constexpr bool operator==(const IndexRangeCyclic &other) const
{
return this->start_ == other.start_ && this->end_ == other.end_ &&
this->cycles_ == other.cycles_ && this->range_size_ == other.range_size_;
}
constexpr bool operator!=(const IndexRangeCyclic &other) const
{
return !this->operator==(other);
}
struct CyclicIterator; /* Forward declaration */
constexpr CyclicIterator begin() const
{
return CyclicIterator(range_start_, range_end_, start_, 0);
return CyclicIterator(this->range_size_, this->start_, 0);
}
constexpr CyclicIterator end() const
{
return CyclicIterator(range_start_, range_end_, end_, cycles_);
return CyclicIterator(this->range_size_, this->end_, this->cycles_);
}
struct CyclicIterator {
int64_t index_, begin_, end_, cycles_;
int index_, range_end_, cycles_;
constexpr CyclicIterator(int64_t range_begin, int64_t range_end, int64_t index, int64_t cycles)
: index_(index), begin_(range_begin), end_(range_end), cycles_(cycles)
constexpr CyclicIterator(const int range_end, const int index, const int cycles)
: index_(index), range_end_(range_end), cycles_(cycles)
{
BLI_assert(range_begin <= index && index <= range_end);
BLI_assert(0 <= index && index <= range_end);
}
constexpr CyclicIterator(const CyclicIterator &copy)
: index_(copy.index_), begin_(copy.begin_), end_(copy.end_), cycles_(copy.cycles_)
: index_(copy.index_), range_end_(copy.range_end_), cycles_(copy.cycles_)
{
}
~CyclicIterator() = default;
@ -261,37 +419,36 @@ class IndexRangeCyclic {
if (this == &copy) {
return *this;
}
index_ = copy.index_;
begin_ = copy.begin_;
end_ = copy.end_;
cycles_ = copy.cycles_;
this->index_ = copy.index_;
this->range_end_ = copy.range_end_;
this->cycles_ = copy.cycles_;
return *this;
}
constexpr CyclicIterator &operator++()
{
index_++;
if (index_ == end_) {
index_ = begin_;
cycles_++;
this->index_++;
if (this->index_ == this->range_end_) {
this->index_ = 0;
this->cycles_++;
}
return *this;
}
void increment(int64_t n)
void increment(const int n)
{
for (int i = 0; i < n; i++) {
++*this;
}
}
constexpr const int64_t &operator*() const
constexpr const int &operator*() const
{
return index_;
return this->index_;
}
constexpr bool operator==(const CyclicIterator &other) const
{
return index_ == other.index_ && cycles_ == other.cycles_;
return this->index_ == other.index_ && this->cycles_ == other.cycles_;
}
constexpr bool operator!=(const CyclicIterator &other) const
{
@ -386,6 +543,12 @@ IndexMask indices_for_type(const VArray<int8_t> &types,
const IndexMask selection,
Vector<int64_t> &r_indices);
void indices_for_each_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &counts,
const IndexMask selection,
std::array<IndexMask, CURVE_TYPES_NUM> &r_type_masks,
std::array<Vector<int64_t>, CURVE_TYPES_NUM> &r_type_indices);
void foreach_curve_by_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
IndexMask selection,
@ -394,6 +557,15 @@ void foreach_curve_by_type(const VArray<int8_t> &types,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn);
/**
* Same as 'by_type' but index mask for each curve type is pre-computed.
*/
void foreach_curve_by_type_mask(const std::array<IndexMask, CURVE_TYPES_NUM> &curve_type_mask,
FunctionRef<void(IndexMask)> catmull_rom_fn,
FunctionRef<void(IndexMask)> poly_fn,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn);
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -128,6 +128,18 @@ IndexMask indices_for_type(const VArray<int8_t> &types,
selection, 4096, r_indices, [&](const int index) { return types_span[index] == type; });
}
void indices_for_each_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &counts,
const IndexMask selection,
std::array<IndexMask, CURVE_TYPES_NUM> &r_type_masks,
std::array<Vector<int64_t>, CURVE_TYPES_NUM> &r_type_indices)
{
for (const int64_t curve_type : IndexRange(CURVE_TYPES_NUM)) {
r_type_masks[curve_type] = indices_for_type(
types, counts, CurveType(curve_type), selection, r_type_indices[curve_type]);
}
}
void foreach_curve_by_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &counts,
const IndexMask selection,
@ -150,4 +162,21 @@ void foreach_curve_by_type(const VArray<int8_t> &types,
call_if_not_empty(CURVE_TYPE_NURBS, nurbs_fn);
}
void foreach_curve_by_type_mask(const std::array<IndexMask, CURVE_TYPES_NUM> &curve_type_mask,
FunctionRef<void(IndexMask)> catmull_rom_fn,
FunctionRef<void(IndexMask)> poly_fn,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn)
{
auto call_if_not_empty = [&](const IndexMask curve_type_mask, FunctionRef<void(IndexMask)> fn) {
if (!curve_type_mask.is_empty()) {
fn(curve_type_mask);
}
};
call_if_not_empty(curve_type_mask[0], catmull_rom_fn);
call_if_not_empty(curve_type_mask[1], poly_fn);
call_if_not_empty(curve_type_mask[2], bezier_fn);
call_if_not_empty(curve_type_mask[3], nurbs_fn);
}
} // namespace blender::bke::curves

View File

@ -3,6 +3,7 @@
#pragma once
#include "BLI_span.hh"
#include "DNA_node_types.h"
#include "BKE_curves.hh"
#include "BKE_curves_utils.hh"
@ -16,21 +17,8 @@ namespace blender::geometry {
*/
bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
Span<bke::curves::CurvePoint> start_points,
Span<bke::curves::CurvePoint> end_points);
/**
* Find the point(s) and piecewise segment corresponding to the given distance along the length of
* the curve. Returns points on the evaluated curve for Catmull-Rom and NURBS splines.
*
* \param curves: Curve geometry to sample.
* \param lengths: Distance along the curve on form [0.0, length] to determine the point for.
* \param curve_indices: Curve index to lookup for each 'length', negative index are set to 0.
* \param normalized_factors: If true, 'lengths' are normalized to the interval [0.0, 1.0].
*/
Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves,
Span<float> lengths,
Span<int64_t> curve_indices,
bool normalized_factors);
const VArray<float> &starts,
const VArray<float> &ends,
GeometryNodeCurveSampleMode mode);
} // namespace blender::geometry

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
// b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
b.add_input<decl::Float>(N_("Start"))
.min(0.0f)
.max(1.0f)
@ -64,6 +65,7 @@ static void node_update(bNodeTree *ntree, bNode *node)
const NodeGeometryCurveTrim &storage = node_storage(*node);
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
// bNodeSocket *start_fac = static_cast<bNodeSocket *>(node->inputs.first)->next->next;
bNodeSocket *start_fac = static_cast<bNodeSocket *>(node->inputs.first)->next;
bNodeSocket *end_fac = start_fac->next;
bNodeSocket *start_len = end_fac->next;
@ -109,6 +111,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
Field<bool> &selection_field,
Field<float> &start_field,
Field<float> &end_field)
{
@ -123,41 +126,21 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.add(selection_field);
evaluator.add(start_field);
evaluator.add(end_field);
evaluator.evaluate();
const VArray<float> starts = evaluator.get_evaluated<float>(0);
const VArray<float> ends = evaluator.get_evaluated<float>(1);
const VArray<bool> cyclic = src_curves.cyclic();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
const VArray<float> starts = evaluator.get_evaluated<float>(1);
const VArray<float> ends = evaluator.get_evaluated<float>(2);
/* If node length input is on form [0, 1] instead of [0, length]*/
const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR;
/* Stack start + end field. */
Vector<float> length_factors(src_curves.curves_num() * 2);
Vector<int64_t> lookup_indices(src_curves.curves_num() * 2);
threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) {
for (const int64_t curve_i : curve_range) {
const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i];
length_factors[curve_i] = starts[curve_i];
length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] :
ends[curve_i];
lookup_indices[curve_i] = curve_i;
lookup_indices[curve_i + src_curves.curves_num()] = curve_i;
}
});
/* Create curve trim lookup table. */
Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points(
src_curves, length_factors, lookup_indices, normalized_length_lookup);
if (selection.is_empty()) {
return;
}
bke::CurvesGeometry dst_curves = geometry::trim_curves(
src_curves,
src_curves.curves_range().as_span(),
point_lookups.as_span().slice(0, src_curves.curves_num()),
point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num()));
src_curves, selection, starts, ends, mode);
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
@ -171,18 +154,20 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
// Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<bool> selection_field = fn::make_constant_field<bool>(true);
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
Field<float> end_field = params.extract_input<Field<float>>("End");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, start_field, end_field);
geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field);
});
}
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
Field<float> start_field = params.extract_input<Field<float>>("Start_001");
Field<float> end_field = params.extract_input<Field<float>>("End_001");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, start_field, end_field);
geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field);
});
}