Blender V4.3
curve_bevel.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
12#include <cstring>
13
14#include "BLI_listbase.h"
15#include "BLI_math_base.h" /* Needed with MSVC for M_PI & M_PI_2. */
16
17#include "MEM_guardedalloc.h"
18
19#include "DNA_curve_types.h"
21#include "DNA_object_types.h"
22
23#include "BKE_curve.hh"
24#include "BKE_displist.h"
25#include "BKE_object_types.hh"
26
33
35{
36 if (!(curve->flag & (CU_FRONT | CU_BACK))) {
37 return FULL;
38 }
39 if ((curve->flag & CU_FRONT) && (curve->flag & CU_BACK)) {
40 return HALF;
41 }
42
43 return (curve->flag & CU_FRONT) ? FRONT : BACK;
44}
45
46static void bevel_quarter_fill(const Curve *curve,
47 float *quarter_coords_x,
48 float *quarter_coords_y)
49{
50 if (curve->bevel_mode == CU_BEV_MODE_ROUND) {
51 float angle = 0.0f;
52 const float dangle = float(M_PI_2) / (curve->bevresol + 1);
53 for (int i = 0; i < curve->bevresol + 1; i++) {
54 quarter_coords_x[i] = float(cosf(angle) * (curve->bevel_radius));
55 quarter_coords_y[i] = float(sinf(angle) * (curve->bevel_radius));
56 angle += dangle;
57 }
58 }
59 else {
60 /* The curve profile evaluation should be done when the resolution is set. */
61 BLI_assert(curve->bevel_profile->segments != nullptr);
62 BLI_assert(curve->bevel_profile->segments_len == curve->bevresol + 1);
63
64 /* If there aren't enough samples, the curveprofile won't
65 * sample the start vertex, so set it manually instead. */
66 quarter_coords_x[0] = curve->bevel_radius;
67 quarter_coords_y[0] = 0.0f;
68 for (int i = 1; i < curve->bevresol + 1; i++) {
69 quarter_coords_x[i] = float(curve->bevel_profile->segments[i].x * (curve->bevel_radius));
70 quarter_coords_y[i] = float(curve->bevel_profile->segments[i].y * (curve->bevel_radius));
71 }
72 }
73}
74
76 ListBase *disp,
77 const bool use_extrude,
78 const CurveBevelFillType fill_type)
79{
80 DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
81
82 /* Calculate the profile of the bevel once to reuse it for each quarter. We will need
83 * to flip around the indices for every other section in order to build around the circle
84 * in a consistent direction.
85 *
86 * These should be small enough for stack allocations because the current limit
87 * for #Curve.bevresol is 32. */
88 float *quarter_coords_x = static_cast<float *>(alloca(sizeof(float) * (cu->bevresol + 1)));
89 float *quarter_coords_y = static_cast<float *>(alloca(sizeof(float) * (cu->bevresol + 1)));
90 bevel_quarter_fill(cu, quarter_coords_x, quarter_coords_y);
91
92 int nr;
93 if (fill_type == FULL) {
94 /* The full loop. */
95 nr = 4 * cu->bevresol + (use_extrude ? 6 : 4);
97 }
98 else if (fill_type == HALF) {
99 /* Half the loop. */
100 nr = 2 * (cu->bevresol + 1) + (use_extrude ? 2 : 1);
102 }
103 else {
104 /* One quarter of the loop (just front or back). */
105 nr = use_extrude ? cu->bevresol + 3 : cu->bevresol + 2;
106 dl->flag = (fill_type == FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE;
107 }
108
109 dl->verts = static_cast<float *>(MEM_malloc_arrayN(nr, sizeof(float[3]), __func__));
110 BLI_addtail(disp, dl);
111 /* Use a different type depending on whether the loop is complete or not. */
112 dl->type = (fill_type == FULL) ? DL_POLY : DL_SEGM;
113 dl->parts = 1;
114 dl->nr = nr;
115
116 float *fp = dl->verts;
117
118 /* Build the back section. */
119 if (ELEM(fill_type, BACK, HALF, FULL)) {
120 /* Add the bottom vertex. */
121 fp[0] = 0.0f;
122 fp[1] = 0.0f;
123 fp[2] = -cu->extrude - cu->bevel_radius;
124 fp += 3;
125
126 for (int i = cu->bevresol; i >= 0; i--) {
127 fp[0] = 0.0f;
128 fp[1] = quarter_coords_x[i];
129 fp[2] = -quarter_coords_y[i] - cu->extrude;
130 fp += 3;
131 }
132 }
133
134 /* Add the extrusion if we're only building either the back or the front. */
135 if (use_extrude && ELEM(fill_type, FRONT, BACK)) {
136 fp[0] = 0.0f;
137 fp[1] = cu->bevel_radius;
138 fp[2] = (fill_type == FRONT) ? -cu->extrude : cu->extrude;
139 fp += 3;
140 }
141
142 /* Build the front section. */
143 if (ELEM(fill_type, FRONT, HALF, FULL)) {
144 /* Don't duplicate the last back vertex. */
145 const int front_start = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? 1 : 0;
146 for (int i = front_start; i < cu->bevresol + 1; i++) {
147 fp[0] = 0.0f;
148 fp[1] = quarter_coords_x[i];
149 fp[2] = quarter_coords_y[i] + cu->extrude;
150 fp += 3;
151 }
152 /* Add the top vertex. */
153 fp[0] = 0.0f;
154 fp[1] = 0.0f;
155 fp[2] = cu->extrude + cu->bevel_radius;
156 fp += 3;
157 }
158
159 /* Build the other half only if we're building the full loop. */
160 if (fill_type == FULL) {
161 for (int i = cu->bevresol; i > 0; i--) {
162 fp[0] = 0.0f;
163 fp[1] = -quarter_coords_x[i];
164 fp[2] = quarter_coords_y[i] + cu->extrude;
165 fp += 3;
166 }
167
168 if (use_extrude) {
169 /* Add the extrusion. */
170 fp[0] = 0.0f;
171 fp[1] = -cu->bevel_radius;
172 fp[2] = cu->extrude;
173 fp += 3;
174 }
175
176 for (int i = 0; i < cu->bevresol + 1; i++) {
177 fp[0] = 0.0f;
178 fp[1] = -quarter_coords_x[i];
179 fp[2] = -quarter_coords_y[i] - cu->extrude;
180 fp += 3;
181 }
182 }
183}
184
185static void curve_bevel_make_full_circle(const Curve *cu, ListBase *disp)
186{
187 const int nr = 4 + 2 * cu->bevresol;
188
189 DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
190 dl->verts = static_cast<float *>(MEM_malloc_arrayN(nr, sizeof(float[3]), __func__));
191 BLI_addtail(disp, dl);
192 dl->type = DL_POLY;
193 dl->parts = 1;
194 dl->flag = DL_BACK_CURVE;
195 dl->nr = nr;
196
197 float *fp = dl->verts;
198 const float dangle = (2.0f * float(M_PI) / (nr));
199 float angle = -(nr - 1) * dangle;
200
201 for (int i = 0; i < nr; i++) {
202 fp[0] = 0.0;
203 fp[1] = (cosf(angle) * (cu->bevel_radius));
204 fp[2] = (sinf(angle) * (cu->bevel_radius)) - cu->extrude;
205 angle += dangle;
206 fp += 3;
207 }
208}
209
210static void curve_bevel_make_only_extrude(const Curve *cu, ListBase *disp)
211{
212 DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
213 dl->verts = static_cast<float *>(MEM_malloc_arrayN(2, sizeof(float[3]), __func__));
214 BLI_addtail(disp, dl);
215 dl->type = DL_SEGM;
216 dl->parts = 1;
218 dl->nr = 2;
219
220 float *fp = dl->verts;
221 fp[0] = fp[1] = 0.0;
222 fp[2] = -cu->extrude;
223 fp[3] = fp[4] = 0.0;
224 fp[5] = cu->extrude;
225}
226
227static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
228{
229 if (cu->bevobj == nullptr) {
230 return;
231 }
232 if (cu->bevobj->type != OB_CURVES_LEGACY) {
233 return;
234 }
235
236 Curve *bevcu = static_cast<Curve *>(cu->bevobj->data);
237 if (bevcu->extrude == 0.0f && bevcu->bevel_radius == 0.0f) {
238 ListBase bevdisp = {nullptr, nullptr};
239 float facx = cu->bevobj->scale[0];
240 float facy = cu->bevobj->scale[1];
241
242 DispList *dl;
243 if (cu->bevobj->runtime->curve_cache) {
244 dl = static_cast<DispList *>(cu->bevobj->runtime->curve_cache->disp.first);
245 }
246 else {
247 BLI_assert(cu->bevobj->runtime->curve_cache != nullptr);
248 dl = nullptr;
249 }
250
251 while (dl) {
252 if (ELEM(dl->type, DL_POLY, DL_SEGM)) {
253 DispList *dlnew = static_cast<DispList *>(MEM_mallocN(sizeof(DispList), __func__));
254 *dlnew = *dl;
255 dlnew->verts = static_cast<float *>(
256 MEM_malloc_arrayN(dl->parts * dl->nr, sizeof(float[3]), __func__));
257 memcpy(dlnew->verts, dl->verts, sizeof(float[3]) * dl->parts * dl->nr);
258
259 if (dlnew->type == DL_SEGM) {
260 dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE);
261 }
262
263 BLI_addtail(disp, dlnew);
264 float *fp = dlnew->verts;
265 int nr = dlnew->parts * dlnew->nr;
266 while (nr--) {
267 fp[2] = fp[1] * facy;
268 fp[1] = -fp[0] * facx;
269 fp[0] = 0.0;
270 fp += 3;
271 }
272 }
273 dl = dl->next;
274 }
275
276 BKE_displist_free(&bevdisp);
277 }
278}
279
281{
282 ListBase bevel_shape = {nullptr, nullptr};
283
284 if (curve->bevel_mode == CU_BEV_MODE_OBJECT) {
285 if (curve->bevobj != nullptr) {
286 curve_bevel_make_from_object(curve, &bevel_shape);
287 }
288 }
289 else {
290 const bool use_extrude = curve->extrude != 0.0f;
291 const bool use_bevel = curve->bevel_radius != 0.0f;
292 /* Pass. */
293 if (use_extrude && !use_bevel) {
294 curve_bevel_make_only_extrude(curve, &bevel_shape);
295 }
296 else if (use_extrude || use_bevel) {
298
299 if (!use_extrude && fill_type == FULL && curve->bevel_mode == CU_BEV_MODE_ROUND) {
300 curve_bevel_make_full_circle(curve, &bevel_shape);
301 }
302 else {
303 /* The general case for nonzero extrusion or an incomplete loop. */
304 curve_bevel_make_extrude_and_fill(curve, &bevel_shape, use_extrude, fill_type);
305 }
306 }
307 }
308
309 return bevel_shape;
310}
display list (or rather multi purpose list) stuff.
void BKE_displist_free(struct ListBase *lb)
Definition displist.cc:64
@ DL_BACK_CURVE
@ DL_FRONT_CURVE
@ DL_POLY
@ DL_SEGM
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
#define M_PI_2
#define M_PI
#define ELEM(...)
@ CU_FRONT
@ CU_BACK
@ CU_BEV_MODE_OBJECT
@ CU_BEV_MODE_ROUND
Object is a sort of wrapper for general info.
@ OB_CURVES_LEGACY
Read Guarded memory(de)allocation.
static void curve_bevel_make_extrude_and_fill(const Curve *cu, ListBase *disp, const bool use_extrude, const CurveBevelFillType fill_type)
CurveBevelFillType
@ HALF
@ FRONT
@ BACK
@ FULL
static void curve_bevel_make_only_extrude(const Curve *cu, ListBase *disp)
static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve)
ListBase BKE_curve_bevel_make(const Curve *curve)
static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
static void bevel_quarter_fill(const Curve *curve, float *quarter_coords_x, float *quarter_coords_y)
static void curve_bevel_make_full_circle(const Curve *cu, ListBase *disp)
#define sinf(x)
#define cosf(x)
draw_view in_light_buf[] float
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
struct Object * bevobj
short bevresol
float extrude
float bevel_radius
short type
float * verts
struct DispList * next
short flag
ObjectRuntimeHandle * runtime
float scale[3]