Blender V5.0
node_geo_curve_primitive_arc.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 "BLI_math_geom.h"
6#include "BLI_math_matrix.h"
7#include "BLI_math_rotation.h"
8
9#include "BKE_curves.hh"
10
11#include "NOD_rna_define.hh"
12
14#include "UI_resources.hh"
15
16#include "node_geometry_util.hh"
17
19
21
23{
24 auto enable_points = [](bNode &node) {
25 node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS;
26 };
27 auto enable_radius = [](bNode &node) {
28 node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS;
29 };
30
31 b.add_input<decl::Int>("Resolution")
32 .default_value(16)
33 .min(2)
34 .max(256)
35 .subtype(PROP_UNSIGNED)
36 .description("The number of points on the arc");
37 auto &start = b.add_input<decl::Vector>("Start")
38 .default_value({-1.0f, 0.0f, 0.0f})
39 .subtype(PROP_TRANSLATION)
40 .description("Position of the first control point")
41 .make_available(enable_points);
42 auto &middle = b.add_input<decl::Vector>("Middle")
43 .default_value({0.0f, 2.0f, 0.0f})
44 .subtype(PROP_TRANSLATION)
45 .description("Position of the middle control point")
46 .make_available(enable_points);
47 auto &end = b.add_input<decl::Vector>("End")
48 .default_value({1.0f, 0.0f, 0.0f})
49 .subtype(PROP_TRANSLATION)
50 .description("Position of the last control point")
51 .make_available(enable_points);
52 auto &radius = b.add_input<decl::Float>("Radius")
53 .default_value(1.0f)
54 .min(0.0f)
55 .subtype(PROP_DISTANCE)
56 .description("Distance of the points from the origin")
57 .make_available(enable_radius);
58 auto &start_angle = b.add_input<decl::Float>("Start Angle")
59 .default_value(0.0f)
60 .subtype(PROP_ANGLE)
61 .description("Starting angle of the arc")
62 .make_available(enable_radius);
63 auto &sweep_angle = b.add_input<decl::Float>("Sweep Angle")
64 .default_value(1.75f * M_PI)
65 .min(-2 * M_PI)
66 .max(2 * M_PI)
67 .subtype(PROP_ANGLE)
68 .description("Length of the arc")
69 .make_available(enable_radius);
70 auto &offset_angle = b.add_input<decl::Float>("Offset Angle")
71 .default_value(0.0f)
72 .subtype(PROP_ANGLE)
73 .description("Offset angle of the arc")
74 .make_available(enable_points);
75 b.add_input<decl::Bool>("Connect Center")
76 .default_value(false)
77 .description("Connect the arc at the center");
78 b.add_input<decl::Bool>("Invert Arc")
79 .default_value(false)
80 .description("Invert and draw opposite arc");
81
82 b.add_output<decl::Geometry>("Curve");
83 auto &center_out = b.add_output<decl::Vector>("Center")
84 .description("The center of the circle described by the three points")
85 .make_available(enable_points);
86 auto &normal_out = b.add_output<decl::Vector>("Normal")
88 "The normal direction of the plane described by the three points, "
89 "pointing towards the positive Z axis")
90 .make_available(enable_points);
91 auto &radius_out = b.add_output<decl::Float>("Radius")
92 .description("The radius of the circle described by the three points")
93 .make_available(enable_points);
94
95 const bNode *node = b.node_or_null();
96 if (node != nullptr) {
97 const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node);
99
100 const bool radius_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS);
101 const bool points_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS);
102
103 start.available(points_mode);
104 middle.available(points_mode);
105 end.available(points_mode);
106
107 radius.available(radius_mode);
108 start_angle.available(radius_mode);
109 sweep_angle.available(radius_mode);
110
111 offset_angle.available(points_mode);
112
113 center_out.available(points_mode);
114 normal_out.available(points_mode);
115 radius_out.available(points_mode);
116 }
117}
118
119static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
120{
121 layout->prop(ptr, "mode", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
122}
123
131
132static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle)
133{
135 float mat[3][3];
136 axis_angle_to_mat3(mat, axis, angle);
137 mul_m3_v3(mat, result);
138 return result;
139}
140
141static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
142{
143 const float3 a = math::normalize(p2 - p1);
144 const float3 b = math::normalize(p3 - p1);
145 return ELEM(a, b, b * -1.0f);
146}
147
148static Curves *create_arc_curve_from_points(const int resolution,
149 const float3 a,
150 const float3 b,
151 const float3 c,
152 float angle_offset,
153 const bool connect_center,
154 const bool invert_arc,
155 float3 &r_center,
156 float3 &r_normal,
157 float &r_radius)
158{
159 const int size = connect_center ? resolution + 1 : resolution;
161 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
162
163 const int stepcount = resolution - 1;
164 const int centerpoint = resolution;
165 MutableSpan<float3> positions = curves.positions_for_write();
166
167 const bool is_colinear = colinear_f3_f3_f3(a, b, c);
168
169 float3 center;
170 float3 normal;
171 float radius;
172 const float3 mid_ac = math::midpoint(a, c);
173 normal_tri_v3(normal, a, c, b);
174
175 if (is_colinear || a == c || a == b || b == c || resolution == 2) {
176 /* If colinear, generate a point line between points. */
177 float3 p1, p2;
178
179 /* Find the two points that are furthest away from each other. */
180 const float ab = math::distance_squared(a, b);
181 const float ac = math::distance_squared(a, c);
182 const float bc = math::distance_squared(b, c);
183 if (ab > ac && ab > bc) {
184 p1 = a;
185 p2 = b;
186 }
187 else if (bc > ab && bc > ac) {
188 p1 = b;
189 p2 = c;
190 }
191 else {
192 p1 = a;
193 p2 = c;
194 }
195
196 const float step = 1.0f / stepcount;
197 for (const int i : IndexRange(resolution)) {
198 const float factor = step * i;
199 positions[i] = math::interpolate(p1, p2, factor);
200 }
201 center = mid_ac;
202 radius = 0.0f;
203 }
204 else {
205 /* Midpoints of `A->B` and `B->C`. */
206 const float3 mid_ab = math::midpoint(a, b);
207 const float3 mid_bc = math::midpoint(c, b);
208
209 /* Normalized vectors of `A->B` and `B->C`. */
210 const float3 nba = math::normalize(b - a);
211 const float3 ncb = math::normalize(c - b);
212
213 /* Normal of plane of main 2 segments A->B and `B->C`. */
214 const float3 nabc = math::normalize(math::cross(nba, ncb));
215
216 /* Determine center point from the intersection of 3 planes. */
217 float plane_1[4], plane_2[4], plane_3[4];
218 plane_from_point_normal_v3(plane_1, mid_ab, nabc);
219 plane_from_point_normal_v3(plane_2, mid_ab, nba);
220 plane_from_point_normal_v3(plane_3, mid_bc, ncb);
221
222 /* If the 3 planes do not intersect at one point, just return empty geometry. */
223 if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) {
224 r_center = mid_ac;
225 r_normal = normal;
226 r_radius = 0.0f;
227 return nullptr;
228 }
229
230 /* Radial vectors. */
231 const float3 rad_a = math::normalize(a - center);
232 const float3 rad_b = math::normalize(b - center);
233 const float3 rad_c = math::normalize(c - center);
234
235 /* Calculate angles. */
236 radius = math::distance(center, b);
237 float angle_ab = angle_signed_on_axis_v3v3_v3(rad_a, rad_b, normal) + 2.0f * M_PI;
238 float angle_ac = angle_signed_on_axis_v3v3_v3(rad_a, rad_c, normal) + 2.0f * M_PI;
239 float angle = (angle_ac > angle_ab) ? angle_ac : angle_ab;
240 angle -= 2.0f * M_PI;
241 if (invert_arc) {
242 angle = -(2.0f * M_PI - angle);
243 }
244
245 /* Create arc. */
246 const float step = angle / stepcount;
247 for (const int i : IndexRange(resolution)) {
248 const float factor = step * i + angle_offset;
249 float3 out = rotate_vector_around_axis(rad_a, -normal, factor);
250 positions[i] = out * radius + center;
251 }
252 }
253
254 if (connect_center) {
255 curves.cyclic_for_write().first() = true;
256 positions[centerpoint] = center;
257 }
258
259 /* Ensure normal is relative to Z-up. */
260 if (math::dot(float3(0, 0, 1), normal) < 0) {
261 normal = -normal;
262 }
263
264 r_center = center;
265 r_radius = radius;
266 r_normal = normal;
267 return curves_id;
268}
269
270static Curves *create_arc_curve_from_radius(const int resolution,
271 const float radius,
272 const float start_angle,
273 const float sweep_angle,
274 const bool connect_center,
275 const bool invert_arc)
276{
277 const int size = connect_center ? resolution + 1 : resolution;
279 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
280
281 const int stepcount = resolution - 1;
282 const int centerpoint = resolution;
283 MutableSpan<float3> positions = curves.positions_for_write();
284
285 const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle;
286
287 const float theta_step = sweep / float(stepcount);
288 for (const int i : IndexRange(resolution)) {
289 const float theta = theta_step * i + start_angle;
290 const float x = radius * cos(theta);
291 const float y = radius * sin(theta);
292 positions[i] = float3(x, y, 0.0f);
293 }
294
295 if (connect_center) {
296 curves.cyclic_for_write().first() = true;
297 positions[centerpoint] = float3(0.0f, 0.0f, 0.0f);
298 }
299
300 return curves_id;
301}
302
304{
305 const NodeGeometryCurvePrimitiveArc &storage = node_storage(params.node());
306
308
309 switch (mode) {
311 float3 center, normal;
312 float radius;
314 std::max(params.extract_input<int>("Resolution"), 2),
315 params.extract_input<float3>("Start"),
316 params.extract_input<float3>("Middle"),
317 params.extract_input<float3>("End"),
318 params.extract_input<float>("Offset Angle"),
319 params.extract_input<bool>("Connect Center"),
320 params.extract_input<bool>("Invert Arc"),
321 center,
322 normal,
323 radius);
324 params.set_output("Curve", GeometrySet::from_curves(curves));
325 params.set_output("Center", center);
326 params.set_output("Normal", normal);
327 params.set_output("Radius", radius);
328 break;
329 }
332 std::max(params.extract_input<int>("Resolution"), 2),
333 params.extract_input<float>("Radius"),
334 params.extract_input<float>("Start Angle"),
335 params.extract_input<float>("Sweep Angle"),
336 params.extract_input<bool>("Connect Center"),
337 params.extract_input<bool>("Invert Arc"));
338
339 params.set_output("Curve", GeometrySet::from_curves(curves));
340 break;
341 }
342 }
343}
344
345static void node_rna(StructRNA *srna)
346{
347 static const EnumPropertyItem mode_items[] = {
349 "POINTS",
350 ICON_NONE,
351 "Points",
352 "Define arc by 3 points on circle. Arc is calculated between start and end points"},
354 "RADIUS",
355 ICON_NONE,
356 "Radius",
357 "Define radius with a float"},
358 {0, nullptr, 0, nullptr, nullptr},
359 };
360
362 "mode",
363 "Mode",
364 "Method used to determine radius and placement",
365 mode_items,
368}
369
370static void node_register()
371{
372 static blender::bke::bNodeType ntype;
373 geo_node_type_base(&ntype, "GeometryNodeCurveArc", GEO_NODE_CURVE_PRIMITIVE_ARC);
374 ntype.ui_name = "Arc";
375 ntype.ui_description = "Generate a poly spline arc";
376 ntype.enum_name_legacy = "CURVE_PRIMITIVE_ARC";
378 ntype.initfunc = node_init;
380 "NodeGeometryCurvePrimitiveArc",
383 ntype.declare = node_declare;
387
388 node_rna(ntype.rna_ext.srna);
389}
391
392} // namespace blender::nodes::node_geo_curve_primitive_arc_cc
Low-level operations for curves.
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_CURVE_PRIMITIVE_ARC
#define M_PI
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
bool isect_plane_plane_plane_v3(const float plane_a[4], const float plane_b[4], const float plane_c[4], float r_isect_co[3]) ATTR_WARN_UNUSED_RESULT
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void mul_m3_v3(const float M[3][3], float r[3])
void axis_angle_to_mat3(float R[3][3], const float axis[3], float angle)
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
#define ELEM(...)
GeometryNodeCurvePrimitiveArcMode
@ GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS
@ GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_DISTANCE
Definition RNA_types.hh:256
@ PROP_ANGLE
Definition RNA_types.hh:252
@ PROP_TRANSLATION
Definition RNA_types.hh:261
@ PROP_UNSIGNED
Definition RNA_types.hh:249
@ UI_ITEM_R_EXPAND
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr T & first() const
Definition BLI_span.hh:679
MutableSpan< float3 > positions_for_write()
MutableSpan< bool > cyclic_for_write()
nullptr float
#define out
#define sin
#define cos
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
Curves * curves_new_nomain_single(int points_num, CurveType type)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T midpoint(const T &a, const T &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
static Curves * create_arc_curve_from_points(const int resolution, const float3 a, const float3 b, const float3 c, float angle_offset, const bool connect_center, const bool invert_arc, float3 &r_center, float3 &r_normal, float &r_radius)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static Curves * create_arc_curve_from_radius(const int resolution, const float radius, const float start_angle, const float sweep_angle, const bool connect_center, const bool invert_arc)
static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle)
static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
#define min(a, b)
Definition sort.cc:36
CurvesGeometry geometry
StructRNA * srna
void * storage
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeDeclareFunction declare
Definition BKE_node.hh:362
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
PointerRNA * ptr
Definition wm_files.cc:4238