Blender V4.3
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 <numeric>
6
7#include "BLI_math_geom.h"
8#include "BLI_math_matrix.h"
9#include "BLI_math_rotation.h"
10
11#include "BKE_curves.hh"
12
13#include "NOD_rna_define.hh"
14
15#include "UI_interface.hh"
16#include "UI_resources.hh"
17
18#include "node_geometry_util.hh"
19
21
23
25{
26 auto enable_points = [](bNode &node) {
27 node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS;
28 };
29 auto enable_radius = [](bNode &node) {
30 node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS;
31 };
32
33 b.add_input<decl::Int>("Resolution")
34 .default_value(16)
35 .min(2)
36 .max(256)
38 .description("The number of points on the arc");
39 auto &start = b.add_input<decl::Vector>("Start")
40 .default_value({-1.0f, 0.0f, 0.0f})
41 .subtype(PROP_TRANSLATION)
42 .description("Position of the first control point")
43 .make_available(enable_points);
44 auto &middle = b.add_input<decl::Vector>("Middle")
45 .default_value({0.0f, 2.0f, 0.0f})
46 .subtype(PROP_TRANSLATION)
47 .description("Position of the middle control point")
48 .make_available(enable_points);
49 auto &end = b.add_input<decl::Vector>("End")
50 .default_value({1.0f, 0.0f, 0.0f})
51 .subtype(PROP_TRANSLATION)
52 .description("Position of the last control point")
53 .make_available(enable_points);
54 auto &radius = b.add_input<decl::Float>("Radius")
55 .default_value(1.0f)
56 .min(0.0f)
58 .description("Distance of the points from the origin")
59 .make_available(enable_radius);
60 auto &start_angle = b.add_input<decl::Float>("Start Angle")
61 .default_value(0.0f)
63 .description("Starting angle of the arc")
64 .make_available(enable_radius);
65 auto &sweep_angle = b.add_input<decl::Float>("Sweep Angle")
66 .default_value(1.75f * M_PI)
67 .min(-2 * M_PI)
68 .max(2 * M_PI)
70 .description("Length of the arc")
71 .make_available(enable_radius);
72 auto &offset_angle = b.add_input<decl::Float>("Offset Angle")
73 .default_value(0.0f)
75 .description("Offset angle of the arc")
76 .make_available(enable_points);
77 b.add_input<decl::Bool>("Connect Center")
78 .default_value(false)
79 .description("Connect the arc at the center");
80 b.add_input<decl::Bool>("Invert Arc")
81 .default_value(false)
82 .description("Invert and draw opposite arc");
83
84 b.add_output<decl::Geometry>("Curve");
85 auto &center_out = b.add_output<decl::Vector>("Center")
86 .description("The center of the circle described by the three points")
87 .make_available(enable_points);
88 auto &normal_out = b.add_output<decl::Vector>("Normal")
89 .description(
90 "The normal direction of the plane described by the three points, "
91 "pointing towards the positive Z axis")
92 .make_available(enable_points);
93 auto &radius_out = b.add_output<decl::Float>("Radius")
94 .description("The radius of the circle described by the three points")
95 .make_available(enable_points);
96
97 const bNode *node = b.node_or_null();
98 if (node != nullptr) {
99 const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node);
101
102 const bool radius_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS);
103 const bool points_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS);
104
105 start.available(points_mode);
106 middle.available(points_mode);
107 end.available(points_mode);
108
109 radius.available(radius_mode);
110 start_angle.available(radius_mode);
111 sweep_angle.available(radius_mode);
112
113 offset_angle.available(points_mode);
114
115 center_out.available(points_mode);
116 normal_out.available(points_mode);
117 radius_out.available(points_mode);
118 }
119}
120
121static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
122{
123 uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
124}
125
126static void node_init(bNodeTree * /*tree*/, bNode *node)
127{
128 NodeGeometryCurvePrimitiveArc *data = MEM_cnew<NodeGeometryCurvePrimitiveArc>(__func__);
129
131 node->storage = data;
132}
133
134static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle)
135{
136 float3 result = vector;
137 float mat[3][3];
138 axis_angle_to_mat3(mat, axis, angle);
139 mul_m3_v3(mat, result);
140 return result;
141}
142
143static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
144{
145 const float3 a = math::normalize(p2 - p1);
146 const float3 b = math::normalize(p3 - p1);
147 return ELEM(a, b, b * -1.0f);
148}
149
150static Curves *create_arc_curve_from_points(const int resolution,
151 const float3 a,
152 const float3 b,
153 const float3 c,
154 float angle_offset,
155 const bool connect_center,
156 const bool invert_arc,
157 float3 &r_center,
158 float3 &r_normal,
159 float &r_radius)
160{
161 const int size = connect_center ? resolution + 1 : resolution;
163 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
164
165 const int stepcount = resolution - 1;
166 const int centerpoint = resolution;
167 MutableSpan<float3> positions = curves.positions_for_write();
168
169 const bool is_colinear = colinear_f3_f3_f3(a, b, c);
170
171 float3 center;
172 float3 normal;
173 float radius;
174 const float3 mid_ac = math::midpoint(a, c);
175 normal_tri_v3(normal, a, c, b);
176
177 if (is_colinear || a == c || a == b || b == c || resolution == 2) {
178 /* If colinear, generate a point line between points. */
179 float3 p1, p2;
180
181 /* Find the two points that are furthest away from each other. */
182 const float ab = math::distance_squared(a, b);
183 const float ac = math::distance_squared(a, c);
184 const float bc = math::distance_squared(b, c);
185 if (ab > ac && ab > bc) {
186 p1 = a;
187 p2 = b;
188 }
189 else if (bc > ab && bc > ac) {
190 p1 = b;
191 p2 = c;
192 }
193 else {
194 p1 = a;
195 p2 = c;
196 }
197
198 const float step = 1.0f / stepcount;
199 for (const int i : IndexRange(resolution)) {
200 const float factor = step * i;
201 positions[i] = math::interpolate(p1, p2, factor);
202 }
203 center = mid_ac;
204 radius = 0.0f;
205 }
206 else {
207 /* Midpoints of `A->B` and `B->C`. */
208 const float3 mid_ab = math::midpoint(a, b);
209 const float3 mid_bc = math::midpoint(c, b);
210
211 /* Normalized vectors of `A->B` and `B->C`. */
212 const float3 nba = math::normalize(b - a);
213 const float3 ncb = math::normalize(c - b);
214
215 /* Normal of plane of main 2 segments A->B and `B->C`. */
216 const float3 nabc = math::normalize(math::cross(nba, ncb));
217
218 /* Determine center point from the intersection of 3 planes. */
219 float plane_1[4], plane_2[4], plane_3[4];
220 plane_from_point_normal_v3(plane_1, mid_ab, nabc);
221 plane_from_point_normal_v3(plane_2, mid_ab, nba);
222 plane_from_point_normal_v3(plane_3, mid_bc, ncb);
223
224 /* If the 3 planes do not intersect at one point, just return empty geometry. */
225 if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) {
226 r_center = mid_ac;
227 r_normal = normal;
228 r_radius = 0.0f;
229 return nullptr;
230 }
231
232 /* Radial vectors. */
233 const float3 rad_a = math::normalize(a - center);
234 const float3 rad_b = math::normalize(b - center);
235 const float3 rad_c = math::normalize(c - center);
236
237 /* Calculate angles. */
238 radius = math::distance(center, b);
239 float angle_ab = angle_signed_on_axis_v3v3_v3(rad_a, rad_b, normal) + 2.0f * M_PI;
240 float angle_ac = angle_signed_on_axis_v3v3_v3(rad_a, rad_c, normal) + 2.0f * M_PI;
241 float angle = (angle_ac > angle_ab) ? angle_ac : angle_ab;
242 angle -= 2.0f * M_PI;
243 if (invert_arc) {
244 angle = -(2.0f * M_PI - angle);
245 }
246
247 /* Create arc. */
248 const float step = angle / stepcount;
249 for (const int i : IndexRange(resolution)) {
250 const float factor = step * i + angle_offset;
251 float3 out = rotate_vector_around_axis(rad_a, -normal, factor);
252 positions[i] = out * radius + center;
253 }
254 }
255
256 if (connect_center) {
257 curves.cyclic_for_write().first() = true;
258 positions[centerpoint] = center;
259 }
260
261 /* Ensure normal is relative to Z-up. */
262 if (math::dot(float3(0, 0, 1), normal) < 0) {
263 normal = -normal;
264 }
265
266 r_center = center;
267 r_radius = radius;
268 r_normal = normal;
269 return curves_id;
270}
271
272static Curves *create_arc_curve_from_radius(const int resolution,
273 const float radius,
274 const float start_angle,
275 const float sweep_angle,
276 const bool connect_center,
277 const bool invert_arc)
278{
279 const int size = connect_center ? resolution + 1 : resolution;
281 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
282
283 const int stepcount = resolution - 1;
284 const int centerpoint = resolution;
285 MutableSpan<float3> positions = curves.positions_for_write();
286
287 const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle;
288
289 const float theta_step = sweep / float(stepcount);
290 for (const int i : IndexRange(resolution)) {
291 const float theta = theta_step * i + start_angle;
292 const float x = radius * cos(theta);
293 const float y = radius * sin(theta);
294 positions[i] = float3(x, y, 0.0f);
295 }
296
297 if (connect_center) {
298 curves.cyclic_for_write().first() = true;
299 positions[centerpoint] = float3(0.0f, 0.0f, 0.0f);
300 }
301
302 return curves_id;
303}
304
306{
307 const NodeGeometryCurvePrimitiveArc &storage = node_storage(params.node());
308
310
311 switch (mode) {
313 float3 r_center, r_normal;
314 float r_radius;
316 std::max(params.extract_input<int>("Resolution"), 2),
317 params.extract_input<float3>("Start"),
318 params.extract_input<float3>("Middle"),
319 params.extract_input<float3>("End"),
320 params.extract_input<float>("Offset Angle"),
321 params.extract_input<bool>("Connect Center"),
322 params.extract_input<bool>("Invert Arc"),
323 r_center,
324 r_normal,
325 r_radius);
326 params.set_output("Curve", GeometrySet::from_curves(curves));
327 params.set_output("Center", r_center);
328 params.set_output("Normal", r_normal);
329 params.set_output("Radius", r_radius);
330 break;
331 }
334 std::max(params.extract_input<int>("Resolution"), 2),
335 params.extract_input<float>("Radius"),
336 params.extract_input<float>("Start Angle"),
337 params.extract_input<float>("Sweep Angle"),
338 params.extract_input<bool>("Connect Center"),
339 params.extract_input<bool>("Invert Arc"));
340
341 params.set_output("Curve", GeometrySet::from_curves(curves));
342 break;
343 }
344 }
345}
346
347static void node_rna(StructRNA *srna)
348{
349 static const EnumPropertyItem mode_items[] = {
351 "POINTS",
352 ICON_NONE,
353 "Points",
354 "Define arc by 3 points on circle. Arc is calculated between start and end points"},
356 "RADIUS",
357 ICON_NONE,
358 "Radius",
359 "Define radius with a float"},
360 {0, nullptr, 0, nullptr, nullptr},
361 };
362
364 "mode",
365 "Mode",
366 "Method used to determine radius and placement",
367 mode_items,
370}
371
372static void node_register()
373{
374 static blender::bke::bNodeType ntype;
375 geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_ARC, "Arc", NODE_CLASS_GEOMETRY);
376 ntype.initfunc = node_init;
378 "NodeGeometryCurvePrimitiveArc",
381 ntype.declare = node_declare;
385
386 node_rna(ntype.rna_ext.srna);
387}
389
390} // namespace blender::nodes::node_geo_curve_primitive_arc_cc
Low-level operations for curves.
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#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:215
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:39
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(...)
@ CURVE_TYPE_POLY
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:125
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a vector
@ PROP_DISTANCE
Definition RNA_types.hh:159
@ PROP_ANGLE
Definition RNA_types.hh:155
@ PROP_TRANSLATION
Definition RNA_types.hh:164
@ PROP_UNSIGNED
Definition RNA_types.hh:152
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
void make_available(bNode &node) const
local_group_size(16, 16) .push_constant(Type b
OperationNode * node
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float3 cos(float3 v)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
Curves * curves_new_nomain_single(int points_num, CurveType type)
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, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
CurvesGeometry geometry
StructRNA * srna
Definition RNA_types.hh:780
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126