Blender V5.0
transform_convert_curve.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_curve_types.h"
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_listbase.h"
14#include "BLI_math_matrix.h"
15#include "BLI_math_vector.h"
16
17#include "BKE_curve.hh"
18
19#include "ED_object.hh"
20
21#include "transform.hh"
22#include "transform_snap.hh"
23
24/* Own include. */
25#include "transform_convert.hh"
27
28namespace blender::ed::transform {
29
30/* -------------------------------------------------------------------- */
33
39 const eNurbHandleTest_Mode handle_mode)
40{
41 int flag = BKE_nurb_bezt_handle_test_calc_flag(bezt, SELECT, handle_mode);
42
43 /* Special case for auto & aligned handles:
44 * When a center point is being moved without the handles,
45 * leaving the handles stationary makes no sense and only causes strange behavior,
46 * where one handle is arbitrarily anchored, the other one is aligned and lengthened
47 * based on where the center point is moved. Also a bug when canceling, see: #52007.
48 *
49 * A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
50 * However that doesn't resolve odd behavior, so best transform the handles in this case.
51 */
52 if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) {
53 if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
54 flag = (1 << 0) | (1 << 1) | (1 << 2);
55 }
56 }
57
58 return flag;
59}
60
62{
63
64#define SEL_F1 (1 << 0)
65#define SEL_F2 (1 << 1)
66#define SEL_F3 (1 << 2)
67
68 t->data_len_all = 0;
69
70 /* Count control points (one per #BezTriple) if any number of handles are selected.
71 * Needed for #transform_around_single_fallback_ex. */
72 int data_len_all_pt = 0;
73
74 const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
75 const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
76 View3D *v3d = static_cast<View3D *>(t->view);
77 short hide_handles = (v3d != nullptr) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
78 false;
79 const eNurbHandleTest_Mode handle_mode = hide_handles ? NURB_HANDLE_TEST_KNOT_ONLY :
81
83 Curve *cu = static_cast<Curve *>(tc->obedit->data);
84 BLI_assert(cu->editnurb != nullptr);
85 BezTriple *bezt;
86 BPoint *bp;
87 int a;
88 int count = 0, countsel = 0;
89 int count_pt = 0, countsel_pt = 0;
90
91 /* Avoid editing locked shapes. */
92 if (t->mode != TFM_DUMMY && object::shape_key_report_if_locked(tc->obedit, t->reports)) {
93 continue;
94 }
95
96 /* Count total of vertices, check identical as in 2nd loop for making transdata! */
98 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
99 if (nu->type == CU_BEZIER) {
100 for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
101 if (bezt->hide == 0) {
102 const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, handle_mode);
103 if (bezt_tx & (SEL_F1 | SEL_F2 | SEL_F3)) {
104 if (bezt_tx & SEL_F1) {
105 countsel++;
106 }
107 if (bezt_tx & SEL_F2) {
108 countsel++;
109 }
110 if (bezt_tx & SEL_F3) {
111 countsel++;
112 }
113 countsel_pt++;
114 }
115 if (is_prop_edit) {
116 count += 3;
117 count_pt++;
118 }
119 }
120 }
121 }
122 else {
123 for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
124 if (bp->hide == 0) {
125 if (bp->f1 & SELECT) {
126 countsel++;
127 countsel_pt++;
128 }
129 if (is_prop_edit) {
130 count++;
131 count_pt++;
132 }
133 }
134 }
135 }
136 }
137
138 /* Support other objects using proportional editing to adjust these, unless connected is
139 * enabled. */
140 if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
141 tc->data_len = 0;
142 continue;
143 }
144
145 int data_len_pt = 0;
146
147 if (is_prop_edit) {
148 tc->data_len = count;
149 data_len_pt = count_pt;
150 }
151 else {
152 tc->data_len = countsel;
153 data_len_pt = countsel_pt;
154 }
155 tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransObData(Curve EditMode)");
156
157 t->data_len_all += tc->data_len;
158 data_len_all_pt += data_len_pt;
159 }
160
161 transform_around_single_fallback_ex(t, data_len_all_pt);
162 t->data_len_all = -1;
163
165 if (tc->data_len == 0) {
166 continue;
167 }
168
169 Curve *cu = static_cast<Curve *>(tc->obedit->data);
170 BezTriple *bezt;
171 BPoint *bp;
172 int a;
173
174 bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
176 float mtx[3][3], smtx[3][3];
177
178 copy_m3_m4(mtx, tc->obedit->object_to_world().ptr());
180
181 TransData *td = tc->data;
183 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
184 TransData *head, *tail;
185 head = tail = td;
186 bool has_any_selected = false;
187 if (nu->type == CU_BEZIER) {
188 for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
189 if (bezt->hide == 0) {
190 TransDataCurveHandleFlags *hdata = nullptr;
191 float axismtx[3][3];
192
194 float normal[3], plane[3];
195
196 BKE_nurb_bezt_calc_normal(nu, bezt, normal);
197 BKE_nurb_bezt_calc_plane(nu, bezt, plane);
198
199 createSpaceNormalTangent_or_fallback(axismtx, normal, plane);
200 }
201
202 /* Elements that will be transform (not always a match to selection). */
203 const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, handle_mode);
204 has_any_selected |= bezt_tx != 0;
205
206 if (is_prop_edit || bezt_tx & SEL_F1) {
207 copy_v3_v3(td->iloc, bezt->vec[0]);
208 td->loc = bezt->vec[0];
209 copy_v3_v3(td->center,
210 bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
211 (bezt->f2 & SELECT)) ?
212 1 :
213 0]);
214 if (hide_handles) {
215 if (bezt->f2 & SELECT) {
216 td->flag = TD_SELECTED;
217 }
218 else {
219 td->flag = 0;
220 }
221 }
222 else {
223 if (bezt->f1 & SELECT) {
224 td->flag = TD_SELECTED;
225 }
226 else {
227 td->flag = 0;
228 }
229 }
230 td->val = nullptr;
231
232 hdata = initTransDataCurveHandles(td, bezt);
233
234 copy_m3_m3(td->smtx, smtx);
235 copy_m3_m3(td->mtx, mtx);
237 copy_m3_m3(td->axismtx, axismtx);
238 }
239
240 td++;
241 tail++;
242 }
243
244 /* This is the Curve Point, the other two are handles. */
245 if (is_prop_edit || bezt_tx & SEL_F2) {
246 copy_v3_v3(td->iloc, bezt->vec[1]);
247 td->loc = bezt->vec[1];
248 copy_v3_v3(td->center, td->loc);
249 if (bezt->f2 & SELECT) {
250 td->flag = TD_SELECTED;
251 }
252 else {
253 td->flag = 0;
254 }
255
256 /* TODO: make points scale. */
257 if (t->mode == TFM_CURVE_SHRINKFATTEN /* `|| t->mode == TFM_RESIZE` */) {
258 td->val = &(bezt->radius);
259 td->ival = bezt->radius;
260 }
261 else if (t->mode == TFM_TILT) {
262 td->val = &(bezt->tilt);
263 td->ival = bezt->tilt;
264 }
265 else {
266 td->val = nullptr;
267 }
268
269 copy_m3_m3(td->smtx, smtx);
270 copy_m3_m3(td->mtx, mtx);
272 copy_m3_m3(td->axismtx, axismtx);
273 }
274
275 if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) {
276 /* If the middle is selected but the sides aren't, this is needed. */
277 if (hdata == nullptr) {
278 /* If the handle was not saved by the previous handle. */
279 hdata = initTransDataCurveHandles(td, bezt);
280 }
281 }
282
283 td++;
284 tail++;
285 }
286 if (is_prop_edit || bezt_tx & SEL_F3) {
287 copy_v3_v3(td->iloc, bezt->vec[2]);
288 td->loc = bezt->vec[2];
289 copy_v3_v3(td->center,
290 bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
291 (bezt->f2 & SELECT)) ?
292 1 :
293 2]);
294 if (hide_handles) {
295 if (bezt->f2 & SELECT) {
296 td->flag = TD_SELECTED;
297 }
298 else {
299 td->flag = 0;
300 }
301 }
302 else {
303 if (bezt->f3 & SELECT) {
304 td->flag = TD_SELECTED;
305 }
306 else {
307 td->flag = 0;
308 }
309 }
310 td->val = nullptr;
311
312 if (hdata == nullptr) {
313 /* If the handle was not saved by the previous handle. */
314 hdata = initTransDataCurveHandles(td, bezt);
315 }
316
317 copy_m3_m3(td->smtx, smtx);
318 copy_m3_m3(td->mtx, mtx);
320 copy_m3_m3(td->axismtx, axismtx);
321 }
322
323 td++;
324 tail++;
325 }
326
327 (void)hdata; /* Quiet warning. */
328 }
329 }
330 }
331 else {
332 for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
333 if (bp->hide == 0) {
334 if (is_prop_edit || (bp->f1 & SELECT)) {
335 copy_v3_v3(td->iloc, bp->vec);
336 td->loc = bp->vec;
337 copy_v3_v3(td->center, td->loc);
338 if (bp->f1 & SELECT) {
339 td->flag = TD_SELECTED;
340 has_any_selected |= true;
341 }
342 else {
343 td->flag = 0;
344 }
345
347 td->val = &(bp->radius);
348 td->ival = bp->radius;
349 }
350 else {
351 td->val = &(bp->tilt);
352 td->ival = bp->tilt;
353 }
354
355 copy_m3_m3(td->smtx, smtx);
356 copy_m3_m3(td->mtx, mtx);
357
359 if (nu->pntsv == 1) {
360 float normal[3], plane[3];
361
362 BKE_nurb_bpoint_calc_normal(nu, bp, normal);
363 BKE_nurb_bpoint_calc_plane(nu, bp, plane);
364
366 }
367 }
368
369 td++;
370 tail++;
371 }
372 }
373 }
374 }
375 if (is_prop_edit && head != tail) {
376 tail -= 1;
377 if (is_prop_connected && has_any_selected) {
378 bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
379 calc_distanceCurveVerts(head, tail, cyclic);
380 }
381 else {
382 for (td = head; td <= tail; td++) {
383 td->dist = FLT_MAX;
384 }
385 }
386 }
387
388 /* TODO: in the case of tilt and radius we can also avoid allocating the
389 * #initTransDataCurveHandles but for now just don't change handle types. */
390 if ((nu->type == CU_BEZIER) &&
392 {
393 /* Sets the handles based on their selection,
394 * do this after the data is copied to the #TransData. */
395 BKE_nurb_handles_test(nu, handle_mode, use_around_origins_for_handles_test);
396 }
397 }
398 }
399#undef SEL_F1
400#undef SEL_F2
401#undef SEL_F3
402}
403
405{
406 if (t->state != TRANS_CANCEL) {
408 }
409
411 Curve *cu = static_cast<Curve *>(tc->obedit->data);
413 Nurb *nu = static_cast<Nurb *>(nurbs->first);
414
415 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
416
417 if (t->state == TRANS_CANCEL) {
418 while (nu) {
419 /* Can't do testhandlesNurb here, it messes up the h1 and h2 flags. */
421 nu = nu->next;
422 }
423 }
424 else {
425 /* Apply clipping after so we never project past the clip plane #25423. */
427
428 /* Normal updating. */
430 }
431 }
432}
433
435
437 /*flags*/ (T_EDIT | T_POINTS),
438 /*create_trans_data*/ createTransCurveVerts,
439 /*recalc_data*/ recalcData_curve,
440 /*special_aftertrans_update*/ nullptr,
441};
442
443} // namespace blender::ed::transform
eNurbHandleTest_Mode
Definition BKE_curve.hh:59
@ NURB_HANDLE_TEST_KNOT_OR_EACH
Definition BKE_curve.hh:66
@ NURB_HANDLE_TEST_KNOT_ONLY
Definition BKE_curve.hh:71
void BKE_nurb_handles_calc(Nurb *nu)
Definition curve.cc:3957
void BKE_nurb_bpoint_calc_plane(Nurb *nu, BPoint *bp, float r_plane[3])
Definition curve.cc:1082
short BKE_nurb_bezt_handle_test_calc_flag(const BezTriple *bezt, const eBezTriple_Flag__Alias sel_flag, const eNurbHandleTest_Mode handle_mode)
Definition curve.cc:4015
void BKE_nurb_handles_test(Nurb *nu, eNurbHandleTest_Mode handle_mode, bool use_around_local)
Definition curve.cc:4084
void BKE_curve_dimension_update(Curve *cu)
Definition curve.cc:437
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:419
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1007
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1059
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1022
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon)
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
#define PSEUDOINVERSE_EPSILON
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CU_NURB_CYCLIC
@ CU_BEZIER
@ HD_AUTO
@ HD_ALIGN
@ V3D_AROUND_LOCAL_ORIGINS
@ CURVE_HANDLE_NONE
Read Guarded memory(de)allocation.
#define SEL_F1
Definition curve.cc:4011
#define SEL_F2
Definition curve.cc:4012
#define SEL_F3
Definition curve.cc:4013
#define SELECT
int count
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
bool shape_key_report_if_locked(const Object *obedit, ReportList *reports)
void createSpaceNormalTangent_or_fallback(float mat[3][3], const float normal[3], const float tangent[3])
static void createTransCurveVerts(bContext *, TransInfo *t)
bool transform_mode_use_local_origins(const TransInfo *t)
TransConvertTypeInfo TransConvertType_Curve
static void recalcData_curve(TransInfo *t)
void transform_snap_project_individual_apply(TransInfo *t)
static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const eNurbHandleTest_Mode handle_mode)
TransDataCurveHandleFlags * initTransDataCurveHandles(TransData *td, BezTriple *bezt)
void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
void transform_around_single_fallback_ex(TransInfo *t, int data_len_all)
void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
#define FLT_MAX
Definition stdcycles.h:14
uint8_t f1
float vec[4]
float vec[3][3]
EditNurb * editnurb
Definition DNA_ID.h:414
void * first
struct Nurb * next
View3DOverlay overlay
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
conversion and adaptation of different datablocks to a common struct.
uint8_t flag
Definition wm_window.cc:145