Blender V5.0
node_geo_curve_topology_points_of_curve.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_curves.hh"
6
7#include "BLI_array_utils.hh"
8
10
12
14{
15 b.add_input<decl::Int>("Curve Index")
16 .implicit_field(NODE_DEFAULT_INPUT_INDEX_FIELD)
17 .description("The curve to retrieve data from. Defaults to the curve from the context")
18 .structure_type(StructureType::Field);
19 b.add_input<decl::Float>("Weights").supports_field().hide_value().description(
20 "Values used to sort the curve's points. Uses indices by default");
21 b.add_input<decl::Int>("Sort Index")
22 .min(0)
23 .supports_field()
24 .description("Which of the sorted points to output");
25 b.add_output<decl::Int>("Point Index")
26 .field_source_reference_all()
27 .description("A point of the curve, chosen by the sort index");
28 b.add_output<decl::Int>("Total").field_source().reference_pass({0}).description(
29 "The number of points in the curve");
30}
31
37static bool use_start_point_special_case(const Field<int> &curve_index,
38 const Field<int> &sort_index,
39 const Field<float> &sort_weights)
40{
41 if (!dynamic_cast<const fn::IndexFieldInput *>(&curve_index.node())) {
42 return false;
43 }
44 if (sort_index.node().depends_on_input() || sort_weights.node().depends_on_input()) {
45 return false;
46 }
47 return fn::evaluate_constant_field(sort_index) == 0;
48}
49
51 const Field<int> curve_index_;
52 const Field<int> sort_index_;
53 const Field<float> sort_weight_;
54
55 public:
56 PointsOfCurveInput(Field<int> curve_index, Field<int> sort_index, Field<float> sort_weight)
57 : bke::GeometryFieldInput(CPPType::get<int>(), "Point of Curve"),
58 curve_index_(std::move(curve_index)),
59 sort_index_(std::move(sort_index)),
60 sort_weight_(std::move(sort_weight))
61 {
63 }
64
66 const IndexMask &mask) const final
67 {
68 const bke::CurvesGeometry *curves_ptr = context.curves_or_strokes();
69 if (!curves_ptr) {
70 return {};
71 }
72 const bke::CurvesGeometry &curves = *curves_ptr;
73 const OffsetIndices points_by_curve = curves.points_by_curve();
74
75 if (context.domain() == AttrDomain::Curve) {
76 if (use_start_point_special_case(curve_index_, sort_index_, sort_weight_)) {
77 return VArray<int>::from_span(points_by_curve.data());
78 }
79 }
80
81 fn::FieldEvaluator evaluator{context, &mask};
82 evaluator.add(curve_index_);
83 evaluator.add(sort_index_);
84 evaluator.evaluate();
85 const VArray<int> curve_indices = evaluator.get_evaluated<int>(0);
86 const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1);
87
88 const bke::GeometryFieldContext point_context{context, AttrDomain::Point};
89 fn::FieldEvaluator point_evaluator{point_context, curves.points_num()};
90 point_evaluator.add(sort_weight_);
91 point_evaluator.evaluate();
92 const VArray<float> all_sort_weights = point_evaluator.get_evaluated<float>(0);
93 const bool use_sorting = !all_sort_weights.is_single();
94
95 Array<int> point_of_curve(mask.min_array_size());
96 mask.foreach_segment(GrainSize(256), [&](const IndexMaskSegment segment) {
97 /* Reuse arrays to avoid allocation. */
98 Array<float> sort_weights;
99 Array<int> sort_indices;
100
101 for (const int selection_i : segment) {
102 const int curve_i = curve_indices[selection_i];
103 const int index_in_sort = indices_in_sort[selection_i];
104 if (!curves.curves_range().contains(curve_i)) {
105 point_of_curve[selection_i] = 0;
106 continue;
107 }
108 const IndexRange points = points_by_curve[curve_i];
109
110 const int index_in_sort_wrapped = mod_i(index_in_sort, points.size());
111 if (use_sorting) {
112 /* Retrieve the weights for each point. */
113 sort_weights.reinitialize(points.size());
114 all_sort_weights.materialize_compressed(IndexMask(points),
115 sort_weights.as_mutable_span());
116
117 /* Sort a separate array of compressed indices corresponding to the compressed weights.
118 * This allows using `materialize_compressed` to avoid virtual function call overhead
119 * when accessing values in the sort weights. However, it means a separate array of
120 * indices within the compressed array is necessary for sorting. */
121 sort_indices.reinitialize(points.size());
123 std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) {
124 return sort_weights[a] < sort_weights[b];
125 });
126 point_of_curve[selection_i] = points[sort_indices[index_in_sort_wrapped]];
127 }
128 else {
129 point_of_curve[selection_i] = points[index_in_sort_wrapped];
130 }
131 }
132 });
133
134 return VArray<int>::from_container(std::move(point_of_curve));
135 }
136
137 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
138 {
139 curve_index_.node().for_each_field_input_recursive(fn);
140 sort_index_.node().for_each_field_input_recursive(fn);
141 sort_weight_.node().for_each_field_input_recursive(fn);
142 }
143
144 uint64_t hash() const override
145 {
146 return 26978695677882;
147 }
148
149 bool is_equal_to(const fn::FieldNode &other) const override
150 {
151 if (const auto *typed = dynamic_cast<const PointsOfCurveInput *>(&other)) {
152 return typed->curve_index_ == curve_index_ && typed->sort_index_ == sort_index_ &&
153 typed->sort_weight_ == sort_weight_;
154 }
155 return false;
156 }
157
158 std::optional<AttrDomain> preferred_domain(const GeometryComponent & /*component*/) const final
159 {
160 return AttrDomain::Curve;
161 }
162};
163
165 public:
166 CurvePointCountInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Curve Point Count")
167 {
169 }
170
172 const AttrDomain domain,
173 const IndexMask & /*mask*/) const final
174 {
175 if (domain != AttrDomain::Curve) {
176 return {};
177 }
178 const OffsetIndices points_by_curve = curves.points_by_curve();
179 return VArray<int>::from_func(curves.curves_num(), [points_by_curve](const int64_t curve_i) {
180 return points_by_curve[curve_i].size();
181 });
182 }
183
185 {
186 return 903847569873762;
187 }
188
189 bool is_equal_to(const fn::FieldNode &other) const final
190 {
191 return dynamic_cast<const CurvePointCountInput *>(&other) != nullptr;
192 }
193
194 std::optional<AttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final
195 {
196 return AttrDomain::Curve;
197 }
198};
199
201{
202 const Field<int> curve_index = params.extract_input<Field<int>>("Curve Index");
203 if (params.output_is_required("Total")) {
204 params.set_output("Total",
205 Field<int>(std::make_shared<bke::EvaluateAtIndexInput>(
206 curve_index,
207 Field<int>(std::make_shared<CurvePointCountInput>()),
208 AttrDomain::Curve)));
209 }
210 if (params.output_is_required("Point Index")) {
211 params.set_output("Point Index",
212 Field<int>(std::make_shared<PointsOfCurveInput>(
213 curve_index,
214 params.extract_input<Field<int>>("Sort Index"),
215 params.extract_input<Field<float>>("Weights"))));
216 }
217}
218
219static void node_register()
220{
221 static blender::bke::bNodeType ntype;
222 geo_node_type_base(&ntype, "GeometryNodePointsOfCurve", GEO_NODE_CURVE_TOPOLOGY_POINTS_OF_CURVE);
223 ntype.ui_name = "Points of Curve";
224 ntype.ui_description = "Retrieve a point index within a curve";
225 ntype.enum_name_legacy = "POINTS_OF_CURVE";
226 ntype.nclass = NODE_CLASS_INPUT;
228 ntype.declare = node_declare;
230}
232
233} // namespace blender::nodes::node_geo_curve_topology_points_of_curve_cc
Low-level operations for curves.
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#define GEO_NODE_CURVE_TOPOLOGY_POINTS_OF_CURVE
#define final(a, b, c)
Definition BLI_hash.h:19
MINLINE int mod_i(int i, int n)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
long long int int64_t
unsigned long long int uint64_t
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * end() const
Definition BLI_array.hh:325
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
const T * begin() const
Definition BLI_array.hh:321
constexpr int64_t size() const
constexpr bool contains(int64_t value) const
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray from_func(const int64_t size, GetFunc get_func)
static VArray from_span(Span< T > values)
static VArray from_container(ContainerT container)
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:677
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
bool depends_on_input() const
Definition FN_field.hh:554
const FieldNode & node() const
Definition FN_field.hh:135
GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const AttrDomain domain, const IndexMask &) const final
std::optional< AttrDomain > preferred_domain(const bke::CurvesGeometry &) const final
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
PointsOfCurveInput(Field< int > curve_index, Field< int > sort_index, Field< float > sort_weight)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void fill_index_range(MutableSpan< T > span, const T start=0)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void evaluate_constant_field(const GField &field, void *r_value)
Definition field.cc:493
static bool use_start_point_special_case(const Field< int > &curve_index, const Field< int > &sort_index, const Field< float > &sort_weights)
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
#define min(a, b)
Definition sort.cc:36
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362