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