Blender V4.5
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 b.add_input<decl::Int>("Offset").supports_field().description(
17 "The number of control points along the curve to traverse");
18 b.add_output<decl::Bool>("Is Valid Offset")
19 .field_source_reference_all()
21 "Whether the input control point plus the offset is a valid index of the "
22 "original curve");
23 b.add_output<decl::Int>("Point Index")
24 .field_source_reference_all()
26 "The index of the control point plus the offset within the entire "
27 "curves data-block");
28}
29
31 private:
32 const Field<int> index_;
33 const Field<int> offset_;
34
35 public:
37 : GeometryFieldInput(CPPType::get<int>(), "Offset Point in Curve"),
38 index_(std::move(index)),
39 offset_(std::move(offset))
40 {
42 }
43
45 const IndexMask &mask) const final
46 {
47 const bke::CurvesGeometry *curves_ptr = context.curves_or_strokes();
48 if (!curves_ptr) {
49 return {};
50 }
51 const bke::CurvesGeometry &curves = *curves_ptr;
52
53 const OffsetIndices points_by_curve = curves.points_by_curve();
54 const VArray<bool> cyclic = curves.cyclic();
55 const Array<int> parent_curves = curves.point_to_curve_map();
56
57 fn::FieldEvaluator evaluator{context, &mask};
58 evaluator.add(index_);
59 evaluator.add(offset_);
60 evaluator.evaluate();
61 const VArray<int> indices = evaluator.get_evaluated<int>(0);
62 const VArray<int> offsets = evaluator.get_evaluated<int>(1);
63
64 Array<int> output(mask.min_array_size());
65 mask.foreach_index([&](const int i_selection) {
66 const int point = std::clamp(indices[i_selection], 0, curves.points_num() - 1);
67 const int curve = parent_curves[point];
68 const IndexRange curve_points = points_by_curve[curve];
69 const int shifted_point = point + offsets[i_selection];
70
71 if (cyclic[curve]) {
72 const int point_index_in_curve = shifted_point - curve_points.start();
73 output[i_selection] = curve_points.start() +
74 math::mod_periodic<int>(point_index_in_curve, curve_points.size());
75 return;
76 }
77 output[i_selection] = std::clamp(shifted_point, 0, curves.points_num() - 1);
78 });
79
80 return VArray<int>::ForContainer(std::move(output));
81 }
82
83 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
84 {
85 index_.node().for_each_field_input_recursive(fn);
86 offset_.node().for_each_field_input_recursive(fn);
87 }
88};
89
91 private:
92 const Field<int> index_;
93 const Field<int> offset_;
94
95 public:
97 : GeometryFieldInput(CPPType::get<bool>(), "Offset Valid"),
98 index_(std::move(index)),
99 offset_(std::move(offset))
100 {
102 }
103
105 const IndexMask &mask) const final
106 {
107 const bke::CurvesGeometry *curves_ptr = context.curves_or_strokes();
108 if (!curves_ptr) {
109 return {};
110 }
111 const bke::CurvesGeometry &curves = *curves_ptr;
112
113 const VArray<bool> cyclic = curves.cyclic();
114 const OffsetIndices points_by_curve = curves.points_by_curve();
115 const Array<int> parent_curves = curves.point_to_curve_map();
116
117 fn::FieldEvaluator evaluator{context, &mask};
118 evaluator.add(index_);
119 evaluator.add(offset_);
120 evaluator.evaluate();
121 const VArray<int> indices = evaluator.get_evaluated<int>(0);
122 const VArray<int> offsets = evaluator.get_evaluated<int>(1);
123
124 Array<bool> output(mask.min_array_size());
125 mask.foreach_index([&](const int i_selection) {
126 const int i_point = indices[i_selection];
127 if (!curves.points_range().contains(i_point)) {
128 output[i_selection] = false;
129 return;
130 }
131
132 const int i_curve = parent_curves[i_point];
133 const IndexRange curve_points = points_by_curve[i_curve];
134 if (cyclic[i_curve]) {
135 output[i_selection] = true;
136 return;
137 }
138 output[i_selection] = curve_points.contains(i_point + offsets[i_selection]);
139 });
140 return VArray<bool>::ForContainer(std::move(output));
141 }
142
143 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
144 {
145 index_.node().for_each_field_input_recursive(fn);
146 offset_.node().for_each_field_input_recursive(fn);
147 }
148};
149
151{
152 Field<int> index = params.extract_input<Field<int>>("Point Index");
153 Field<int> offset = params.extract_input<Field<int>>("Offset");
154
155 if (params.output_is_required("Point Index")) {
156 Field<int> curve_point_field{std::make_shared<ControlPointNeighborFieldInput>(index, offset)};
157 params.set_output("Point Index", std::move(curve_point_field));
158 }
159 if (params.output_is_required("Is Valid Offset")) {
160 Field<bool> valid_field{std::make_shared<OffsetValidFieldInput>(index, offset)};
161 params.set_output("Is Valid Offset", std::move(valid_field));
162 }
163}
164
165static void node_register()
166{
167 static blender::bke::bNodeType ntype;
168 geo_node_type_base(&ntype, "GeometryNodeOffsetPointInCurve", GEO_NODE_OFFSET_POINT_IN_CURVE);
169 ntype.ui_name = "Offset Point in Curve";
170 ntype.ui_description = "Offset a control point index within its curve";
171 ntype.enum_name_legacy = "OFFSET_POINT_IN_CURVE";
172 ntype.nclass = NODE_CLASS_INPUT;
174 ntype.declare = node_declare;
176}
178
179} // namespace blender::nodes::node_geo_offset_point_in_curve_cc
Low-level operations for curves.
#define NODE_CLASS_INPUT
Definition BKE_node.hh:433
#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 ForContainer(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:2748
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:226
std::string ui_description
Definition BKE_node.hh:232
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
NodeDeclareFunction declare
Definition BKE_node.hh:355