Blender V5.0
node_geo_offset_point_in_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
8
10
12{
13 b.add_input<decl::Int>("Point Index")
14 .implicit_field(NODE_DEFAULT_INPUT_INDEX_FIELD)
15 .description("The index of the control point to evaluate. Defaults to the current index")
16 .structure_type(StructureType::Field);
17 b.add_input<decl::Int>("Offset").supports_field().description(
18 "The number of control points along the curve to traverse");
19 b.add_output<decl::Bool>("Is Valid Offset")
20 .field_source_reference_all()
22 "Whether the input control point plus the offset is a valid index of the "
23 "original curve");
24 b.add_output<decl::Int>("Point Index")
25 .field_source_reference_all()
27 "The index of the control point plus the offset within the entire "
28 "curves data-block");
29}
30
32 private:
33 const Field<int> index_;
34 const Field<int> offset_;
35
36 public:
38 : GeometryFieldInput(CPPType::get<int>(), "Offset Point in Curve"),
39 index_(std::move(index)),
40 offset_(std::move(offset))
41 {
43 }
44
46 const IndexMask &mask) const final
47 {
48 const bke::CurvesGeometry *curves_ptr = context.curves_or_strokes();
49 if (!curves_ptr) {
50 return {};
51 }
52 const bke::CurvesGeometry &curves = *curves_ptr;
53
54 const OffsetIndices points_by_curve = curves.points_by_curve();
55 const VArray<bool> cyclic = curves.cyclic();
56 const Array<int> parent_curves = curves.point_to_curve_map();
57
58 fn::FieldEvaluator evaluator{context, &mask};
59 evaluator.add(index_);
60 evaluator.add(offset_);
61 evaluator.evaluate();
62 const VArray<int> indices = evaluator.get_evaluated<int>(0);
63 const VArray<int> offsets = evaluator.get_evaluated<int>(1);
64
65 Array<int> output(mask.min_array_size());
66 mask.foreach_index([&](const int i_selection) {
67 const int point = std::clamp(indices[i_selection], 0, curves.points_num() - 1);
68 const int curve = parent_curves[point];
69 const IndexRange curve_points = points_by_curve[curve];
70 const int shifted_point = point + offsets[i_selection];
71
72 if (cyclic[curve]) {
73 const int point_index_in_curve = shifted_point - curve_points.start();
74 output[i_selection] = curve_points.start() +
75 math::mod_periodic<int>(point_index_in_curve, curve_points.size());
76 return;
77 }
78 output[i_selection] = std::clamp(shifted_point, 0, curves.points_num() - 1);
79 });
80
81 return VArray<int>::from_container(std::move(output));
82 }
83
84 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
85 {
86 index_.node().for_each_field_input_recursive(fn);
87 offset_.node().for_each_field_input_recursive(fn);
88 }
89};
90
92 private:
93 const Field<int> index_;
94 const Field<int> offset_;
95
96 public:
98 : GeometryFieldInput(CPPType::get<bool>(), "Offset Valid"),
99 index_(std::move(index)),
100 offset_(std::move(offset))
101 {
103 }
104
106 const IndexMask &mask) const final
107 {
108 const bke::CurvesGeometry *curves_ptr = context.curves_or_strokes();
109 if (!curves_ptr) {
110 return {};
111 }
112 const bke::CurvesGeometry &curves = *curves_ptr;
113
114 const VArray<bool> cyclic = curves.cyclic();
115 const OffsetIndices points_by_curve = curves.points_by_curve();
116 const Array<int> parent_curves = curves.point_to_curve_map();
117
118 fn::FieldEvaluator evaluator{context, &mask};
119 evaluator.add(index_);
120 evaluator.add(offset_);
121 evaluator.evaluate();
122 const VArray<int> indices = evaluator.get_evaluated<int>(0);
123 const VArray<int> offsets = evaluator.get_evaluated<int>(1);
124
125 Array<bool> output(mask.min_array_size());
126 mask.foreach_index([&](const int i_selection) {
127 const int i_point = indices[i_selection];
128 if (!curves.points_range().contains(i_point)) {
129 output[i_selection] = false;
130 return;
131 }
132
133 const int i_curve = parent_curves[i_point];
134 const IndexRange curve_points = points_by_curve[i_curve];
135 if (cyclic[i_curve]) {
136 output[i_selection] = true;
137 return;
138 }
139 output[i_selection] = curve_points.contains(i_point + offsets[i_selection]);
140 });
141 return VArray<bool>::from_container(std::move(output));
142 }
143
144 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
145 {
146 index_.node().for_each_field_input_recursive(fn);
147 offset_.node().for_each_field_input_recursive(fn);
148 }
149};
150
152{
153 Field<int> index = params.extract_input<Field<int>>("Point Index");
154 Field<int> offset = params.extract_input<Field<int>>("Offset");
155
156 if (params.output_is_required("Point Index")) {
157 Field<int> curve_point_field{std::make_shared<ControlPointNeighborFieldInput>(index, offset)};
158 params.set_output("Point Index", std::move(curve_point_field));
159 }
160 if (params.output_is_required("Is Valid Offset")) {
161 Field<bool> valid_field{std::make_shared<OffsetValidFieldInput>(index, offset)};
162 params.set_output("Is Valid Offset", std::move(valid_field));
163 }
164}
165
166static void node_register()
167{
168 static blender::bke::bNodeType ntype;
169 geo_node_type_base(&ntype, "GeometryNodeOffsetPointInCurve", GEO_NODE_OFFSET_POINT_IN_CURVE);
170 ntype.ui_name = "Offset Point in Curve";
171 ntype.ui_description = "Offset a control point index within its curve";
172 ntype.enum_name_legacy = "OFFSET_POINT_IN_CURVE";
173 ntype.nclass = NODE_CLASS_INPUT;
175 ntype.declare = node_declare;
177}
179
180} // namespace blender::nodes::node_geo_offset_point_in_curve_cc
Low-level operations for curves.
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#define GEO_NODE_OFFSET_POINT_IN_CURVE
#define final(a, b, c)
Definition BLI_hash.h:19
#define NOD_REGISTER_NODE(REGISTER_FUNC)
constexpr int64_t size() const
constexpr int64_t start() const
constexpr bool contains(int64_t value) const
static VArray from_container(ContainerT container)
Array< int > point_to_curve_map() const
OffsetIndices< int > points_by_curve() const
IndexRange points_range() const
VArray< bool > cyclic() 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
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
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
static ushort indices[]
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
T mod_periodic(const T &a, const T &b)
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
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