Blender V4.5
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->ext = nullptr;
231 td->val = nullptr;
232
233 hdata = initTransDataCurveHandles(td, bezt);
234
235 copy_m3_m3(td->smtx, smtx);
236 copy_m3_m3(td->mtx, mtx);
238 copy_m3_m3(td->axismtx, axismtx);
239 }
240
241 td++;
242 tail++;
243 }
244
245 /* This is the Curve Point, the other two are handles. */
246 if (is_prop_edit || bezt_tx & SEL_F2) {
247 copy_v3_v3(td->iloc, bezt->vec[1]);
248 td->loc = bezt->vec[1];
249 copy_v3_v3(td->center, td->loc);
250 if (bezt->f2 & SELECT) {
251 td->flag = TD_SELECTED;
252 }
253 else {
254 td->flag = 0;
255 }
256 td->ext = nullptr;
257
258 /* TODO: make points scale. */
259 if (t->mode == TFM_CURVE_SHRINKFATTEN /* `|| t->mode == TFM_RESIZE` */) {
260 td->val = &(bezt->radius);
261 td->ival = bezt->radius;
262 }
263 else if (t->mode == TFM_TILT) {
264 td->val = &(bezt->tilt);
265 td->ival = bezt->tilt;
266 }
267 else {
268 td->val = nullptr;
269 }
270
271 copy_m3_m3(td->smtx, smtx);
272 copy_m3_m3(td->mtx, mtx);
274 copy_m3_m3(td->axismtx, axismtx);
275 }
276
277 if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) {
278 /* If the middle is selected but the sides aren't, this is needed. */
279 if (hdata == nullptr) {
280 /* If the handle was not saved by the previous handle. */
281 hdata = initTransDataCurveHandles(td, bezt);
282 }
283 }
284
285 td++;
286 tail++;
287 }
288 if (is_prop_edit || bezt_tx & SEL_F3) {
289 copy_v3_v3(td->iloc, bezt->vec[2]);
290 td->loc = bezt->vec[2];
291 copy_v3_v3(td->center,
292 bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
293 (bezt->f2 & SELECT)) ?
294 1 :
295 2]);
296 if (hide_handles) {
297 if (bezt->f2 & SELECT) {
298 td->flag = TD_SELECTED;
299 }
300 else {
301 td->flag = 0;
302 }
303 }
304 else {
305 if (bezt->f3 & SELECT) {
306 td->flag = TD_SELECTED;
307 }
308 else {
309 td->flag = 0;
310 }
311 }
312 td->ext = nullptr;
313 td->val = nullptr;
314
315 if (hdata == nullptr) {
316 /* If the handle was not saved by the previous handle. */
317 hdata = initTransDataCurveHandles(td, bezt);
318 }
319
320 copy_m3_m3(td->smtx, smtx);
321 copy_m3_m3(td->mtx, mtx);
323 copy_m3_m3(td->axismtx, axismtx);
324 }
325
326 td++;
327 tail++;
328 }
329
330 (void)hdata; /* Quiet warning. */
331 }
332 }
333 }
334 else {
335 for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
336 if (bp->hide == 0) {
337 if (is_prop_edit || (bp->f1 & SELECT)) {
338 copy_v3_v3(td->iloc, bp->vec);
339 td->loc = bp->vec;
340 copy_v3_v3(td->center, td->loc);
341 if (bp->f1 & SELECT) {
342 td->flag = TD_SELECTED;
343 has_any_selected |= true;
344 }
345 else {
346 td->flag = 0;
347 }
348 td->ext = nullptr;
349
351 td->val = &(bp->radius);
352 td->ival = bp->radius;
353 }
354 else {
355 td->val = &(bp->tilt);
356 td->ival = bp->tilt;
357 }
358
359 copy_m3_m3(td->smtx, smtx);
360 copy_m3_m3(td->mtx, mtx);
361
363 if (nu->pntsv == 1) {
364 float normal[3], plane[3];
365
366 BKE_nurb_bpoint_calc_normal(nu, bp, normal);
367 BKE_nurb_bpoint_calc_plane(nu, bp, plane);
368
370 }
371 }
372
373 td++;
374 tail++;
375 }
376 }
377 }
378 }
379 if (is_prop_edit && head != tail) {
380 tail -= 1;
381 if (is_prop_connected && has_any_selected) {
382 bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
383 calc_distanceCurveVerts(head, tail, cyclic);
384 }
385 else {
386 for (td = head; td <= tail; td++) {
387 td->dist = FLT_MAX;
388 }
389 }
390 }
391
392 /* TODO: in the case of tilt and radius we can also avoid allocating the
393 * #initTransDataCurveHandles but for now just don't change handle types. */
394 if ((nu->type == CU_BEZIER) &&
396 {
397 /* Sets the handles based on their selection,
398 * do this after the data is copied to the #TransData. */
399 BKE_nurb_handles_test(nu, handle_mode, use_around_origins_for_handles_test);
400 }
401 }
402 }
403#undef SEL_F1
404#undef SEL_F2
405#undef SEL_F3
406}
407
409{
410 if (t->state != TRANS_CANCEL) {
412 }
413
415 Curve *cu = static_cast<Curve *>(tc->obedit->data);
417 Nurb *nu = static_cast<Nurb *>(nurbs->first);
418
419 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
420
421 if (t->state == TRANS_CANCEL) {
422 while (nu) {
423 /* Can't do testhandlesNurb here, it messes up the h1 and h2 flags. */
425 nu = nu->next;
426 }
427 }
428 else {
429 /* Apply clipping after so we never project past the clip plane #25423. */
431
432 /* Normal updating. */
434 }
435 }
436}
437
439
441 /*flags*/ (T_EDIT | T_POINTS),
442 /*create_trans_data*/ createTransCurveVerts,
443 /*recalc_data*/ recalcData_curve,
444 /*special_aftertrans_update*/ nullptr,
445};
446
447} // 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:3961
void BKE_nurb_bpoint_calc_plane(Nurb *nu, BPoint *bp, float r_plane[3])
Definition curve.cc:1086
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:4019
void BKE_nurb_handles_test(Nurb *nu, eNurbHandleTest_Mode handle_mode, bool use_around_local)
Definition curve.cc:4088
void BKE_curve_dimension_update(Curve *cu)
Definition curve.cc:441
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:423
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1011
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1063
void BKE_nurb_bezt_calc_plane(Nurb *nu, BezTriple *bezt, float r_plane[3])
Definition curve.cc:1026
#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:982
@ 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:4015
#define SEL_F2
Definition curve.cc:4016
#define SEL_F3
Definition curve.cc:4017
#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:404
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:139