Blender V4.3
editcurve.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_anim_types.h"
10#include "DNA_key_types.h"
11#include "DNA_object_types.h"
12#include "DNA_scene_types.h"
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_array_utils.h"
17#include "BLI_blenlib.h"
18#include "BLI_ghash.h"
20#include "BLI_math_geom.h"
21#include "BLI_math_matrix.h"
22#include "BLI_math_rotation.h"
23#include "BLI_math_vector.h"
24#include "BLI_set.hh"
25#include "BLI_span.hh"
26
27#include "BLT_translation.hh"
28
29#include "BKE_action.hh"
30#include "BKE_anim_data.hh"
31#include "BKE_context.hh"
32#include "BKE_curve.hh"
33#include "BKE_displist.h"
34#include "BKE_fcurve.hh"
35#include "BKE_global.hh"
36#include "BKE_key.hh"
37#include "BKE_layer.hh"
38#include "BKE_lib_id.hh"
39#include "BKE_main.hh"
40#include "BKE_modifier.hh"
41#include "BKE_object_types.hh"
42#include "BKE_report.hh"
43
44#include "ANIM_action.hh"
45#include "ANIM_action_legacy.hh"
46
47#include "DEG_depsgraph.hh"
50
51#include "WM_api.hh"
52#include "WM_types.hh"
53
54#include "ED_curve.hh"
55#include "ED_object.hh"
56#include "ED_outliner.hh"
57#include "ED_screen.hh"
58#include "ED_select_utils.hh"
59#include "ED_transform.hh"
61#include "ED_view3d.hh"
62
63#include "curve_intern.hh"
64
65extern "C" {
66#include "curve_fit_nd.h"
67}
68
69#include "UI_interface.hh"
70#include "UI_resources.hh"
71
72#include "RNA_access.hh"
73#include "RNA_define.hh"
74#include "RNA_enum_types.hh"
75
76using blender::Vector;
77
78void selectend_nurb(Object *obedit, enum eEndPoint_Types selfirst, bool doswap, bool selstatus);
79static void adduplicateflagNurb(
80 Object *obedit, View3D *v3d, ListBase *newnurb, const uint8_t flag, const bool split);
81static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split);
82static bool curve_delete_vertices(Object *obedit, View3D *v3d);
83
84/* -------------------------------------------------------------------- */
89{
90 if (ob && ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
91 Curve *cu = static_cast<Curve *>(ob->data);
92 return &cu->editnurb->nurbs;
93 }
94 return nullptr;
95}
96
98{
100
101 return BKE_keyblock_find_by_index(cu->key, cu->editnurb->shapenr - 1);
102}
103
106/* -------------------------------------------------------------------- */
110#if 0
111void printknots(Object *obedit)
112{
113 ListBase *editnurb = object_editcurve_get(obedit);
114 Nurb *nu;
115 int a, num;
116
117 for (nu = editnurb->first; nu; nu = nu->next) {
118 if (ED_curve_nurb_select_check(nu) && nu->type == CU_NURBS) {
119 if (nu->knotsu) {
120 num = KNOTSU(nu);
121 for (a = 0; a < num; a++) {
122 printf("knotu %d: %f\n", a, nu->knotsu[a]);
123 }
124 }
125 if (nu->knotsv) {
126 num = KNOTSV(nu);
127 for (a = 0; a < num; a++) {
128 printf("knotv %d: %f\n", a, nu->knotsv[a]);
129 }
130 }
131 }
132 }
133}
134#endif
135
138/* -------------------------------------------------------------------- */
143 void *cv, int key_index, int nu_index, int pt_index, int vertex_index)
144{
145 CVKeyIndex *cvIndex = static_cast<CVKeyIndex *>(MEM_callocN(sizeof(CVKeyIndex), __func__));
146
147 cvIndex->orig_cv = cv;
148 cvIndex->key_index = key_index;
149 cvIndex->nu_index = nu_index;
150 cvIndex->pt_index = pt_index;
151 cvIndex->vertex_index = vertex_index;
152 cvIndex->switched = false;
153
154 return cvIndex;
155}
156
157static void init_editNurb_keyIndex(EditNurb *editnurb, ListBase *origBase)
158{
159 Nurb *nu = static_cast<Nurb *>(editnurb->nurbs.first);
160 Nurb *orignu = static_cast<Nurb *>(origBase->first);
161 GHash *gh;
162 BezTriple *bezt, *origbezt;
163 BPoint *bp, *origbp;
164 CVKeyIndex *keyIndex;
165 int a, key_index = 0, nu_index = 0, pt_index = 0, vertex_index = 0;
166
167 if (editnurb->keyindex) {
168 return;
169 }
170
171 gh = BLI_ghash_ptr_new("editNurb keyIndex");
172
173 while (orignu) {
174 if (orignu->bezt) {
175 a = orignu->pntsu;
176 bezt = nu->bezt;
177 origbezt = orignu->bezt;
178 pt_index = 0;
179 while (a--) {
180 /* We cannot keep *any* reference to curve obdata,
181 * it might be replaced and freed while editcurve remain in use
182 * (in viewport render case e.g.). Note that we could use a pool to avoid
183 * lots of malloc's here, but... not really a problem for now. */
184 BezTriple *origbezt_cpy = static_cast<BezTriple *>(
185 MEM_mallocN(sizeof(*origbezt), __func__));
186 *origbezt_cpy = *origbezt;
187 keyIndex = init_cvKeyIndex(origbezt_cpy, key_index, nu_index, pt_index, vertex_index);
188 BLI_ghash_insert(gh, bezt, keyIndex);
189 key_index += KEYELEM_FLOAT_LEN_BEZTRIPLE;
190 vertex_index += 3;
191 bezt++;
192 origbezt++;
193 pt_index++;
194 }
195 }
196 else {
197 a = orignu->pntsu * orignu->pntsv;
198 bp = nu->bp;
199 origbp = orignu->bp;
200 pt_index = 0;
201 while (a--) {
202 /* We cannot keep *any* reference to curve obdata,
203 * it might be replaced and freed while editcurve remain in use
204 * (in viewport render case e.g.). Note that we could use a pool to avoid
205 * lots of malloc's here, but... not really a problem for now. */
206 BPoint *origbp_cpy = static_cast<BPoint *>(MEM_mallocN(sizeof(*origbp_cpy), __func__));
207 *origbp_cpy = *origbp;
208 keyIndex = init_cvKeyIndex(origbp_cpy, key_index, nu_index, pt_index, vertex_index);
209 BLI_ghash_insert(gh, bp, keyIndex);
210 key_index += KEYELEM_FLOAT_LEN_BPOINT;
211 bp++;
212 origbp++;
213 pt_index++;
214 vertex_index++;
215 }
216 }
217
218 nu = nu->next;
219 orignu = orignu->next;
220 nu_index++;
221 }
222
223 editnurb->keyindex = gh;
224}
225
226static CVKeyIndex *getCVKeyIndex(EditNurb *editnurb, const void *cv)
227{
228 return static_cast<CVKeyIndex *>(BLI_ghash_lookup(editnurb->keyindex, cv));
229}
230
231static CVKeyIndex *popCVKeyIndex(EditNurb *editnurb, const void *cv)
232{
233 return static_cast<CVKeyIndex *>(BLI_ghash_popkey(editnurb->keyindex, cv, nullptr));
234}
235
236static BezTriple *getKeyIndexOrig_bezt(EditNurb *editnurb, const BezTriple *bezt)
237{
238 CVKeyIndex *index = getCVKeyIndex(editnurb, bezt);
239
240 if (!index) {
241 return nullptr;
242 }
243
244 return (BezTriple *)index->orig_cv;
245}
246
248{
249 CVKeyIndex *index = getCVKeyIndex(editnurb, bp);
250
251 if (!index) {
252 return nullptr;
253 }
254
255 return (BPoint *)index->orig_cv;
256}
257
258static int getKeyIndexOrig_keyIndex(EditNurb *editnurb, void *cv)
259{
260 CVKeyIndex *index = getCVKeyIndex(editnurb, cv);
261
262 if (!index) {
263 return -1;
264 }
265
266 return index->key_index;
267}
268
269static void keyIndex_delBezt(EditNurb *editnurb, BezTriple *bezt)
270{
271 if (!editnurb->keyindex) {
272 return;
273 }
274
276}
277
278static void keyIndex_delBP(EditNurb *editnurb, BPoint *bp)
279{
280 if (!editnurb->keyindex) {
281 return;
282 }
283
285}
286
287static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu)
288{
289 int a;
290
291 if (!editnurb->keyindex) {
292 return;
293 }
294
295 if (nu->bezt) {
296 const BezTriple *bezt = nu->bezt;
297 a = nu->pntsu;
298
299 while (a--) {
301 bezt++;
302 }
303 }
304 else {
305 const BPoint *bp = nu->bp;
306 a = nu->pntsu * nu->pntsv;
307
308 while (a--) {
310 bp++;
311 }
312 }
313}
314
315static void keyIndex_delNurbList(EditNurb *editnurb, ListBase *nubase)
316{
317 LISTBASE_FOREACH (Nurb *, nu, nubase) {
318 keyIndex_delNurb(editnurb, nu);
319 }
320}
321
322static void keyIndex_updateCV(EditNurb *editnurb, char *cv, char *newcv, int count, int size)
323{
324 int i;
325 CVKeyIndex *index;
326
327 if (editnurb->keyindex == nullptr) {
328 /* No shape keys - updating not needed */
329 return;
330 }
331
332 for (i = 0; i < count; i++) {
333 index = popCVKeyIndex(editnurb, cv);
334
335 if (index) {
336 BLI_ghash_insert(editnurb->keyindex, newcv, index);
337 }
338
339 newcv += size;
340 cv += size;
341 }
342}
343
344static void keyIndex_updateBezt(EditNurb *editnurb, BezTriple *bezt, BezTriple *newbezt, int count)
345{
346 keyIndex_updateCV(editnurb, (char *)bezt, (char *)newbezt, count, sizeof(BezTriple));
347}
348
349static void keyIndex_updateBP(EditNurb *editnurb, BPoint *bp, BPoint *newbp, int count)
350{
351 keyIndex_updateCV(editnurb, (char *)bp, (char *)newbp, count, sizeof(BPoint));
352}
353
355{
356 if (nu->bezt) {
357 keyIndex_updateBezt(editnurb, nu->bezt, newnu->bezt, newnu->pntsu);
358 }
359 else {
360 keyIndex_updateBP(editnurb, nu->bp, newnu->bp, newnu->pntsu * newnu->pntsv);
361 }
362}
363
364static void keyIndex_swap(EditNurb *editnurb, void *a, void *b)
365{
366 CVKeyIndex *index1 = popCVKeyIndex(editnurb, a);
367 CVKeyIndex *index2 = popCVKeyIndex(editnurb, b);
368
369 if (index2) {
370 BLI_ghash_insert(editnurb->keyindex, a, index2);
371 }
372 if (index1) {
373 BLI_ghash_insert(editnurb->keyindex, b, index1);
374 }
375}
376
377static void keyIndex_switchDirection(EditNurb *editnurb, Nurb *nu)
378{
379 int a;
380 CVKeyIndex *index1, *index2;
381
382 if (nu->bezt) {
383 BezTriple *bezt1, *bezt2;
384
385 a = nu->pntsu;
386
387 bezt1 = nu->bezt;
388 bezt2 = bezt1 + (a - 1);
389
390 if (a & 1) {
391 a++;
392 }
393
394 a /= 2;
395
396 while (a--) {
397 index1 = getCVKeyIndex(editnurb, bezt1);
398 index2 = getCVKeyIndex(editnurb, bezt2);
399
400 if (index1) {
401 index1->switched = !index1->switched;
402 }
403
404 if (bezt1 != bezt2) {
405 keyIndex_swap(editnurb, bezt1, bezt2);
406
407 if (index2) {
408 index2->switched = !index2->switched;
409 }
410 }
411
412 bezt1++;
413 bezt2--;
414 }
415 }
416 else {
417 BPoint *bp1, *bp2;
418
419 if (nu->pntsv == 1) {
420 a = nu->pntsu;
421 bp1 = nu->bp;
422 bp2 = bp1 + (a - 1);
423 a /= 2;
424 while (bp1 != bp2 && a > 0) {
425 index1 = getCVKeyIndex(editnurb, bp1);
426 index2 = getCVKeyIndex(editnurb, bp2);
427
428 if (index1) {
429 index1->switched = !index1->switched;
430 }
431
432 if (bp1 != bp2) {
433 if (index2) {
434 index2->switched = !index2->switched;
435 }
436
437 keyIndex_swap(editnurb, bp1, bp2);
438 }
439
440 a--;
441 bp1++;
442 bp2--;
443 }
444 }
445 else {
446 int b;
447
448 for (b = 0; b < nu->pntsv; b++) {
449
450 bp1 = &nu->bp[b * nu->pntsu];
451 a = nu->pntsu;
452 bp2 = bp1 + (a - 1);
453 a /= 2;
454
455 while (bp1 != bp2 && a > 0) {
456 index1 = getCVKeyIndex(editnurb, bp1);
457 index2 = getCVKeyIndex(editnurb, bp2);
458
459 if (index1) {
460 index1->switched = !index1->switched;
461 }
462
463 if (bp1 != bp2) {
464 if (index2) {
465 index2->switched = !index2->switched;
466 }
467
468 keyIndex_swap(editnurb, bp1, bp2);
469 }
470
471 a--;
472 bp1++;
473 bp2--;
474 }
475 }
476 }
477 }
478}
479
480static void switch_keys_direction(Curve *cu, Nurb *actnu)
481{
482 EditNurb *editnurb = cu->editnurb;
483 ListBase *nubase = &editnurb->nurbs;
484 float *fp;
485 int a;
486
487 LISTBASE_FOREACH (KeyBlock *, currkey, &cu->key->block) {
488 fp = static_cast<float *>(currkey->data);
489
490 LISTBASE_FOREACH (Nurb *, nu, nubase) {
491 if (nu->bezt) {
492 BezTriple *bezt = nu->bezt;
493 a = nu->pntsu;
494 if (nu == actnu) {
495 while (a--) {
496 if (getKeyIndexOrig_bezt(editnurb, bezt)) {
497 swap_v3_v3(fp, fp + 6);
498 *(fp + 9) = -*(fp + 9);
500 }
501 bezt++;
502 }
503 }
504 else {
506 }
507 }
508 else {
509 BPoint *bp = nu->bp;
510 a = nu->pntsu * nu->pntsv;
511 if (nu == actnu) {
512 while (a--) {
513 if (getKeyIndexOrig_bp(editnurb, bp)) {
514 *(fp + 3) = -*(fp + 3);
516 }
517 bp++;
518 }
519 }
520 else {
521 fp += a * KEYELEM_FLOAT_LEN_BPOINT;
522 }
523 }
524 }
525 }
526}
527
529{
530 EditNurb *editnurb = cu->editnurb;
531
532 if (!editnurb->keyindex) {
533 /* no shape keys - nothing to do */
534 return;
535 }
536
537 keyIndex_switchDirection(editnurb, nu);
538 if (cu->key) {
539 switch_keys_direction(cu, nu);
540 }
541}
542
544{
545 GHash *gh;
546 GHashIterator gh_iter;
547
548 gh = BLI_ghash_ptr_new_ex("dupli_keyIndex gh", BLI_ghash_len(keyindex));
549
550 GHASH_ITER (gh_iter, keyindex) {
551 void *cv = BLI_ghashIterator_getKey(&gh_iter);
552 CVKeyIndex *index = static_cast<CVKeyIndex *>(BLI_ghashIterator_getValue(&gh_iter));
553 CVKeyIndex *newIndex = static_cast<CVKeyIndex *>(
554 MEM_mallocN(sizeof(CVKeyIndex), "dupli_keyIndexHash index"));
555
556 memcpy(newIndex, index, sizeof(CVKeyIndex));
557 newIndex->orig_cv = MEM_dupallocN(index->orig_cv);
558
559 BLI_ghash_insert(gh, cv, newIndex);
560 }
561
562 return gh;
563}
564
565static void key_to_bezt(float *key, BezTriple *basebezt, BezTriple *bezt)
566{
567 memcpy(bezt, basebezt, sizeof(BezTriple));
568 memcpy(bezt->vec, key, sizeof(float[9]));
569 bezt->tilt = key[9];
570 bezt->radius = key[10];
571}
572
573static void bezt_to_key(BezTriple *bezt, float *key)
574{
575 memcpy(key, bezt->vec, sizeof(float[9]));
576 key[9] = bezt->tilt;
577 key[10] = bezt->radius;
578}
579
580static void calc_keyHandles(ListBase *nurb, float *key)
581{
582 int a;
583 float *fp = key;
584 BezTriple *bezt;
585
586 LISTBASE_FOREACH (Nurb *, nu, nurb) {
587 if (nu->bezt) {
588 BezTriple *prevp, *nextp;
589 BezTriple cur, prev, next;
590 float *startfp, *prevfp, *nextfp;
591
592 bezt = nu->bezt;
593 a = nu->pntsu;
594 startfp = fp;
595
596 if (nu->flagu & CU_NURB_CYCLIC) {
597 prevp = bezt + (a - 1);
598 prevfp = fp + (KEYELEM_FLOAT_LEN_BEZTRIPLE * (a - 1));
599 }
600 else {
601 prevp = nullptr;
602 prevfp = nullptr;
603 }
604
605 nextp = bezt + 1;
606 nextfp = fp + KEYELEM_FLOAT_LEN_BEZTRIPLE;
607
608 while (a--) {
609 key_to_bezt(fp, bezt, &cur);
610
611 if (nextp) {
612 key_to_bezt(nextfp, nextp, &next);
613 }
614 if (prevp) {
615 key_to_bezt(prevfp, prevp, &prev);
616 }
617
618 BKE_nurb_handle_calc(&cur, prevp ? &prev : nullptr, nextp ? &next : nullptr, false, 0);
619 bezt_to_key(&cur, fp);
620
621 prevp = bezt;
622 prevfp = fp;
623 if (a == 1) {
624 if (nu->flagu & CU_NURB_CYCLIC) {
625 nextp = nu->bezt;
626 nextfp = startfp;
627 }
628 else {
629 nextp = nullptr;
630 nextfp = nullptr;
631 }
632 }
633 else {
634 nextp++;
636 }
637
638 bezt++;
640 }
641 }
642 else {
643 a = nu->pntsu * nu->pntsv;
644 fp += a * KEYELEM_FLOAT_LEN_BPOINT;
645 }
646 }
647}
648
649static void calc_shapeKeys(Object *obedit, ListBase *newnurbs)
650{
651 Curve *cu = (Curve *)obedit->data;
652
653 if (cu->key == nullptr) {
654 return;
655 }
656
657 int a, i, currkey_i;
658 EditNurb *editnurb = cu->editnurb;
659 KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&cu->key->block, editnurb->shapenr - 1));
660 BezTriple *bezt, *oldbezt;
661 BPoint *bp, *oldbp;
662 Nurb *newnu;
663 int totvert = BKE_keyblock_curve_element_count(&editnurb->nurbs);
664
665 float(*ofs)[3] = nullptr;
666 std::optional<blender::Array<bool>> dependent;
667 const float *oldkey, *ofp;
668 float *newkey;
669
670 /* editing the base key should update others */
671 if (cu->key->type == KEY_RELATIVE) {
672 dependent = BKE_keyblock_get_dependent_keys(cu->key, editnurb->shapenr - 1);
673
674 if (dependent) { /* active key is a base */
675 int totvec = 0;
676
677 /* Calculate needed memory to store offset */
678 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
679
680 if (nu->bezt) {
681 /* Three vectors to store handles and one for tilt. */
682 totvec += nu->pntsu * 4;
683 }
684 else {
685 totvec += 2 * nu->pntsu * nu->pntsv;
686 }
687 }
688
689 ofs = static_cast<float(*)[3]>(MEM_callocN(sizeof(float[3]) * totvec, "currkey->data"));
690 i = 0;
691 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
692 if (nu->bezt) {
693 bezt = nu->bezt;
694 a = nu->pntsu;
695 while (a--) {
696 oldbezt = getKeyIndexOrig_bezt(editnurb, bezt);
697
698 if (oldbezt) {
699 int j;
700 for (j = 0; j < 3; j++) {
701 sub_v3_v3v3(ofs[i], bezt->vec[j], oldbezt->vec[j]);
702 i++;
703 }
704 ofs[i][0] = bezt->tilt - oldbezt->tilt;
705 ofs[i][1] = bezt->radius - oldbezt->radius;
706 i++;
707 }
708 else {
709 i += 4;
710 }
711 bezt++;
712 }
713 }
714 else {
715 bp = nu->bp;
716 a = nu->pntsu * nu->pntsv;
717 while (a--) {
718 oldbp = getKeyIndexOrig_bp(editnurb, bp);
719 if (oldbp) {
720 sub_v3_v3v3(ofs[i], bp->vec, oldbp->vec);
721 ofs[i + 1][0] = bp->tilt - oldbp->tilt;
722 ofs[i + 1][1] = bp->radius - oldbp->radius;
723 }
724 i += 2;
725 bp++;
726 }
727 }
728 }
729 }
730 }
731
732 LISTBASE_FOREACH_INDEX (KeyBlock *, currkey, &cu->key->block, currkey_i) {
733 const bool apply_offset = (ofs && (currkey != actkey) && (*dependent)[currkey_i]);
734
735 float *fp = newkey = static_cast<float *>(
736 MEM_callocN(cu->key->elemsize * totvert, "currkey->data"));
737 ofp = oldkey = static_cast<float *>(currkey->data);
738
739 Nurb *nu = static_cast<Nurb *>(editnurb->nurbs.first);
740 /* We need to restore to original curve into newnurb, *not* editcurve's nurbs.
741 * Otherwise, in case we update obdata *without* leaving editmode (e.g. viewport render),
742 * we would invalidate editcurve. */
743 newnu = static_cast<Nurb *>(newnurbs->first);
744 i = 0;
745 while (nu) {
746 if (currkey == actkey) {
747 const bool restore = actkey != cu->key->refkey;
748
749 if (nu->bezt) {
750 bezt = nu->bezt;
751 a = nu->pntsu;
752 BezTriple *newbezt = newnu->bezt;
753 while (a--) {
754 int j;
755 oldbezt = getKeyIndexOrig_bezt(editnurb, bezt);
756
757 for (j = 0; j < 3; j++, i++) {
758 copy_v3_v3(&fp[j * 3], bezt->vec[j]);
759
760 if (restore && oldbezt) {
761 copy_v3_v3(newbezt->vec[j], oldbezt->vec[j]);
762 }
763 }
764 fp[9] = bezt->tilt;
765 fp[10] = bezt->radius;
766
767 if (restore && oldbezt) {
768 newbezt->tilt = oldbezt->tilt;
769 newbezt->radius = oldbezt->radius;
770 }
771
773 i++;
774 bezt++;
775 newbezt++;
776 }
777 }
778 else {
779 bp = nu->bp;
780 a = nu->pntsu * nu->pntsv;
781 BPoint *newbp = newnu->bp;
782 while (a--) {
783 oldbp = getKeyIndexOrig_bp(editnurb, bp);
784
785 copy_v3_v3(fp, bp->vec);
786
787 fp[3] = bp->tilt;
788 fp[4] = bp->radius;
789
790 if (restore && oldbp) {
791 copy_v3_v3(newbp->vec, oldbp->vec);
792 newbp->tilt = oldbp->tilt;
793 newbp->radius = oldbp->radius;
794 }
795
797 bp++;
798 newbp++;
799 i += 2;
800 }
801 }
802 }
803 else {
804 int index;
805 const float *curofp;
806
807 if (oldkey) {
808 if (nu->bezt) {
809 bezt = nu->bezt;
810 a = nu->pntsu;
811
812 while (a--) {
813 index = getKeyIndexOrig_keyIndex(editnurb, bezt);
814 if (index >= 0) {
815 int j;
816 curofp = ofp + index;
817
818 for (j = 0; j < 3; j++, i++) {
819 copy_v3_v3(&fp[j * 3], &curofp[j * 3]);
820
821 if (apply_offset) {
822 add_v3_v3(&fp[j * 3], ofs[i]);
823 }
824 }
825 fp[9] = curofp[9];
826 fp[10] = curofp[10];
827
828 if (apply_offset) {
829 /* Apply tilt offsets. */
830 add_v3_v3(fp + 9, ofs[i]);
831 i++;
832 }
833
835 }
836 else {
837 int j;
838 for (j = 0; j < 3; j++, i++) {
839 copy_v3_v3(&fp[j * 3], bezt->vec[j]);
840 }
841 fp[9] = bezt->tilt;
842 fp[10] = bezt->radius;
843
845 }
846 bezt++;
847 }
848 }
849 else {
850 bp = nu->bp;
851 a = nu->pntsu * nu->pntsv;
852 while (a--) {
853 index = getKeyIndexOrig_keyIndex(editnurb, bp);
854
855 if (index >= 0) {
856 curofp = ofp + index;
857 copy_v3_v3(fp, curofp);
858 fp[3] = curofp[3];
859 fp[4] = curofp[4];
860
861 if (apply_offset) {
862 add_v3_v3(fp, ofs[i]);
863 add_v3_v3(&fp[3], ofs[i + 1]);
864 }
865 }
866 else {
867 copy_v3_v3(fp, bp->vec);
868 fp[3] = bp->tilt;
869 fp[4] = bp->radius;
870 }
871
873 bp++;
874 i += 2;
875 }
876 }
877 }
878 }
879
880 nu = nu->next;
881 newnu = newnu->next;
882 }
883
884 if (apply_offset) {
885 /* handles could become malicious after offsets applying */
886 calc_keyHandles(&editnurb->nurbs, newkey);
887 }
888
889 currkey->totelem = totvert;
890 if (currkey->data) {
891 MEM_freeN(currkey->data);
892 }
893 currkey->data = newkey;
894 }
895
896 MEM_SAFE_FREE(ofs);
897}
898
901/* -------------------------------------------------------------------- */
905static bool curve_is_animated(Curve *cu)
906{
907 AnimData *ad = BKE_animdata_from_id(&cu->id);
908
909 return ad && (ad->action || ad->drivers.first);
910}
911
915static void fcurve_path_rename(const char *orig_rna_path,
916 const char *rna_path,
917 const blender::Span<FCurve *> orig_curves,
918 blender::Set<FCurve *> &processed_fcurves)
919{
920 const int len = strlen(orig_rna_path);
921
922 for (FCurve *fcu : orig_curves) {
923 if (processed_fcurves.contains(fcu)) {
924 continue;
925 }
926 if (!STREQLEN(fcu->rna_path, orig_rna_path, len)) {
927 continue;
928 }
929
930 processed_fcurves.add(fcu);
931
932 const char *suffix = fcu->rna_path + len;
933 char *new_rna_path = BLI_sprintfN("%s%s", rna_path, suffix);
934 MEM_SAFE_FREE(fcu->rna_path);
935 fcu->rna_path = new_rna_path;
936 }
937}
938
946 Curve *cu, blender::Span<FCurve *> orig_curves)
947{
948 if (orig_curves.is_empty()) {
949 /* If there is no animation data to operate on, better stop now. */
950 return {};
951 }
952
953 int a, pt_index;
954 EditNurb *editnurb = cu->editnurb;
955 CVKeyIndex *keyIndex;
956 char rna_path[64], orig_rna_path[64];
957
958 blender::Set<FCurve *> processed_fcurves;
959 blender::Vector<FCurve *> fcurves_to_remove;
960
961 int nu_index = 0;
962 LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, nu_index) {
963 if (nu->bezt) {
964 BezTriple *bezt = nu->bezt;
965 a = nu->pntsu;
966 pt_index = 0;
967
968 while (a--) {
969 SNPRINTF(rna_path, "splines[%d].bezier_points[%d]", nu_index, pt_index);
970
971 keyIndex = getCVKeyIndex(editnurb, bezt);
972 if (keyIndex) {
973 SNPRINTF(orig_rna_path,
974 "splines[%d].bezier_points[%d]",
975 keyIndex->nu_index,
976 keyIndex->pt_index);
977
978 if (keyIndex->switched) {
979 char handle_path[64], orig_handle_path[64];
980 SNPRINTF(orig_handle_path, "%s.handle_left", orig_rna_path);
981 SNPRINTF(handle_path, "%s.handle_right", rna_path);
982 fcurve_path_rename(orig_handle_path, handle_path, orig_curves, processed_fcurves);
983
984 SNPRINTF(orig_handle_path, "%s.handle_right", orig_rna_path);
985 SNPRINTF(handle_path, "%s.handle_left", rna_path);
986 fcurve_path_rename(orig_handle_path, handle_path, orig_curves, processed_fcurves);
987 }
988
989 fcurve_path_rename(orig_rna_path, rna_path, orig_curves, processed_fcurves);
990
991 keyIndex->nu_index = nu_index;
992 keyIndex->pt_index = pt_index;
993 }
994 else {
995 /* In this case, the bezier point exists. It just hasn't been indexed yet (which seems to
996 * happen on entering edit mode, so points added after that may not have such an index
997 * yet) */
998
999 /* This is a no-op when it comes to the manipulation of F-Curves. It does find the
1000 * relevant F-Curves to place them in `processed_fcurves`, which will prevent them from
1001 * being deleted later on. */
1002 fcurve_path_rename(rna_path, rna_path, orig_curves, processed_fcurves);
1003 }
1004
1005 bezt++;
1006 pt_index++;
1007 }
1008 }
1009 else {
1010 BPoint *bp = nu->bp;
1011 a = nu->pntsu * nu->pntsv;
1012 pt_index = 0;
1013
1014 while (a--) {
1015 SNPRINTF(rna_path, "splines[%d].points[%d]", nu_index, pt_index);
1016
1017 keyIndex = getCVKeyIndex(editnurb, bp);
1018 if (keyIndex) {
1019 SNPRINTF(
1020 orig_rna_path, "splines[%d].points[%d]", keyIndex->nu_index, keyIndex->pt_index);
1021 fcurve_path_rename(orig_rna_path, rna_path, orig_curves, processed_fcurves);
1022
1023 keyIndex->nu_index = nu_index;
1024 keyIndex->pt_index = pt_index;
1025 }
1026 else {
1027 /* In this case, the bezier point exists. It just hasn't been indexed yet (which seems to
1028 * happen on entering edit mode, so points added after that may not have such an index
1029 * yet) */
1030
1031 /* This is a no-op when it comes to the manipulation of F-Curves. It does find the
1032 * relevant F-Curves to place them in `processed_fcurves`, which will prevent them from
1033 * being deleted later on. */
1034 fcurve_path_rename(rna_path, rna_path, orig_curves, processed_fcurves);
1035 }
1036
1037 bp++;
1038 pt_index++;
1039 }
1040 }
1041 }
1042
1043 /* remove paths for removed control points
1044 * need this to make further step with copying non-cv related curves copying
1045 * not touching cv's f-curves */
1046 for (FCurve *fcu : orig_curves) {
1047 if (processed_fcurves.contains(fcu)) {
1048 continue;
1049 }
1050
1051 if (STRPREFIX(fcu->rna_path, "splines")) {
1052 const char *ch = strchr(fcu->rna_path, '.');
1053
1054 if (ch && (STRPREFIX(ch, ".bezier_points") || STRPREFIX(ch, ".points"))) {
1055 fcurves_to_remove.append(fcu);
1056 }
1057 }
1058 }
1059
1060 nu_index = 0;
1061 LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, nu_index) {
1062 keyIndex = nullptr;
1063 if (nu->pntsu) {
1064 if (nu->bezt) {
1065 keyIndex = getCVKeyIndex(editnurb, &nu->bezt[0]);
1066 }
1067 else {
1068 keyIndex = getCVKeyIndex(editnurb, &nu->bp[0]);
1069 }
1070 }
1071
1072 if (keyIndex) {
1073 SNPRINTF(rna_path, "splines[%d]", nu_index);
1074 SNPRINTF(orig_rna_path, "splines[%d]", keyIndex->nu_index);
1075 fcurve_path_rename(orig_rna_path, rna_path, orig_curves, processed_fcurves);
1076 }
1077 }
1078
1079 /* the remainders in orig_curves can be copied back (like follow path) */
1080 /* (if it's not path to spline) */
1081 for (FCurve *fcu : orig_curves) {
1082 if (processed_fcurves.contains(fcu)) {
1083 continue;
1084 }
1085 if (STRPREFIX(fcu->rna_path, "splines")) {
1086 fcurves_to_remove.append(fcu);
1087 }
1088 }
1089
1090 return fcurves_to_remove;
1091}
1092
1094{
1095 AnimData *adt = BKE_animdata_from_id(&cu->id);
1096 EditNurb *editnurb = cu->editnurb;
1097
1098 if (!editnurb->keyindex) {
1099 return 0;
1100 }
1101
1102 if (!curve_is_animated(cu)) {
1103 return 0;
1104 }
1105
1106 if (adt->action != nullptr) {
1107 blender::animrig::Action &action = adt->action->wrap();
1108 const bool is_action_legacy = action.is_action_legacy();
1109
1111 adt);
1112
1113 Vector<FCurve *> fcurves_to_remove = curve_rename_fcurves(cu, fcurves_to_process);
1114 for (FCurve *fcurve : fcurves_to_remove) {
1115 if (is_action_legacy) {
1117 BKE_fcurve_free(fcurve);
1118 }
1119 else {
1120 const bool remove_ok = blender::animrig::action_fcurve_remove(action, *fcurve);
1121 BLI_assert(remove_ok);
1122 UNUSED_VARS_NDEBUG(remove_ok);
1123 }
1124 }
1125
1128 }
1129
1130 {
1132 Vector<FCurve *> fcurves_to_remove = curve_rename_fcurves(cu, fcurves_to_process);
1133 for (FCurve *driver : fcurves_to_remove) {
1134 BLI_remlink(&adt->drivers, driver);
1135 BKE_fcurve_free(driver);
1136 }
1138 }
1139
1140 /* TODO(sergey): Only update if something actually changed. */
1142
1143 return 1;
1144}
1145
1148/* -------------------------------------------------------------------- */
1152static int *init_index_map(Object *obedit, int *r_old_totvert)
1153{
1154 Curve *curve = (Curve *)obedit->data;
1155 EditNurb *editnurb = curve->editnurb;
1156 CVKeyIndex *keyIndex;
1157 int *old_to_new_map;
1158
1159 int old_totvert = 0;
1160 LISTBASE_FOREACH (Nurb *, nu, &curve->nurb) {
1161 if (nu->bezt) {
1162 old_totvert += nu->pntsu * 3;
1163 }
1164 else {
1165 old_totvert += nu->pntsu * nu->pntsv;
1166 }
1167 }
1168
1169 old_to_new_map = static_cast<int *>(
1170 MEM_mallocN(old_totvert * sizeof(int), "curve old to new index map"));
1171 for (int i = 0; i < old_totvert; i++) {
1172 old_to_new_map[i] = -1;
1173 }
1174
1175 int vertex_index = 0;
1176 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
1177 if (nu->bezt) {
1178 BezTriple *bezt = nu->bezt;
1179 int a = nu->pntsu;
1180
1181 while (a--) {
1182 keyIndex = getCVKeyIndex(editnurb, bezt);
1183 if (keyIndex && keyIndex->vertex_index + 2 < old_totvert) {
1184 if (keyIndex->switched) {
1185 old_to_new_map[keyIndex->vertex_index] = vertex_index + 2;
1186 old_to_new_map[keyIndex->vertex_index + 1] = vertex_index + 1;
1187 old_to_new_map[keyIndex->vertex_index + 2] = vertex_index;
1188 }
1189 else {
1190 old_to_new_map[keyIndex->vertex_index] = vertex_index;
1191 old_to_new_map[keyIndex->vertex_index + 1] = vertex_index + 1;
1192 old_to_new_map[keyIndex->vertex_index + 2] = vertex_index + 2;
1193 }
1194 }
1195 vertex_index += 3;
1196 bezt++;
1197 }
1198 }
1199 else {
1200 BPoint *bp = nu->bp;
1201 int a = nu->pntsu * nu->pntsv;
1202
1203 while (a--) {
1204 keyIndex = getCVKeyIndex(editnurb, bp);
1205 if (keyIndex) {
1206 old_to_new_map[keyIndex->vertex_index] = vertex_index;
1207 }
1208 vertex_index++;
1209 bp++;
1210 }
1211 }
1212 }
1213
1214 *r_old_totvert = old_totvert;
1215 return old_to_new_map;
1216}
1217
1218static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit)
1219{
1220 Curve *curve = (Curve *)obedit->data;
1221 EditNurb *editnurb = curve->editnurb;
1222 int *old_to_new_map = nullptr;
1223 int old_totvert;
1224
1225 if (editnurb->keyindex == nullptr) {
1226 /* TODO(sergey): Happens when separating curves, this would lead to
1227 * the wrong indices in the hook modifier, address this together with
1228 * other indices issues.
1229 */
1230 return;
1231 }
1232
1233 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
1234 int index;
1235 if ((object->parent) && (object->parent->data == curve) &&
1236 ELEM(object->partype, PARVERT1, PARVERT3))
1237 {
1238 if (old_to_new_map == nullptr) {
1239 old_to_new_map = init_index_map(obedit, &old_totvert);
1240 }
1241
1242 if (object->par1 < old_totvert) {
1243 index = old_to_new_map[object->par1];
1244 if (index != -1) {
1245 object->par1 = index;
1246 }
1247 }
1248 if (object->par2 < old_totvert) {
1249 index = old_to_new_map[object->par2];
1250 if (index != -1) {
1251 object->par2 = index;
1252 }
1253 }
1254 if (object->par3 < old_totvert) {
1255 index = old_to_new_map[object->par3];
1256 if (index != -1) {
1257 object->par3 = index;
1258 }
1259 }
1260 }
1261 if (object->data == curve) {
1262 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
1263 if (md->type == eModifierType_Hook) {
1265 int i, j;
1266
1267 if (old_to_new_map == nullptr) {
1268 old_to_new_map = init_index_map(obedit, &old_totvert);
1269 }
1270
1271 for (i = j = 0; i < hmd->indexar_num; i++) {
1272 if (hmd->indexar[i] < old_totvert) {
1273 index = old_to_new_map[hmd->indexar[i]];
1274 if (index != -1) {
1275 hmd->indexar[j++] = index;
1276 }
1277 }
1278 else {
1279 j++;
1280 }
1281 }
1282
1283 hmd->indexar_num = j;
1284 }
1285 }
1286 }
1287 }
1288 if (old_to_new_map != nullptr) {
1289 MEM_freeN(old_to_new_map);
1290 }
1291}
1292
1294{
1295 ListBase *editnurb = object_editcurve_get(obedit);
1296
1297 if (obedit == nullptr) {
1298 return;
1299 }
1300
1301 if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
1302 Curve *cu = static_cast<Curve *>(obedit->data);
1303 ListBase newnurb = {nullptr, nullptr}, oldnurb = cu->nurb;
1304
1305 remap_hooks_and_vertex_parents(bmain, obedit);
1306
1307 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1308 Nurb *newnu = BKE_nurb_duplicate(nu);
1309 BLI_addtail(&newnurb, newnu);
1310
1311 if (nu->type == CU_NURBS) {
1313 }
1314 }
1315
1316 /* We have to pass also new copied nurbs, since we want to restore original curve
1317 * (without edited shape-key) on obdata, but *not* on editcurve itself
1318 * (ED_curve_editnurb_load call does not always implies freeing
1319 * of editcurve, e.g. when called to generate render data). */
1320 calc_shapeKeys(obedit, &newnurb);
1321
1322 cu->nurb = newnurb;
1323
1324 ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data));
1325
1326 BKE_nurbList_free(&oldnurb);
1327 }
1328}
1329
1331{
1332 Curve *cu = (Curve *)obedit->data;
1333 EditNurb *editnurb = cu->editnurb;
1334 KeyBlock *actkey;
1335
1336 if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
1337 actkey = BKE_keyblock_from_object(obedit);
1338
1339 if (actkey) {
1340/* TODO(@ideasman42): undo_system: investigate why this was needed. */
1341#if 0
1342 undo_editmode_clear();
1343#endif
1344 }
1345
1346 if (editnurb) {
1347 BKE_nurbList_free(&editnurb->nurbs);
1349 }
1350 else {
1351 editnurb = static_cast<EditNurb *>(MEM_callocN(sizeof(EditNurb), "editnurb"));
1352 cu->editnurb = editnurb;
1353 }
1354
1355 LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
1356 Nurb *newnu = BKE_nurb_duplicate(nu);
1357 BLI_addtail(&editnurb->nurbs, newnu);
1358 }
1359
1360 /* Animation could be added in edit-mode even if there was no animdata in
1361 * object mode hence we always need CVs index be created. */
1362 init_editNurb_keyIndex(editnurb, &cu->nurb);
1363
1364 if (actkey) {
1365 editnurb->shapenr = obedit->shapenr;
1366 /* Apply shape-key to new nurbs of editnurb, not those of original curve
1367 * (and *after* we generated keyIndex), else we do not have valid 'original' data
1368 * to properly restore curve when leaving edit-mode. */
1369 BKE_keyblock_convert_to_curve(actkey, cu, &editnurb->nurbs);
1370 }
1371 }
1372}
1373
1375{
1376 Curve *cu = static_cast<Curve *>(obedit->data);
1377
1379}
1380
1383/* -------------------------------------------------------------------- */
1388{
1389 Main *bmain = CTX_data_main(C);
1390 Scene *scene = CTX_data_scene(C);
1391 ViewLayer *view_layer = CTX_data_view_layer(C);
1392 View3D *v3d = CTX_wm_view3d(C);
1393
1394 struct {
1395 int changed;
1396 int unselected;
1397 int error_vertex_keys;
1398 int error_generic;
1399 } status = {0};
1400
1401 WM_cursor_wait(true);
1402
1404 scene, view_layer, CTX_wm_view3d(C));
1405 for (Base *oldbase : bases) {
1406 Base *newbase;
1407 Object *oldob, *newob;
1408 Curve *oldcu, *newcu;
1409 EditNurb *newedit;
1410 ListBase newnurb = {nullptr, nullptr};
1411
1412 oldob = oldbase->object;
1413 oldcu = static_cast<Curve *>(oldob->data);
1414
1415 if (oldcu->key) {
1416 status.error_vertex_keys++;
1417 continue;
1418 }
1419
1420 if (!ED_curve_select_check(v3d, oldcu->editnurb)) {
1421 status.unselected++;
1422 continue;
1423 }
1424
1425 /* 1. Duplicate geometry and check for valid selection for separate. */
1426 adduplicateflagNurb(oldob, v3d, &newnurb, SELECT, true);
1427
1428 if (BLI_listbase_is_empty(&newnurb)) {
1429 status.error_generic++;
1430 continue;
1431 }
1432
1433 /* 2. Duplicate the object and data. */
1434
1435 /* Take into account user preferences for duplicating actions. */
1436 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(U.dupflag & USER_DUP_ACT);
1437
1438 newbase = blender::ed::object::add_duplicate(bmain, scene, view_layer, oldbase, dupflag);
1440
1441 newob = newbase->object;
1442 newcu = static_cast<Curve *>(newob->data = BKE_id_copy(bmain, &oldcu->id));
1443 newcu->editnurb = nullptr;
1444 id_us_min(&oldcu->id); /* Because new curve is a copy: reduce user count. */
1445
1446 /* 3. Put new object in editmode, clear it and set separated nurbs. */
1448 newedit = newcu->editnurb;
1449 BKE_nurbList_free(&newedit->nurbs);
1451 BLI_movelisttolist(&newedit->nurbs, &newnurb);
1452
1453 /* 4. Put old object out of editmode and delete separated geometry. */
1454 ED_curve_editnurb_load(bmain, newob);
1456 curve_delete_segments(oldob, v3d, true);
1457
1458 DEG_id_tag_update(&oldob->id, ID_RECALC_GEOMETRY); /* This is the original one. */
1459 DEG_id_tag_update(&newob->id, ID_RECALC_GEOMETRY); /* This is the separated one. */
1460
1463 status.changed++;
1464 }
1465 WM_cursor_wait(false);
1466
1467 if (status.unselected == bases.size()) {
1468 BKE_report(op->reports, RPT_ERROR, "No point was selected");
1469 return OPERATOR_CANCELLED;
1470 }
1471
1472 const int tot_errors = status.error_vertex_keys + status.error_generic;
1473 if (tot_errors > 0) {
1474
1475 /* Some curves changed, but some curves failed: don't explain why it failed. */
1476 if (status.changed) {
1477 BKE_reportf(op->reports, RPT_INFO, "%d curve(s) could not be separated", tot_errors);
1478 return OPERATOR_FINISHED;
1479 }
1480
1481 /* All curves failed: If there is more than one error give a generic error report. */
1482 if (((status.error_vertex_keys ? 1 : 0) + (status.error_generic ? 1 : 0)) > 1) {
1483 BKE_report(op->reports, RPT_ERROR, "Could not separate selected curve(s)");
1484 }
1485
1486 /* All curves failed due to the same error. */
1487 if (status.error_vertex_keys) {
1488 BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with shape keys");
1489 }
1490 else {
1491 BLI_assert(status.error_generic);
1492 BKE_report(op->reports, RPT_ERROR, "Cannot separate current selection");
1493 }
1494 return OPERATOR_CANCELLED;
1495 }
1496
1498
1499 return OPERATOR_FINISHED;
1500}
1501
1503{
1504 /* identifiers */
1505 ot->name = "Separate";
1506 ot->idname = "CURVE_OT_separate";
1507 ot->description = "Separate selected points from connected unselected points into a new object";
1508
1509 /* api callbacks */
1512
1513 /* flags */
1515}
1516
1519/* -------------------------------------------------------------------- */
1524{
1525 Main *bmain = CTX_data_main(C);
1526 const Scene *scene = CTX_data_scene(C);
1527 ViewLayer *view_layer = CTX_data_view_layer(C);
1528 View3D *v3d = CTX_wm_view3d(C);
1529 bool changed = false;
1530 int count_failed = 0;
1531
1533 scene, view_layer, CTX_wm_view3d(C));
1534 for (Object *obedit : objects) {
1535 Curve *cu = static_cast<Curve *>(obedit->data);
1536
1537 if (!ED_curve_select_check(v3d, cu->editnurb)) {
1538 continue;
1539 }
1540
1541 ListBase newnurb = {nullptr, nullptr};
1542
1543 adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, true);
1544
1545 if (BLI_listbase_is_empty(&newnurb)) {
1546 count_failed += 1;
1547 continue;
1548 }
1549
1550 ListBase *editnurb = object_editcurve_get(obedit);
1551 const int len_orig = BLI_listbase_count(editnurb);
1552
1553 curve_delete_segments(obedit, v3d, true);
1554 cu->actnu -= len_orig - BLI_listbase_count(editnurb);
1555 BLI_movelisttolist(editnurb, &newnurb);
1556
1557 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
1559 }
1560
1561 changed = true;
1563 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1564 }
1565
1566 if (changed == false) {
1567 if (count_failed != 0) {
1568 BKE_report(op->reports, RPT_ERROR, "Cannot split current selection");
1569 }
1570 return OPERATOR_CANCELLED;
1571 }
1572 return OPERATOR_FINISHED;
1573}
1574
1576{
1577 /* identifiers */
1578 ot->name = "Split";
1579 ot->idname = "CURVE_OT_split";
1580 ot->description = "Split off selected points from connected unselected points";
1581
1582 /* api callbacks */
1585
1586 /* flags */
1588}
1589
1592/* -------------------------------------------------------------------- */
1596/* return true if U direction is selected and number of selected columns v */
1597static bool isNurbselU(Nurb *nu, int *v, int flag)
1598{
1599 BPoint *bp;
1600 int a, b, sel;
1601
1602 *v = 0;
1603
1604 for (b = 0, bp = nu->bp; b < nu->pntsv; b++) {
1605 sel = 0;
1606 for (a = 0; a < nu->pntsu; a++, bp++) {
1607 if (bp->f1 & flag) {
1608 sel++;
1609 }
1610 }
1611 if (sel == nu->pntsu) {
1612 (*v)++;
1613 }
1614 else if (sel >= 1) {
1615 *v = 0;
1616 return false;
1617 }
1618 }
1619
1620 return true;
1621}
1622
1623/* return true if V direction is selected and number of selected rows u */
1624static bool isNurbselV(Nurb *nu, int *u, int flag)
1625{
1626 BPoint *bp;
1627 int a, b, sel;
1628
1629 *u = 0;
1630
1631 for (a = 0; a < nu->pntsu; a++) {
1632 bp = &nu->bp[a];
1633 sel = 0;
1634 for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) {
1635 if (bp->f1 & flag) {
1636 sel++;
1637 }
1638 }
1639 if (sel == nu->pntsv) {
1640 (*u)++;
1641 }
1642 else if (sel >= 1) {
1643 *u = 0;
1644 return false;
1645 }
1646 }
1647
1648 return true;
1649}
1650
1651static void rotateflagNurb(ListBase *editnurb,
1652 short flag,
1653 const float cent[3],
1654 const float rotmat[3][3])
1655{
1656 /* all verts with (flag & 'flag') rotate */
1657 BPoint *bp;
1658 int a;
1659
1660 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1661 if (nu->type == CU_NURBS) {
1662 bp = nu->bp;
1663 a = nu->pntsu * nu->pntsv;
1664
1665 while (a--) {
1666 if (bp->f1 & flag) {
1667 sub_v3_v3(bp->vec, cent);
1668 mul_m3_v3(rotmat, bp->vec);
1669 add_v3_v3(bp->vec, cent);
1670 }
1671 bp++;
1672 }
1673 }
1674 }
1675}
1676
1677void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3], bool is_2d)
1678{
1679 /* all verts with ('flag' & flag) translate */
1680 BezTriple *bezt;
1681 BPoint *bp;
1682 int a;
1683
1684 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1685 if (nu->type == CU_BEZIER) {
1686 a = nu->pntsu;
1687 bezt = nu->bezt;
1688 while (a--) {
1689 if (bezt->f1 & flag) {
1690 add_v3_v3(bezt->vec[0], vec);
1691 }
1692 if (bezt->f2 & flag) {
1693 add_v3_v3(bezt->vec[1], vec);
1694 }
1695 if (bezt->f3 & flag) {
1696 add_v3_v3(bezt->vec[2], vec);
1697 }
1698 bezt++;
1699 }
1700 }
1701 else {
1702 a = nu->pntsu * nu->pntsv;
1703 bp = nu->bp;
1704 while (a--) {
1705 if (bp->f1 & flag) {
1706 add_v3_v3(bp->vec, vec);
1707 }
1708 bp++;
1709 }
1710 }
1711
1712 if (is_2d) {
1714 }
1715 }
1716}
1717
1718static void weightflagNurb(ListBase *editnurb, short flag, float w)
1719{
1720 BPoint *bp;
1721 int a;
1722
1723 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1724 if (nu->type == CU_NURBS) {
1725 a = nu->pntsu * nu->pntsv;
1726 bp = nu->bp;
1727 while (a--) {
1728 if (bp->f1 & flag) {
1729 /* a mode used to exist for replace/multiple but is was unused */
1730 bp->vec[3] *= w;
1731 }
1732 bp++;
1733 }
1734 }
1735 }
1736}
1737
1739{
1740 Curve *cu = static_cast<Curve *>(obedit->data);
1741 ListBase *editnurb = object_editcurve_get(obedit);
1742 BPoint *bp, *bpn, *newbp;
1743 int a, b, newu, newv;
1744
1745 BLI_assert(obedit->type == OB_SURF);
1746
1747 LISTBASE_FOREACH_MUTABLE (Nurb *, nu, editnurb) {
1748 /* is entire nurb selected */
1749 bp = nu->bp;
1750 a = nu->pntsu * nu->pntsv;
1751 while (a) {
1752 a--;
1753 if (bp->f1 & SELECT) {
1754 /* pass */
1755 }
1756 else {
1757 break;
1758 }
1759 bp++;
1760 }
1761 if (a == 0) {
1762 if (cu->actnu == BLI_findindex(editnurb, nu)) {
1763 cu->actnu = CU_ACT_NONE;
1764 }
1765
1766 BLI_remlink(editnurb, nu);
1767 keyIndex_delNurb(cu->editnurb, nu);
1768 BKE_nurb_free(nu);
1769 nu = nullptr;
1770 }
1771 else {
1772 if (isNurbselU(nu, &newv, SELECT)) {
1773 /* U direction selected */
1774 newv = nu->pntsv - newv;
1775 if (newv != nu->pntsv) {
1776 /* delete */
1777 bp = nu->bp;
1778 bpn = newbp = (BPoint *)MEM_mallocN(newv * nu->pntsu * sizeof(BPoint), "deleteNurb");
1779 for (b = 0; b < nu->pntsv; b++) {
1780 if ((bp->f1 & SELECT) == 0) {
1781 memcpy(bpn, bp, nu->pntsu * sizeof(BPoint));
1782 keyIndex_updateBP(cu->editnurb, bp, bpn, nu->pntsu);
1783 bpn += nu->pntsu;
1784 }
1785 else {
1786 keyIndex_delBP(cu->editnurb, bp);
1787 }
1788 bp += nu->pntsu;
1789 }
1790 nu->pntsv = newv;
1791 MEM_freeN(nu->bp);
1792 nu->bp = newbp;
1794
1796 }
1797 }
1798 else if (isNurbselV(nu, &newu, SELECT)) {
1799 /* V direction selected */
1800 newu = nu->pntsu - newu;
1801 if (newu != nu->pntsu) {
1802 /* delete */
1803 bp = nu->bp;
1804 bpn = newbp = (BPoint *)MEM_mallocN(newu * nu->pntsv * sizeof(BPoint), "deleteNurb");
1805 for (b = 0; b < nu->pntsv; b++) {
1806 for (a = 0; a < nu->pntsu; a++, bp++) {
1807 if ((bp->f1 & SELECT) == 0) {
1808 *bpn = *bp;
1809 keyIndex_updateBP(cu->editnurb, bp, bpn, 1);
1810 bpn++;
1811 }
1812 else {
1813 keyIndex_delBP(cu->editnurb, bp);
1814 }
1815 }
1816 }
1817 MEM_freeN(nu->bp);
1818 nu->bp = newbp;
1819 if (newu == 1 && nu->pntsv > 1) { /* make a U spline */
1820 nu->pntsu = nu->pntsv;
1821 nu->pntsv = 1;
1822 std::swap(nu->orderu, nu->orderv);
1824 MEM_SAFE_FREE(nu->knotsv);
1825 }
1826 else {
1827 nu->pntsu = newu;
1829 }
1831 }
1832 }
1833 }
1834 }
1835}
1836
1837static void ed_curve_delete_selected(Object *obedit, View3D *v3d)
1838{
1839 Curve *cu = static_cast<Curve *>(obedit->data);
1840 EditNurb *editnurb = cu->editnurb;
1841 ListBase *nubase = &editnurb->nurbs;
1842 BezTriple *bezt, *bezt1;
1843 BPoint *bp, *bp1;
1844 int a, type, nuindex = 0;
1845
1846 /* first loop, can we remove entire pieces? */
1847 LISTBASE_FOREACH_MUTABLE (Nurb *, nu, nubase) {
1848 if (nu->type == CU_BEZIER) {
1849 bezt = nu->bezt;
1850 a = nu->pntsu;
1851 if (a) {
1852 while (a) {
1853 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
1854 /* pass */
1855 }
1856 else {
1857 break;
1858 }
1859 a--;
1860 bezt++;
1861 }
1862 if (a == 0) {
1863 if (cu->actnu == nuindex) {
1864 cu->actnu = CU_ACT_NONE;
1865 }
1866
1867 BLI_remlink(nubase, nu);
1868 keyIndex_delNurb(editnurb, nu);
1869 BKE_nurb_free(nu);
1870 nu = nullptr;
1871 }
1872 }
1873 }
1874 else {
1875 bp = nu->bp;
1876 a = nu->pntsu * nu->pntsv;
1877 if (a) {
1878 while (a) {
1879 if (bp->f1 & SELECT) {
1880 /* pass */
1881 }
1882 else {
1883 break;
1884 }
1885 a--;
1886 bp++;
1887 }
1888 if (a == 0) {
1889 if (cu->actnu == nuindex) {
1890 cu->actnu = CU_ACT_NONE;
1891 }
1892
1893 BLI_remlink(nubase, nu);
1894 keyIndex_delNurb(editnurb, nu);
1895 BKE_nurb_free(nu);
1896 nu = nullptr;
1897 }
1898 }
1899 }
1900
1901/* Never allow the order to exceed the number of points
1902 * NOTE: this is ok but changes unselected nurbs, disable for now. */
1903#if 0
1904 if ((nu != nullptr) && (nu->type == CU_NURBS)) {
1905 clamp_nurb_order_u(nu);
1906 }
1907#endif
1908 nuindex++;
1909 }
1910 /* 2nd loop, delete small pieces: just for curves */
1911 LISTBASE_FOREACH_MUTABLE (Nurb *, nu, nubase) {
1912 type = 0;
1913 if (nu->type == CU_BEZIER) {
1914 bezt = nu->bezt;
1915 for (a = 0; a < nu->pntsu; a++) {
1916 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
1917 memmove(bezt, bezt + 1, (nu->pntsu - a - 1) * sizeof(BezTriple));
1918 keyIndex_delBezt(editnurb, bezt);
1919 keyIndex_updateBezt(editnurb, bezt + 1, bezt, nu->pntsu - a - 1);
1920 nu->pntsu--;
1921 a--;
1922 type = 1;
1923 }
1924 else {
1925 bezt++;
1926 }
1927 }
1928 if (type) {
1929 bezt1 = (BezTriple *)MEM_mallocN((nu->pntsu) * sizeof(BezTriple), "delNurb");
1930 memcpy(bezt1, nu->bezt, (nu->pntsu) * sizeof(BezTriple));
1931 keyIndex_updateBezt(editnurb, nu->bezt, bezt1, nu->pntsu);
1932 MEM_freeN(nu->bezt);
1933 nu->bezt = bezt1;
1935 }
1936 }
1937 else if (nu->pntsv == 1) {
1938 bp = nu->bp;
1939
1940 for (a = 0; a < nu->pntsu; a++) {
1941 if (bp->f1 & SELECT) {
1942 memmove(bp, bp + 1, (nu->pntsu - a - 1) * sizeof(BPoint));
1943 keyIndex_delBP(editnurb, bp);
1944 keyIndex_updateBP(editnurb, bp + 1, bp, nu->pntsu - a - 1);
1945 nu->pntsu--;
1946 a--;
1947 type = 1;
1948 }
1949 else {
1950 bp++;
1951 }
1952 }
1953 if (type) {
1954 bp1 = (BPoint *)MEM_mallocN(nu->pntsu * sizeof(BPoint), "delNurb2");
1955 memcpy(bp1, nu->bp, (nu->pntsu) * sizeof(BPoint));
1956 keyIndex_updateBP(editnurb, nu->bp, bp1, nu->pntsu);
1957 MEM_freeN(nu->bp);
1958 nu->bp = bp1;
1959
1960/* Never allow the order to exceed the number of points
1961 * NOTE: this is ok but changes unselected nurbs, disable for now. */
1962#if 0
1963 if (nu->type == CU_NURBS) {
1964 clamp_nurb_order_u(nu);
1965 }
1966#endif
1967 }
1970 }
1971 }
1972}
1973
1974static void select_bpoints(BPoint *bp,
1975 const int stride,
1976 const int count,
1977 const bool selstatus,
1978 const uint8_t flag,
1979 const bool hidden)
1980{
1981 for (int i = 0; i < count; i++) {
1982 select_bpoint(bp, selstatus, flag, hidden);
1983 bp += stride;
1984 }
1985}
1986
1994static int sel_to_copy_ints(const BPoint *bp,
1995 const int next_j,
1996 const int max_j,
1997 const int next_i,
1998 const int max_i,
1999 const uint8_t flag,
2000 int copy_intervals[],
2001 int *interval_count,
2002 bool *out_is_first_sel)
2003{
2004 const BPoint *bp_j = bp;
2005
2006 int selected_leg_count = 0;
2007 int ins = 0;
2008 int selected_in_prev_leg = -1;
2009 int not_full = -1;
2010
2011 bool is_first_sel = false;
2012 bool is_last_sel = false;
2013
2014 for (int j = 0; j < max_j; j++, bp_j += next_j) {
2015 const BPoint *bp_j_i = bp_j;
2016 int selected_in_curr_leg = 0;
2017 for (int i = 0; i < max_i; i++, bp_j_i += next_i) {
2018 if (bp_j_i->f1 & flag) {
2019 selected_in_curr_leg++;
2020 }
2021 }
2022 if (selected_in_curr_leg == max_i) {
2023 selected_leg_count++;
2024 if (j == 0) {
2025 is_first_sel = true;
2026 }
2027 else if (j + 1 == max_j) {
2028 is_last_sel = true;
2029 }
2030 }
2031 else if (not_full == -1) {
2032 not_full = selected_in_curr_leg;
2033 }
2034 /* We have partially selected leg in opposite dimension if condition is met. */
2035 else if (not_full != selected_in_curr_leg) {
2036 return -1;
2037 }
2038 /* Extrusion area starts/ends if met. */
2039 if (selected_in_prev_leg != selected_in_curr_leg) {
2040 copy_intervals[ins] = selected_in_curr_leg == max_i || j == 0 ? j : j - 1;
2041 ins++;
2042 selected_in_prev_leg = selected_in_curr_leg;
2043 }
2044 copy_intervals[ins] = j;
2045 }
2046 if (selected_leg_count &&
2047 /* Prevents leading and trailing unselected legs if all selected.
2048 * Unless it is extrusion from point or curve. */
2049 (selected_leg_count < max_j || max_j == 1))
2050 {
2051 /* Prepend unselected leg if more than one leg selected at the starting edge.
2052 * max_j == 1 handles extrusion from point to curve and from curve to surface cases. */
2053 if (is_first_sel && (copy_intervals[0] < copy_intervals[1] || max_j == 1)) {
2054 memmove(copy_intervals + 1, copy_intervals, (ins + 1) * sizeof(copy_intervals[0]));
2055 copy_intervals[0] = 0;
2056 ins++;
2057 is_first_sel = false;
2058 }
2059 /* Append unselected leg if more than one leg selected at the end. */
2060 if (is_last_sel && copy_intervals[ins - 1] < copy_intervals[ins]) {
2061 copy_intervals[ins + 1] = copy_intervals[ins];
2062 ins++;
2063 }
2064 }
2065 *interval_count = ins;
2066 *out_is_first_sel = ins > 1 ? is_first_sel : false;
2067 return selected_leg_count;
2068}
2069
2070struct NurbDim {
2073};
2074
2076{
2077 NurbDim ret = {0, 0};
2078 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
2079 if (nu->pntsu > ret.pntsu) {
2080 ret.pntsu = nu->pntsu;
2081 }
2082 if (nu->pntsv > ret.pntsv) {
2083 ret.pntsv = nu->pntsv;
2084 }
2085 }
2086 return ret;
2087}
2088
2090{
2091 const NurbDim max = editnurb_find_max_points_num(editnurb);
2092 /* One point induces at most one interval. Except single point case, it can give + 1.
2093 * Another +1 is for first element of the first interval. */
2094 int *const intvls_u = static_cast<int *>(
2095 MEM_malloc_arrayN(max.pntsu + 2, sizeof(int), "extrudeNurb0"));
2096 int *const intvls_v = static_cast<int *>(
2097 MEM_malloc_arrayN(max.pntsv + 2, sizeof(int), "extrudeNurb1"));
2098 bool ok = false;
2099
2100 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
2101 int intvl_cnt_u;
2102 bool is_first_sel_u;
2103
2104 /* Calculate selected U legs and intervals for their extrusion. */
2105 const int selected_us = sel_to_copy_ints(
2106 nu->bp, 1, nu->pntsu, nu->pntsu, nu->pntsv, flag, intvls_u, &intvl_cnt_u, &is_first_sel_u);
2107 if (selected_us == -1) {
2108 continue;
2109 }
2110 int intvl_cnt_v;
2111 bool is_first_sel_v;
2112
2113 const bool is_point = nu->pntsu == 1;
2114 const bool is_curve = nu->pntsv == 1;
2115 const bool extrude_every_u_point = selected_us == nu->pntsu;
2116 if (is_point || (is_curve && !extrude_every_u_point)) {
2117 intvls_v[0] = intvls_v[1] = 0;
2118 intvl_cnt_v = 1;
2119 is_first_sel_v = false;
2120 }
2121 else {
2122 sel_to_copy_ints(nu->bp,
2123 nu->pntsu,
2124 nu->pntsv,
2125 1,
2126 nu->pntsu,
2127 flag,
2128 intvls_v,
2129 &intvl_cnt_v,
2130 &is_first_sel_v);
2131 }
2132
2133 const int new_pntsu = nu->pntsu + intvl_cnt_u - 1;
2134 const int new_pntsv = nu->pntsv + intvl_cnt_v - 1;
2135 BPoint *const new_bp = (BPoint *)MEM_malloc_arrayN(
2136 new_pntsu * new_pntsv, sizeof(BPoint), "extrudeNurb2");
2137 BPoint *new_bp_v = new_bp;
2138
2139 bool selected_v = is_first_sel_v;
2140 for (int j = 1; j <= intvl_cnt_v; j++, selected_v = !selected_v) {
2141 BPoint *old_bp_v = nu->bp + intvls_v[j - 1] * nu->pntsu;
2142 for (int v_j = intvls_v[j - 1]; v_j <= intvls_v[j];
2143 v_j++, new_bp_v += new_pntsu, old_bp_v += nu->pntsu)
2144 {
2145 BPoint *new_bp_u_v = new_bp_v;
2146 bool selected_u = is_first_sel_u;
2147 for (int i = 1; i <= intvl_cnt_u; i++, selected_u = !selected_u) {
2148 const int copy_from = intvls_u[i - 1];
2149 const int copy_to = intvls_u[i];
2150 const int copy_count = copy_to - copy_from + 1;
2151 const bool sel_status = selected_u || selected_v ? true : false;
2152 ED_curve_bpcpy(editnurb, new_bp_u_v, old_bp_v + copy_from, copy_count);
2153 select_bpoints(new_bp_u_v, 1, copy_count, sel_status, flag, HIDDEN);
2154 new_bp_u_v += copy_count;
2155 }
2156 }
2157 }
2158
2159 MEM_freeN(nu->bp);
2160 nu->bp = new_bp;
2161 nu->pntsu = new_pntsu;
2162 if (nu->pntsv == 1 && new_pntsv > 1) {
2163 nu->orderv = 2;
2164 }
2165 nu->pntsv = new_pntsv;
2168
2169 ok = true;
2170 }
2171 MEM_freeN(intvls_u);
2172 MEM_freeN(intvls_v);
2173 return ok;
2174}
2175
2176static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu)
2177{
2178 cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb);
2179}
2180
2182 const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert)
2183{
2184 if (cu->actvert == -1) {
2185 calc_duplicate_actnurb(editnurb, newnurb, cu);
2186 return true;
2187 }
2188
2189 if ((start <= cu->actvert) && (end > cu->actvert)) {
2190 calc_duplicate_actnurb(editnurb, newnurb, cu);
2191 cu->actvert = vert;
2192 return true;
2193 }
2194 return false;
2195}
2196
2198 Object *obedit, View3D *v3d, ListBase *newnurb, const uint8_t flag, const bool split)
2199{
2200 ListBase *editnurb = object_editcurve_get(obedit);
2201 Nurb *newnu;
2202 BezTriple *bezt, *bezt1;
2203 BPoint *bp, *bp1, *bp2, *bp3;
2204 Curve *cu = (Curve *)obedit->data;
2205 int a, b, c, starta, enda, diffa, cyclicu, cyclicv, newu, newv;
2206 char *usel;
2207
2208 int i = 0;
2209 LISTBASE_FOREACH_INDEX (Nurb *, nu, editnurb, i) {
2210 cyclicu = cyclicv = 0;
2211 if (nu->type == CU_BEZIER) {
2212 for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
2213 enda = -1;
2214 starta = a;
2215 while ((bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag)) {
2216 if (!split) {
2217 select_beztriple(bezt, false, flag, HIDDEN);
2218 }
2219 enda = a;
2220 if (a >= nu->pntsu - 1) {
2221 break;
2222 }
2223 a++;
2224 bezt++;
2225 }
2226 if (enda >= starta) {
2227 newu = diffa = enda - starta + 1; /* set newu and diffa, may use both */
2228
2229 if (starta == 0 && newu != nu->pntsu && (nu->flagu & CU_NURB_CYCLIC)) {
2230 cyclicu = newu;
2231 }
2232 else {
2233 if (enda == nu->pntsu - 1) {
2234 newu += cyclicu;
2235 }
2236 if (i == cu->actnu) {
2238 editnurb, newnurb, cu, starta, starta + diffa, cu->actvert - starta);
2239 }
2240
2241 newnu = BKE_nurb_copy(nu, newu, 1);
2242 memcpy(newnu->bezt, &nu->bezt[starta], diffa * sizeof(BezTriple));
2243 if (newu != diffa) {
2244 memcpy(&newnu->bezt[diffa], nu->bezt, cyclicu * sizeof(BezTriple));
2245 if (i == cu->actnu) {
2247 editnurb, newnurb, cu, 0, cyclicu, newu - cyclicu + cu->actvert);
2248 }
2249 cyclicu = 0;
2250 }
2251
2252 if (newu != nu->pntsu) {
2253 newnu->flagu &= ~CU_NURB_CYCLIC;
2254 }
2255
2256 for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
2257 select_beztriple(bezt1, true, flag, HIDDEN);
2258 }
2259
2260 BLI_addtail(newnurb, newnu);
2261 }
2262 }
2263 }
2264
2265 if (cyclicu != 0) {
2266 if (i == cu->actnu) {
2267 calc_duplicate_actvert(editnurb, newnurb, cu, 0, cyclicu, cu->actvert);
2268 }
2269
2270 newnu = BKE_nurb_copy(nu, cyclicu, 1);
2271 memcpy(newnu->bezt, nu->bezt, cyclicu * sizeof(BezTriple));
2272 newnu->flagu &= ~CU_NURB_CYCLIC;
2273
2274 for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
2275 select_beztriple(bezt1, true, flag, HIDDEN);
2276 }
2277
2278 BLI_addtail(newnurb, newnu);
2279 }
2280 }
2281 else if (nu->pntsv == 1) { /* because UV Nurb has a different method for dupli */
2282 for (a = 0, bp = nu->bp; a < nu->pntsu; a++, bp++) {
2283 enda = -1;
2284 starta = a;
2285 while (bp->f1 & flag) {
2286 if (!split) {
2287 select_bpoint(bp, false, flag, HIDDEN);
2288 }
2289 enda = a;
2290 if (a >= nu->pntsu - 1) {
2291 break;
2292 }
2293 a++;
2294 bp++;
2295 }
2296 if (enda >= starta) {
2297 newu = diffa = enda - starta + 1; /* set newu and diffa, may use both */
2298
2299 if (starta == 0 && newu != nu->pntsu && (nu->flagu & CU_NURB_CYCLIC)) {
2300 cyclicu = newu;
2301 }
2302 else {
2303 if (enda == nu->pntsu - 1) {
2304 newu += cyclicu;
2305 }
2306 if (i == cu->actnu) {
2308 editnurb, newnurb, cu, starta, starta + diffa, cu->actvert - starta);
2309 }
2310
2311 newnu = BKE_nurb_copy(nu, newu, 1);
2312 memcpy(newnu->bp, &nu->bp[starta], diffa * sizeof(BPoint));
2313 if (newu != diffa) {
2314 memcpy(&newnu->bp[diffa], nu->bp, cyclicu * sizeof(BPoint));
2315 if (i == cu->actnu) {
2317 editnurb, newnurb, cu, 0, cyclicu, newu - cyclicu + cu->actvert);
2318 }
2319 cyclicu = 0;
2320 }
2321
2322 if (newu != nu->pntsu) {
2323 newnu->flagu &= ~CU_NURB_CYCLIC;
2324 }
2325
2326 for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
2327 select_bpoint(bp1, true, flag, HIDDEN);
2328 }
2329
2330 BLI_addtail(newnurb, newnu);
2331 }
2332 }
2333 }
2334
2335 if (cyclicu != 0) {
2336 if (i == cu->actnu) {
2337 calc_duplicate_actvert(editnurb, newnurb, cu, 0, cyclicu, cu->actvert);
2338 }
2339
2340 newnu = BKE_nurb_copy(nu, cyclicu, 1);
2341 memcpy(newnu->bp, nu->bp, cyclicu * sizeof(BPoint));
2342 newnu->flagu &= ~CU_NURB_CYCLIC;
2343
2344 for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
2345 select_bpoint(bp1, true, flag, HIDDEN);
2346 }
2347
2348 BLI_addtail(newnurb, newnu);
2349 }
2350 }
2351 else {
2352 if (ED_curve_nurb_select_check(v3d, nu)) {
2353 /* A rectangular area in nurb has to be selected and if splitting
2354 * must be in U or V direction. */
2355 usel = static_cast<char *>(MEM_callocN(nu->pntsu, "adduplicateN3"));
2356 bp = nu->bp;
2357 for (a = 0; a < nu->pntsv; a++) {
2358 for (b = 0; b < nu->pntsu; b++, bp++) {
2359 if (bp->f1 & flag) {
2360 usel[b]++;
2361 }
2362 }
2363 }
2364 newu = 0;
2365 newv = 0;
2366 for (a = 0; a < nu->pntsu; a++) {
2367 if (usel[a]) {
2368 if (ELEM(newv, 0, usel[a])) {
2369 newv = usel[a];
2370 newu++;
2371 }
2372 else {
2373 newv = 0;
2374 break;
2375 }
2376 }
2377 }
2378 MEM_freeN(usel);
2379
2380 if ((newu == 0 || newv == 0) ||
2381 (split && !isNurbselU(nu, &newv, SELECT) && !isNurbselV(nu, &newu, SELECT)))
2382 {
2383 if (G.debug & G_DEBUG) {
2384 printf("Can't duplicate Nurb\n");
2385 }
2386 }
2387 else {
2388 for (a = 0, bp1 = nu->bp; a < nu->pntsu * nu->pntsv; a++, bp1++) {
2389 newv = newu = 0;
2390
2391 if ((bp1->f1 & flag) && !(bp1->f1 & SURF_SEEN)) {
2392 /* point selected, now loop over points in U and V directions */
2393 for (b = a % nu->pntsu, bp2 = bp1; b < nu->pntsu; b++, bp2++) {
2394 if (bp2->f1 & flag) {
2395 newu++;
2396 for (c = a / nu->pntsu, bp3 = bp2; c < nu->pntsv; c++, bp3 += nu->pntsu) {
2397 if (bp3->f1 & flag) {
2398 /* flag as seen so skipped on future iterations */
2399 bp3->f1 |= SURF_SEEN;
2400 if (newu == 1) {
2401 newv++;
2402 }
2403 }
2404 else {
2405 break;
2406 }
2407 }
2408 }
2409 else {
2410 break;
2411 }
2412 }
2413 }
2414
2415 if ((newu + newv) > 2) {
2416 /* ignore single points */
2417 if (a == 0) {
2418 /* check if need to save cyclic selection and continue if so */
2419 if (newu == nu->pntsu && (nu->flagv & CU_NURB_CYCLIC)) {
2420 cyclicv = newv;
2421 }
2422 if (newv == nu->pntsv && (nu->flagu & CU_NURB_CYCLIC)) {
2423 cyclicu = newu;
2424 }
2425 if (cyclicu != 0 || cyclicv != 0) {
2426 continue;
2427 }
2428 }
2429
2430 if (a + newu == nu->pntsu && cyclicu != 0) {
2431 /* cyclic in U direction */
2432 newnu = BKE_nurb_copy(nu, newu + cyclicu, newv);
2433 for (b = 0; b < newv; b++) {
2434 memcpy(&newnu->bp[b * newnu->pntsu],
2435 &nu->bp[b * nu->pntsu + a],
2436 newu * sizeof(BPoint));
2437 memcpy(&newnu->bp[b * newnu->pntsu + newu],
2438 &nu->bp[b * nu->pntsu],
2439 cyclicu * sizeof(BPoint));
2440 }
2441
2442 if (cu->actnu == i) {
2443 if (cu->actvert == -1) {
2444 calc_duplicate_actnurb(editnurb, newnurb, cu);
2445 }
2446 else {
2447 for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
2448 starta = b * nu->pntsu + a;
2449 if (calc_duplicate_actvert(editnurb,
2450 newnurb,
2451 cu,
2452 cu->actvert,
2453 starta,
2454 cu->actvert % nu->pntsu + newu +
2455 b * newnu->pntsu))
2456 {
2457 /* actvert in cyclicu selection */
2458 break;
2459 }
2460 if (calc_duplicate_actvert(editnurb,
2461 newnurb,
2462 cu,
2463 starta,
2464 starta + newu,
2465 cu->actvert - starta + b * newnu->pntsu))
2466 {
2467 /* actvert in 'current' iteration selection */
2468 break;
2469 }
2470 }
2471 }
2472 }
2473 cyclicu = cyclicv = 0;
2474 }
2475 else if ((a / nu->pntsu) + newv == nu->pntsv && cyclicv != 0) {
2476 /* cyclic in V direction */
2477 newnu = BKE_nurb_copy(nu, newu, newv + cyclicv);
2478 memcpy(newnu->bp, &nu->bp[a], newu * newv * sizeof(BPoint));
2479 memcpy(&newnu->bp[newu * newv], nu->bp, newu * cyclicv * sizeof(BPoint));
2480
2481 /* check for actvert in cyclicv selection */
2482 if (cu->actnu == i) {
2484 editnurb, newnurb, cu, cu->actvert, a, (newu * newv) + cu->actvert);
2485 }
2486 cyclicu = cyclicv = 0;
2487 }
2488 else {
2489 newnu = BKE_nurb_copy(nu, newu, newv);
2490 for (b = 0; b < newv; b++) {
2491 memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu + a], newu * sizeof(BPoint));
2492 }
2493 }
2494
2495 /* general case if not handled by cyclicu or cyclicv */
2496 if (cu->actnu == i) {
2497 if (cu->actvert == -1) {
2498 calc_duplicate_actnurb(editnurb, newnurb, cu);
2499 }
2500 else {
2501 for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
2502 starta = b * nu->pntsu + a;
2503 if (calc_duplicate_actvert(editnurb,
2504 newnurb,
2505 cu,
2506 starta,
2507 starta + newu,
2508 cu->actvert - (a / nu->pntsu * nu->pntsu + diffa +
2509 (starta % nu->pntsu))))
2510 {
2511 break;
2512 }
2513 }
2514 }
2515 }
2516 BLI_addtail(newnurb, newnu);
2517
2518 if (newu != nu->pntsu) {
2519 newnu->flagu &= ~CU_NURB_CYCLIC;
2520 }
2521 if (newv != nu->pntsv) {
2522 newnu->flagv &= ~CU_NURB_CYCLIC;
2523 }
2524 }
2525 }
2526
2527 if (cyclicu != 0 || cyclicv != 0) {
2528 /* copy start of a cyclic surface, or copying all selected points */
2529 newu = cyclicu == 0 ? nu->pntsu : cyclicu;
2530 newv = cyclicv == 0 ? nu->pntsv : cyclicv;
2531
2532 newnu = BKE_nurb_copy(nu, newu, newv);
2533 for (b = 0; b < newv; b++) {
2534 memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu], newu * sizeof(BPoint));
2535 }
2536
2537 /* Check for `actvert` in the unused cyclic-UV selection. */
2538 if (cu->actnu == i) {
2539 if (cu->actvert == -1) {
2540 calc_duplicate_actnurb(editnurb, newnurb, cu);
2541 }
2542 else {
2543 for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
2544 starta = b * nu->pntsu;
2545 if (calc_duplicate_actvert(editnurb,
2546 newnurb,
2547 cu,
2548 starta,
2549 starta + newu,
2550 cu->actvert - (diffa + (starta % nu->pntsu))))
2551 {
2552 break;
2553 }
2554 }
2555 }
2556 }
2557 BLI_addtail(newnurb, newnu);
2558
2559 if (newu != nu->pntsu) {
2560 newnu->flagu &= ~CU_NURB_CYCLIC;
2561 }
2562 if (newv != nu->pntsv) {
2563 newnu->flagv &= ~CU_NURB_CYCLIC;
2564 }
2565 }
2566
2567 for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
2568 bp1->f1 &= ~SURF_SEEN;
2569 if (!split) {
2570 select_bpoint(bp1, false, flag, HIDDEN);
2571 }
2572 }
2573 }
2574 }
2575 }
2576 }
2577
2578 if (BLI_listbase_is_empty(newnurb) == false) {
2579 LISTBASE_FOREACH (Nurb *, nu, newnurb) {
2580 if (nu->type == CU_BEZIER) {
2581 if (split) {
2582 /* recalc first and last */
2583 BKE_nurb_handle_calc_simple(nu, &nu->bezt[0]);
2584 BKE_nurb_handle_calc_simple(nu, &nu->bezt[nu->pntsu - 1]);
2585 }
2586 }
2587 else {
2588 /* knots done after duplicate as pntsu may change */
2591
2592 if (obedit->type == OB_SURF) {
2593 for (a = 0, bp = nu->bp; a < nu->pntsu * nu->pntsv; a++, bp++) {
2594 bp->f1 &= ~SURF_SEEN;
2595 }
2596
2599 }
2600 }
2601 }
2602 }
2603}
2604
2607/* -------------------------------------------------------------------- */
2612{
2613 Main *bmain = CTX_data_main(C);
2614 const Scene *scene = CTX_data_scene(C);
2615 ViewLayer *view_layer = CTX_data_view_layer(C);
2616 View3D *v3d = CTX_wm_view3d(C);
2617
2619 scene, view_layer, CTX_wm_view3d(C));
2620 for (Object *obedit : objects) {
2621 Curve *cu = static_cast<Curve *>(obedit->data);
2622
2623 if (!ED_curve_select_check(v3d, cu->editnurb)) {
2624 continue;
2625 }
2626
2627 EditNurb *editnurb = cu->editnurb;
2628
2629 int i = 0;
2630 LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, i) {
2631 if (ED_curve_nurb_select_check(v3d, nu)) {
2634 if ((i == cu->actnu) && (cu->actvert != CU_ACT_NONE)) {
2635 cu->actvert = (nu->pntsu - 1) - cu->actvert;
2636 }
2637 }
2638 }
2639
2640 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
2642 }
2643
2644 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2646 }
2647 return OPERATOR_FINISHED;
2648}
2649
2651{
2652 /* identifiers */
2653 ot->name = "Switch Direction";
2654 ot->description = "Switch direction of selected splines";
2655 ot->idname = "CURVE_OT_switch_direction";
2656
2657 /* api callbacks */
2660
2661 /* flags */
2663}
2664
2667/* -------------------------------------------------------------------- */
2672{
2673 const Scene *scene = CTX_data_scene(C);
2674 ViewLayer *view_layer = CTX_data_view_layer(C);
2676 scene, view_layer, CTX_wm_view3d(C));
2677
2678 for (Object *obedit : objects) {
2679 ListBase *editnurb = object_editcurve_get(obedit);
2680 BezTriple *bezt;
2681 BPoint *bp;
2682 float weight = RNA_float_get(op->ptr, "weight");
2683 int a;
2684
2685 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
2686 if (nu->bezt) {
2687 for (bezt = nu->bezt, a = 0; a < nu->pntsu; a++, bezt++) {
2688 if (bezt->f2 & SELECT) {
2689 bezt->weight = weight;
2690 }
2691 }
2692 }
2693 else if (nu->bp) {
2694 for (bp = nu->bp, a = 0; a < nu->pntsu * nu->pntsv; a++, bp++) {
2695 if (bp->f1 & SELECT) {
2696 bp->weight = weight;
2697 }
2698 }
2699 }
2700 }
2701
2702 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2704 }
2705
2706 return OPERATOR_FINISHED;
2707}
2708
2710{
2711 /* identifiers */
2712 ot->name = "Set Goal Weight";
2713 ot->description = "Set softbody goal weight for selected points";
2714 ot->idname = "CURVE_OT_spline_weight_set";
2715
2716 /* api callbacks */
2720
2721 /* flags */
2723
2724 /* properties */
2725 RNA_def_float_factor(ot->srna, "weight", 1.0f, 0.0f, 1.0f, "Weight", "", 0.0f, 1.0f);
2726}
2727
2730/* -------------------------------------------------------------------- */
2735{
2736 const Scene *scene = CTX_data_scene(C);
2737 ViewLayer *view_layer = CTX_data_view_layer(C);
2739 scene, view_layer, CTX_wm_view3d(C));
2740
2741 int totobjects = 0;
2742
2743 for (Object *obedit : objects) {
2744
2746 continue;
2747 }
2748
2749 totobjects++;
2750
2751 ListBase *editnurb = object_editcurve_get(obedit);
2752 BezTriple *bezt;
2753 BPoint *bp;
2754 float radius = RNA_float_get(op->ptr, "radius");
2755 int a;
2756
2757 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
2758 if (nu->bezt) {
2759 for (bezt = nu->bezt, a = 0; a < nu->pntsu; a++, bezt++) {
2760 if (bezt->f2 & SELECT) {
2761 bezt->radius = radius;
2762 }
2763 }
2764 }
2765 else if (nu->bp) {
2766 for (bp = nu->bp, a = 0; a < nu->pntsu * nu->pntsv; a++, bp++) {
2767 if (bp->f1 & SELECT) {
2768 bp->radius = radius;
2769 }
2770 }
2771 }
2772 }
2773
2775 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2776 }
2777
2778 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2779}
2780
2782{
2783 /* identifiers */
2784 ot->name = "Set Curve Radius";
2785 ot->description = "Set per-point radius which is used for bevel tapering";
2786 ot->idname = "CURVE_OT_radius_set";
2787
2788 /* api callbacks */
2792
2793 /* flags */
2795
2796 /* properties */
2798 ot->srna, "radius", 1.0f, 0.0f, OBJECT_ADD_SIZE_MAXF, "Radius", "", 0.0001f, 10.0f);
2799}
2800
2803/* -------------------------------------------------------------------- */
2808 const BezTriple *bezt_orig_prev,
2809 const BezTriple *bezt_orig_next,
2810 float factor)
2811{
2812 BLI_assert(IN_RANGE_INCL(factor, 0.0f, 1.0f));
2813
2814 for (int i = 0; i < 3; i++) {
2815 /* get single dimension pos of the mid handle */
2816 float val_old = bezt->vec[1][i];
2817
2818 /* get the weights of the previous/next mid handles and calc offset */
2819 float val_new = (bezt_orig_prev->vec[1][i] * 0.5f) + (bezt_orig_next->vec[1][i] * 0.5f);
2820 float offset = (val_old * (1.0f - factor)) + (val_new * factor) - val_old;
2821
2822 /* offset midpoint and 2 handles */
2823 bezt->vec[1][i] += offset;
2824 bezt->vec[0][i] += offset;
2825 bezt->vec[2][i] += offset;
2826 }
2827}
2828
2832static void smooth_single_bp(BPoint *bp,
2833 const BPoint *bp_orig_prev,
2834 const BPoint *bp_orig_next,
2835 float factor)
2836{
2837 BLI_assert(IN_RANGE_INCL(factor, 0.0f, 1.0f));
2838
2839 for (int i = 0; i < 3; i++) {
2840 float val_old, val_new, offset;
2841
2842 val_old = bp->vec[i];
2843 val_new = (bp_orig_prev->vec[i] * 0.5f) + (bp_orig_next->vec[i] * 0.5f);
2844 offset = (val_old * (1.0f - factor)) + (val_new * factor) - val_old;
2845
2846 bp->vec[i] += offset;
2847 }
2848}
2849
2851{
2852 const float factor = 1.0f / 6.0f;
2853 const Scene *scene = CTX_data_scene(C);
2854 ViewLayer *view_layer = CTX_data_view_layer(C);
2856 scene, view_layer, CTX_wm_view3d(C));
2857
2858 int totobjects = 0;
2859
2860 for (Object *obedit : objects) {
2861
2863 continue;
2864 }
2865
2866 totobjects++;
2867
2868 ListBase *editnurb = object_editcurve_get(obedit);
2869
2870 int a, a_end;
2871
2872 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
2873 if (nu->bezt) {
2874 /* duplicate the curve to use in weight calculation */
2875 const BezTriple *bezt_orig = static_cast<const BezTriple *>(MEM_dupallocN(nu->bezt));
2876 BezTriple *bezt;
2877 bool changed = false;
2878
2879 /* check whether its cyclic or not, and set initial & final conditions */
2880 if (nu->flagu & CU_NURB_CYCLIC) {
2881 a = 0;
2882 a_end = nu->pntsu;
2883 }
2884 else {
2885 a = 1;
2886 a_end = nu->pntsu - 1;
2887 }
2888
2889 /* for all the curve points */
2890 for (; a < a_end; a++) {
2891 /* respect selection */
2892 bezt = &nu->bezt[a];
2893 if (bezt->f2 & SELECT) {
2894 const BezTriple *bezt_orig_prev, *bezt_orig_next;
2895
2896 bezt_orig_prev = &bezt_orig[mod_i(a - 1, nu->pntsu)];
2897 bezt_orig_next = &bezt_orig[mod_i(a + 1, nu->pntsu)];
2898
2899 smooth_single_bezt(bezt, bezt_orig_prev, bezt_orig_next, factor);
2900
2901 changed = true;
2902 }
2903 }
2904 MEM_freeN((void *)bezt_orig);
2905 if (changed) {
2907 }
2908 }
2909 else if (nu->bp) {
2910 /* Same as above, keep these the same! */
2911 const BPoint *bp_orig = static_cast<const BPoint *>(MEM_dupallocN(nu->bp));
2912 BPoint *bp;
2913
2914 if (nu->flagu & CU_NURB_CYCLIC) {
2915 a = 0;
2916 a_end = nu->pntsu;
2917 }
2918 else {
2919 a = 1;
2920 a_end = nu->pntsu - 1;
2921 }
2922
2923 for (; a < a_end; a++) {
2924 bp = &nu->bp[a];
2925 if (bp->f1 & SELECT) {
2926 const BPoint *bp_orig_prev, *bp_orig_next;
2927
2928 bp_orig_prev = &bp_orig[mod_i(a - 1, nu->pntsu)];
2929 bp_orig_next = &bp_orig[mod_i(a + 1, nu->pntsu)];
2930
2931 smooth_single_bp(bp, bp_orig_prev, bp_orig_next, factor);
2932 }
2933 }
2934 MEM_freeN((void *)bp_orig);
2935 }
2936 }
2937
2939 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2940 }
2941
2942 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2943}
2944
2946{
2947 /* identifiers */
2948 ot->name = "Smooth";
2949 ot->description = "Flatten angles of selected points";
2950 ot->idname = "CURVE_OT_smooth";
2951
2952 /* api callbacks */
2953 ot->exec = smooth_exec;
2955
2956 /* flags */
2958}
2959
2962/* -------------------------------------------------------------------- */
2970static void curve_smooth_value(ListBase *editnurb, const int bezt_offsetof, const int bp_offset)
2971{
2972 BezTriple *bezt;
2973 BPoint *bp;
2974 int a;
2975
2976 /* use for smoothing */
2977 int last_sel;
2978 int start_sel, end_sel; /* selection indices, inclusive */
2979 float start_rad, end_rad, fac, range;
2980
2981 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
2982 if (nu->bezt) {
2983#define BEZT_VALUE(bezt) (*((float *)((char *)(bezt) + bezt_offsetof)))
2984
2985 for (last_sel = 0; last_sel < nu->pntsu; last_sel++) {
2986 /* loop over selection segments of a curve, smooth each */
2987
2988 /* Start BezTriple code,
2989 * this is duplicated below for points, make sure these functions stay in sync */
2990 start_sel = -1;
2991 for (bezt = &nu->bezt[last_sel], a = last_sel; a < nu->pntsu; a++, bezt++) {
2992 if (bezt->f2 & SELECT) {
2993 start_sel = a;
2994 break;
2995 }
2996 }
2997 /* in case there are no other selected verts */
2998 end_sel = start_sel;
2999 for (bezt = &nu->bezt[start_sel + 1], a = start_sel + 1; a < nu->pntsu; a++, bezt++) {
3000 if ((bezt->f2 & SELECT) == 0) {
3001 break;
3002 }
3003 end_sel = a;
3004 }
3005
3006 if (start_sel == -1) {
3007 last_sel = nu->pntsu; /* next... */
3008 }
3009 else {
3010 last_sel = end_sel; /* before we modify it */
3011
3012 /* now blend between start and end sel */
3013 start_rad = end_rad = FLT_MAX;
3014
3015 if (start_sel == end_sel) {
3016 /* simple, only 1 point selected */
3017 if (start_sel > 0) {
3018 start_rad = BEZT_VALUE(&nu->bezt[start_sel - 1]);
3019 }
3020 if (end_sel != -1 && end_sel < nu->pntsu) {
3021 end_rad = BEZT_VALUE(&nu->bezt[start_sel + 1]);
3022 }
3023
3024 if (start_rad != FLT_MAX && end_rad >= FLT_MAX) {
3025 BEZT_VALUE(&nu->bezt[start_sel]) = (start_rad + end_rad) / 2.0f;
3026 }
3027 else if (start_rad != FLT_MAX) {
3028 BEZT_VALUE(&nu->bezt[start_sel]) = start_rad;
3029 }
3030 else if (end_rad != FLT_MAX) {
3031 BEZT_VALUE(&nu->bezt[start_sel]) = end_rad;
3032 }
3033 }
3034 else {
3035 /* if endpoints selected, then use them */
3036 if (start_sel == 0) {
3037 start_rad = BEZT_VALUE(&nu->bezt[start_sel]);
3038 start_sel++; /* we don't want to edit the selected endpoint */
3039 }
3040 else {
3041 start_rad = BEZT_VALUE(&nu->bezt[start_sel - 1]);
3042 }
3043 if (end_sel == nu->pntsu - 1) {
3044 end_rad = BEZT_VALUE(&nu->bezt[end_sel]);
3045 end_sel--; /* we don't want to edit the selected endpoint */
3046 }
3047 else {
3048 end_rad = BEZT_VALUE(&nu->bezt[end_sel + 1]);
3049 }
3050
3051 /* Now Blend between the points */
3052 range = float(end_sel - start_sel) + 2.0f;
3053 for (bezt = &nu->bezt[start_sel], a = start_sel; a <= end_sel; a++, bezt++) {
3054 fac = float(1 + a - start_sel) / range;
3055 BEZT_VALUE(bezt) = start_rad * (1.0f - fac) + end_rad * fac;
3056 }
3057 }
3058 }
3059 }
3060#undef BEZT_VALUE
3061 }
3062 else if (nu->bp) {
3063#define BP_VALUE(bp) (*((float *)((char *)(bp) + bp_offset)))
3064
3065 /* Same as above, keep these the same! */
3066 for (last_sel = 0; last_sel < nu->pntsu; last_sel++) {
3067 /* loop over selection segments of a curve, smooth each */
3068
3069 /* Start BezTriple code,
3070 * this is duplicated below for points, make sure these functions stay in sync */
3071 start_sel = -1;
3072 for (bp = &nu->bp[last_sel], a = last_sel; a < nu->pntsu; a++, bp++) {
3073 if (bp->f1 & SELECT) {
3074 start_sel = a;
3075 break;
3076 }
3077 }
3078 /* in case there are no other selected verts */
3079 end_sel = start_sel;
3080 for (bp = &nu->bp[start_sel + 1], a = start_sel + 1; a < nu->pntsu; a++, bp++) {
3081 if ((bp->f1 & SELECT) == 0) {
3082 break;
3083 }
3084 end_sel = a;
3085 }
3086
3087 if (start_sel == -1) {
3088 last_sel = nu->pntsu; /* next... */
3089 }
3090 else {
3091 last_sel = end_sel; /* before we modify it */
3092
3093 /* now blend between start and end sel */
3094 start_rad = end_rad = FLT_MAX;
3095
3096 if (start_sel == end_sel) {
3097 /* simple, only 1 point selected */
3098 if (start_sel > 0) {
3099 start_rad = BP_VALUE(&nu->bp[start_sel - 1]);
3100 }
3101 if (end_sel != -1 && end_sel < nu->pntsu) {
3102 end_rad = BP_VALUE(&nu->bp[start_sel + 1]);
3103 }
3104
3105 if (start_rad != FLT_MAX && end_rad != FLT_MAX) {
3106 BP_VALUE(&nu->bp[start_sel]) = (start_rad + end_rad) / 2;
3107 }
3108 else if (start_rad != FLT_MAX) {
3109 BP_VALUE(&nu->bp[start_sel]) = start_rad;
3110 }
3111 else if (end_rad != FLT_MAX) {
3112 BP_VALUE(&nu->bp[start_sel]) = end_rad;
3113 }
3114 }
3115 else {
3116 /* if endpoints selected, then use them */
3117 if (start_sel == 0) {
3118 start_rad = BP_VALUE(&nu->bp[start_sel]);
3119 start_sel++; /* we don't want to edit the selected endpoint */
3120 }
3121 else {
3122 start_rad = BP_VALUE(&nu->bp[start_sel - 1]);
3123 }
3124 if (end_sel == nu->pntsu - 1) {
3125 end_rad = BP_VALUE(&nu->bp[end_sel]);
3126 end_sel--; /* we don't want to edit the selected endpoint */
3127 }
3128 else {
3129 end_rad = BP_VALUE(&nu->bp[end_sel + 1]);
3130 }
3131
3132 /* Now Blend between the points */
3133 range = float(end_sel - start_sel) + 2.0f;
3134 for (bp = &nu->bp[start_sel], a = start_sel; a <= end_sel; a++, bp++) {
3135 fac = float(1 + a - start_sel) / range;
3136 BP_VALUE(bp) = start_rad * (1.0f - fac) + end_rad * fac;
3137 }
3138 }
3139 }
3140 }
3141#undef BP_VALUE
3142 }
3143 }
3144}
3145
3148/* -------------------------------------------------------------------- */
3153{
3154 const Scene *scene = CTX_data_scene(C);
3155 ViewLayer *view_layer = CTX_data_view_layer(C);
3157 scene, view_layer, CTX_wm_view3d(C));
3158
3159 for (Object *obedit : objects) {
3160 ListBase *editnurb = object_editcurve_get(obedit);
3161
3162 curve_smooth_value(editnurb, offsetof(BezTriple, weight), offsetof(BPoint, weight));
3163
3165 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
3166 }
3167
3168 return OPERATOR_FINISHED;
3169}
3170
3172{
3173 /* identifiers */
3174 ot->name = "Smooth Curve Weight";
3175 ot->description = "Interpolate weight of selected points";
3176 ot->idname = "CURVE_OT_smooth_weight";
3177
3178 /* api callbacks */
3181
3182 /* flags */
3184}
3185
3188/* -------------------------------------------------------------------- */
3193{
3194 const Scene *scene = CTX_data_scene(C);
3195 ViewLayer *view_layer = CTX_data_view_layer(C);
3196
3198 scene, view_layer, CTX_wm_view3d(C));
3199
3200 int totobjects = 0;
3201
3202 for (Object *obedit : objects) {
3203
3205 continue;
3206 }
3207
3208 totobjects++;
3209
3210 ListBase *editnurb = object_editcurve_get(obedit);
3211
3212 curve_smooth_value(editnurb, offsetof(BezTriple, radius), offsetof(BPoint, radius));
3213
3215 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
3216 }
3217
3218 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3219}
3220
3222{
3223 /* identifiers */
3224 ot->name = "Smooth Curve Radius";
3225 ot->description = "Interpolate radii of selected points";
3226 ot->idname = "CURVE_OT_smooth_radius";
3227
3228 /* api callbacks */
3231
3232 /* flags */
3234}
3235
3238/* -------------------------------------------------------------------- */
3243{
3244 const Scene *scene = CTX_data_scene(C);
3245 ViewLayer *view_layer = CTX_data_view_layer(C);
3247 scene, view_layer, CTX_wm_view3d(C));
3248
3249 int totobjects = 0;
3250
3251 for (Object *obedit : objects) {
3252
3254 continue;
3255 }
3256
3257 totobjects++;
3258
3259 ListBase *editnurb = object_editcurve_get(obedit);
3260
3261 curve_smooth_value(editnurb, offsetof(BezTriple, tilt), offsetof(BPoint, tilt));
3262
3264 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
3265 }
3266
3267 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3268}
3269
3271{
3272 /* identifiers */
3273 ot->name = "Smooth Curve Tilt";
3274 ot->description = "Interpolate tilt of selected points";
3275 ot->idname = "CURVE_OT_smooth_tilt";
3276
3277 /* api callbacks */
3280
3281 /* flags */
3283}
3284
3287/* -------------------------------------------------------------------- */
3291static int hide_exec(bContext *C, wmOperator *op)
3292{
3293 const Scene *scene = CTX_data_scene(C);
3294 ViewLayer *view_layer = CTX_data_view_layer(C);
3295 View3D *v3d = CTX_wm_view3d(C);
3296
3297 const bool invert = RNA_boolean_get(op->ptr, "unselected");
3298
3300 scene, view_layer, CTX_wm_view3d(C));
3301 for (Object *obedit : objects) {
3302 Curve *cu = static_cast<Curve *>(obedit->data);
3303
3304 if (!(invert || ED_curve_select_check(v3d, cu->editnurb))) {
3305 continue;
3306 }
3307
3308 ListBase *editnurb = object_editcurve_get(obedit);
3309 BPoint *bp;
3310 BezTriple *bezt;
3311 int a, sel;
3312
3313 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
3314 if (nu->type == CU_BEZIER) {
3315 bezt = nu->bezt;
3316 a = nu->pntsu;
3317 sel = 0;
3318 while (a--) {
3319 if (invert == 0 && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
3320 select_beztriple(bezt, false, SELECT, HIDDEN);
3321 bezt->hide = 1;
3322 }
3323 else if (invert && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
3324 select_beztriple(bezt, false, SELECT, HIDDEN);
3325 bezt->hide = 1;
3326 }
3327 if (bezt->hide) {
3328 sel++;
3329 }
3330 bezt++;
3331 }
3332 if (sel == nu->pntsu) {
3333 nu->hide = 1;
3334 }
3335 }
3336 else {
3337 bp = nu->bp;
3338 a = nu->pntsu * nu->pntsv;
3339 sel = 0;
3340 while (a--) {
3341 if (invert == 0 && (bp->f1 & SELECT)) {
3342 select_bpoint(bp, false, SELECT, HIDDEN);
3343 bp->hide = 1;
3344 }
3345 else if (invert && (bp->f1 & SELECT) == 0) {
3346 select_bpoint(bp, false, SELECT, HIDDEN);
3347 bp->hide = 1;
3348 }
3349 if (bp->hide) {
3350 sel++;
3351 }
3352 bp++;
3353 }
3354 if (sel == nu->pntsu * nu->pntsv) {
3355 nu->hide = 1;
3356 }
3357 }
3358 }
3359
3360 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
3362 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
3363 }
3364 return OPERATOR_FINISHED;
3365}
3366
3368{
3369 /* identifiers */
3370 ot->name = "Hide Selected";
3371 ot->idname = "CURVE_OT_hide";
3372 ot->description = "Hide (un)selected control points";
3373
3374 /* api callbacks */
3375 ot->exec = hide_exec;
3377
3378 /* flags */
3380
3381 /* props */
3383 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
3384}
3385
3388/* -------------------------------------------------------------------- */
3393{
3394 const Scene *scene = CTX_data_scene(C);
3395 ViewLayer *view_layer = CTX_data_view_layer(C);
3396 const bool select = RNA_boolean_get(op->ptr, "select");
3397 bool changed_multi = false;
3398
3400 scene, view_layer, CTX_wm_view3d(C));
3401 for (Object *obedit : objects) {
3402 ListBase *editnurb = object_editcurve_get(obedit);
3403 BPoint *bp;
3404 BezTriple *bezt;
3405 int a;
3406 bool changed = false;
3407
3408 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
3409 nu->hide = 0;
3410 if (nu->type == CU_BEZIER) {
3411 bezt = nu->bezt;
3412 a = nu->pntsu;
3413 while (a--) {
3414 if (bezt->hide) {
3416 bezt->hide = 0;
3417 changed = true;
3418 }
3419 bezt++;
3420 }
3421 }
3422 else {
3423 bp = nu->bp;
3424 a = nu->pntsu * nu->pntsv;
3425 while (a--) {
3426 if (bp->hide) {
3428 bp->hide = 0;
3429 changed = true;
3430 }
3431 bp++;
3432 }
3433 }
3434 }
3435
3436 if (changed) {
3437 DEG_id_tag_update(static_cast<ID *>(obedit->data),
3440 changed_multi = true;
3441 }
3442 }
3443 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3444}
3445
3447{
3448 /* identifiers */
3449 ot->name = "Reveal Hidden";
3450 ot->idname = "CURVE_OT_reveal";
3451 ot->description = "Reveal hidden control points";
3452
3453 /* api callbacks */
3454 ot->exec = reveal_exec;
3456
3457 /* flags */
3459
3460 RNA_def_boolean(ot->srna, "select", true, "Select", "");
3461}
3462
3465/* -------------------------------------------------------------------- */
3474static void subdividenurb(Object *obedit, View3D *v3d, int number_cuts)
3475{
3476 Curve *cu = static_cast<Curve *>(obedit->data);
3477 EditNurb *editnurb = cu->editnurb;
3478 BezTriple *bezt, *beztnew, *beztn;
3479 BPoint *bp, *prevbp, *bpnew, *bpn;
3480 float vec[15];
3481 int a, b, sel, amount, *usel, *vsel;
3482 float factor;
3483
3484 // printf("*** subdivideNurb: entering subdivide\n");
3485
3486 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
3487 amount = 0;
3488 if (nu->type == CU_BEZIER) {
3489 BezTriple *nextbezt;
3490
3491 /*
3492 * Insert a point into a 2D Bezier curve.
3493 * Endpoints are preserved. Otherwise, all selected and inserted points are
3494 * newly created. Old points are discarded.
3495 */
3496 /* count */
3497 a = nu->pntsu;
3498 bezt = nu->bezt;
3499 while (a--) {
3500 nextbezt = BKE_nurb_bezt_get_next(nu, bezt);
3501 if (nextbezt == nullptr) {
3502 break;
3503 }
3504
3505 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nextbezt))
3506 {
3507 amount += number_cuts;
3508 }
3509 bezt++;
3510 }
3511
3512 if (amount) {
3513 /* insert */
3514 beztnew = (BezTriple *)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb");
3515 beztn = beztnew;
3516 a = nu->pntsu;
3517 bezt = nu->bezt;
3518 while (a--) {
3519 memcpy(beztn, bezt, sizeof(BezTriple));
3520 keyIndex_updateBezt(editnurb, bezt, beztn, 1);
3521 beztn++;
3522
3523 nextbezt = BKE_nurb_bezt_get_next(nu, bezt);
3524 if (nextbezt == nullptr) {
3525 break;
3526 }
3527
3528 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt) &&
3529 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nextbezt))
3530 {
3531 float prevvec[3][3];
3532
3533 memcpy(prevvec, bezt->vec, sizeof(float[9]));
3534
3535 for (int i = 0; i < number_cuts; i++) {
3536 factor = 1.0f / (number_cuts + 1 - i);
3537
3538 memcpy(beztn, nextbezt, sizeof(BezTriple));
3539
3540 /* midpoint subdividing */
3541 interp_v3_v3v3(vec, prevvec[1], prevvec[2], factor);
3542 interp_v3_v3v3(vec + 3, prevvec[2], nextbezt->vec[0], factor);
3543 interp_v3_v3v3(vec + 6, nextbezt->vec[0], nextbezt->vec[1], factor);
3544
3545 interp_v3_v3v3(vec + 9, vec, vec + 3, factor);
3546 interp_v3_v3v3(vec + 12, vec + 3, vec + 6, factor);
3547
3548 /* change handle of prev beztn */
3549 copy_v3_v3((beztn - 1)->vec[2], vec);
3550 /* new point */
3551 copy_v3_v3(beztn->vec[0], vec + 9);
3552 interp_v3_v3v3(beztn->vec[1], vec + 9, vec + 12, factor);
3553 copy_v3_v3(beztn->vec[2], vec + 12);
3554 /* handle of next bezt */
3555 if (a == 0 && i == number_cuts - 1 && (nu->flagu & CU_NURB_CYCLIC)) {
3556 copy_v3_v3(beztnew->vec[0], vec + 6);
3557 }
3558 else {
3559 copy_v3_v3(nextbezt->vec[0], vec + 6);
3560 }
3561
3562 beztn->radius = (bezt->radius + nextbezt->radius) / 2;
3563 beztn->weight = (bezt->weight + nextbezt->weight) / 2;
3564
3565 memcpy(prevvec, beztn->vec, sizeof(float[9]));
3566 beztn++;
3567 }
3568 }
3569
3570 bezt++;
3571 }
3572
3573 MEM_freeN(nu->bezt);
3574 nu->bezt = beztnew;
3575 nu->pntsu += amount;
3576
3578 }
3579 } /* End of 'if (nu->type == CU_BEZIER)' */
3580 else if (nu->pntsv == 1) {
3581 BPoint *nextbp;
3582
3583 /* NOTE(@nzc): All flat lines (ie. co-planar), except flat Nurbs. Flat NURB curves
3584 * are handled together with the regular NURB plane division, as it
3585 * should be. I split it off just now, let's see if it is stable. */
3586
3587 /* Count. */
3588 a = nu->pntsu;
3589 bp = nu->bp;
3590 while (a--) {
3591 nextbp = BKE_nurb_bpoint_get_next(nu, bp);
3592 if (nextbp == nullptr) {
3593 break;
3594 }
3595
3596 if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) {
3597 amount += number_cuts;
3598 }
3599 bp++;
3600 }
3601
3602 if (amount) {
3603 /* insert */
3604 bpnew = (BPoint *)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2");
3605 bpn = bpnew;
3606
3607 a = nu->pntsu;
3608 bp = nu->bp;
3609
3610 while (a--) {
3611 /* Copy "old" point. */
3612 memcpy(bpn, bp, sizeof(BPoint));
3613 keyIndex_updateBP(editnurb, bp, bpn, 1);
3614 bpn++;
3615
3616 nextbp = BKE_nurb_bpoint_get_next(nu, bp);
3617 if (nextbp == nullptr) {
3618 break;
3619 }
3620
3621 if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) {
3622 // printf("*** subdivideNurb: insert 'linear' point\n");
3623 for (int i = 0; i < number_cuts; i++) {
3624 factor = float(i + 1) / (number_cuts + 1);
3625
3626 memcpy(bpn, nextbp, sizeof(BPoint));
3627 interp_v4_v4v4(bpn->vec, bp->vec, nextbp->vec, factor);
3628 bpn->radius = interpf(bp->radius, nextbp->radius, factor);
3629 bpn++;
3630 }
3631 }
3632 bp++;
3633 }
3634
3635 MEM_freeN(nu->bp);
3636 nu->bp = bpnew;
3637 nu->pntsu += amount;
3638
3639 if (nu->type & CU_NURBS) {
3641 }
3642 }
3643 } /* End of 'else if (nu->pntsv == 1)' */
3644 else if (nu->type == CU_NURBS) {
3645 /* This is a very strange test ... */
3686 /* selection-arrays */
3687 usel = static_cast<int *>(MEM_callocN(sizeof(int) * nu->pntsu, "subivideNurb3"));
3688 vsel = static_cast<int *>(MEM_callocN(sizeof(int) * nu->pntsv, "subivideNurb3"));
3689 sel = 0;
3690
3691 /* Count the number of selected points. */
3692 bp = nu->bp;
3693 for (a = 0; a < nu->pntsv; a++) {
3694 for (b = 0; b < nu->pntsu; b++) {
3695 if (bp->f1 & SELECT) {
3696 usel[b]++;
3697 vsel[a]++;
3698 sel++;
3699 }
3700 bp++;
3701 }
3702 }
3703 if (sel == (nu->pntsu * nu->pntsv)) { /* subdivide entire nurb */
3704 /* Global subdivision is a special case of partial
3705 * subdivision. Strange it is considered separately... */
3706
3707 /* count of nodes (after subdivision) along U axis */
3708 int countu = nu->pntsu + (nu->pntsu - 1) * number_cuts;
3709
3710 /* total count of nodes after subdivision */
3711 int tot = ((number_cuts + 1) * nu->pntsu - number_cuts) *
3712 ((number_cuts + 1) * nu->pntsv - number_cuts);
3713
3714 bpn = bpnew = static_cast<BPoint *>(MEM_mallocN(tot * sizeof(BPoint), "subdivideNurb4"));
3715 bp = nu->bp;
3716 /* first subdivide rows */
3717 for (a = 0; a < nu->pntsv; a++) {
3718 for (b = 0; b < nu->pntsu; b++) {
3719 *bpn = *bp;
3720 keyIndex_updateBP(editnurb, bp, bpn, 1);
3721 bpn++;
3722 bp++;
3723 if (b < nu->pntsu - 1) {
3724 prevbp = bp - 1;
3725 for (int i = 0; i < number_cuts; i++) {
3726 factor = float(i + 1) / (number_cuts + 1);
3727 *bpn = *bp;
3728 interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
3729 bpn++;
3730 }
3731 }
3732 }
3733 bpn += number_cuts * countu;
3734 }
3735 /* now insert new */
3736 bpn = bpnew + ((number_cuts + 1) * nu->pntsu - number_cuts);
3737 bp = bpnew + (number_cuts + 1) * ((number_cuts + 1) * nu->pntsu - number_cuts);
3738 prevbp = bpnew;
3739 for (a = 1; a < nu->pntsv; a++) {
3740
3741 for (b = 0; b < (number_cuts + 1) * nu->pntsu - number_cuts; b++) {
3742 BPoint *tmp = bpn;
3743 for (int i = 0; i < number_cuts; i++) {
3744 factor = float(i + 1) / (number_cuts + 1);
3745 *tmp = *bp;
3746 interp_v4_v4v4(tmp->vec, prevbp->vec, bp->vec, factor);
3747 tmp += countu;
3748 }
3749 bp++;
3750 prevbp++;
3751 bpn++;
3752 }
3753 bp += number_cuts * countu;
3754 bpn += number_cuts * countu;
3755 prevbp += number_cuts * countu;
3756 }
3757 MEM_freeN(nu->bp);
3758 nu->bp = bpnew;
3759 nu->pntsu = (number_cuts + 1) * nu->pntsu - number_cuts;
3760 nu->pntsv = (number_cuts + 1) * nu->pntsv - number_cuts;
3763 } /* End of 'if (sel == nu->pntsu * nu->pntsv)' (subdivide entire NURB) */
3764 else {
3765 /* subdivide in v direction? */
3766 sel = 0;
3767 for (a = 0; a < nu->pntsv - 1; a++) {
3768 if (vsel[a] == nu->pntsu && vsel[a + 1] == nu->pntsu) {
3769 sel += number_cuts;
3770 }
3771 }
3772
3773 if (sel) { /* V direction. */
3774 bpn = bpnew = static_cast<BPoint *>(
3775 MEM_mallocN((sel + nu->pntsv) * nu->pntsu * sizeof(BPoint), "subdivideNurb4"));
3776 bp = nu->bp;
3777 for (a = 0; a < nu->pntsv; a++) {
3778 for (b = 0; b < nu->pntsu; b++) {
3779 *bpn = *bp;
3780 keyIndex_updateBP(editnurb, bp, bpn, 1);
3781 bpn++;
3782 bp++;
3783 }
3784 if ((a < nu->pntsv - 1) && vsel[a] == nu->pntsu && vsel[a + 1] == nu->pntsu) {
3785 for (int i = 0; i < number_cuts; i++) {
3786 factor = float(i + 1) / (number_cuts + 1);
3787 prevbp = bp - nu->pntsu;
3788 for (b = 0; b < nu->pntsu; b++) {
3789 /*
3790 * This simple bisection must be replaces by a
3791 * subtle resampling of a number of points. Our
3792 * task is made slightly easier because each
3793 * point in our curve is a separate data
3794 * node. (is it?)
3795 */
3796 *bpn = *prevbp;
3797 interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
3798 bpn++;
3799
3800 prevbp++;
3801 bp++;
3802 }
3803 bp -= nu->pntsu;
3804 }
3805 }
3806 }
3807 MEM_freeN(nu->bp);
3808 nu->bp = bpnew;
3809 nu->pntsv += sel;
3811 }
3812 else {
3813 /* or in u direction? */
3814 sel = 0;
3815 for (a = 0; a < nu->pntsu - 1; a++) {
3816 if (usel[a] == nu->pntsv && usel[a + 1] == nu->pntsv) {
3817 sel += number_cuts;
3818 }
3819 }
3820
3821 if (sel) { /* U direction. */
3822 /* Inserting U points is sort of 'default' Flat curves only get
3823 * U points inserted in them. */
3824 bpn = bpnew = static_cast<BPoint *>(
3825 MEM_mallocN((sel + nu->pntsu) * nu->pntsv * sizeof(BPoint), "subdivideNurb4"));
3826 bp = nu->bp;
3827 for (a = 0; a < nu->pntsv; a++) {
3828 for (b = 0; b < nu->pntsu; b++) {
3829 *bpn = *bp;
3830 keyIndex_updateBP(editnurb, bp, bpn, 1);
3831 bpn++;
3832 bp++;
3833 if ((b < nu->pntsu - 1) && usel[b] == nu->pntsv && usel[b + 1] == nu->pntsv) {
3834 /*
3835 * One thing that bugs me here is that the
3836 * orders of things are not the same as in
3837 * the JW piece. Also, this implies that we
3838 * handle at most 3rd order curves? I miss
3839 * some symmetry here...
3840 */
3841 for (int i = 0; i < number_cuts; i++) {
3842 factor = float(i + 1) / (number_cuts + 1);
3843 prevbp = bp - 1;
3844 *bpn = *prevbp;
3845 interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
3846 bpn++;
3847 }
3848 }
3849 }
3850 }
3851 MEM_freeN(nu->bp);
3852 nu->bp = bpnew;
3853 nu->pntsu += sel;
3854 BKE_nurb_knot_calc_u(nu); /* shift knots forward */
3855 }
3856 }
3857 }
3858 MEM_freeN(usel);
3859 MEM_freeN(vsel);
3860
3861 } /* End of `if (nu->type == CU_NURBS)`. */
3862 }
3863}
3864
3866{
3867 const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
3868
3869 Main *bmain = CTX_data_main(C);
3870 const Scene *scene = CTX_data_scene(C);
3871 ViewLayer *view_layer = CTX_data_view_layer(C);
3872 View3D *v3d = CTX_wm_view3d(C);
3873
3875 scene, view_layer, CTX_wm_view3d(C));
3876 for (Object *obedit : objects) {
3877 Curve *cu = static_cast<Curve *>(obedit->data);
3878
3879 if (!ED_curve_select_check(v3d, cu->editnurb)) {
3880 continue;
3881 }
3882
3883 subdividenurb(obedit, v3d, number_cuts);
3884
3885 if (ED_curve_updateAnimPaths(bmain, cu)) {
3887 }
3888
3890 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
3891 }
3892
3893 return OPERATOR_FINISHED;
3894}
3895
3897{
3898 PropertyRNA *prop;
3899
3900 /* identifiers */
3901 ot->name = "Subdivide";
3902 ot->description = "Subdivide selected segments";
3903 ot->idname = "CURVE_OT_subdivide";
3904
3905 /* api callbacks */
3908
3909 /* flags */
3911
3912 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
3913 /* Avoid re-using last var because it can cause _very_ high poly meshes
3914 * and annoy users (or worse crash). */
3916}
3917
3920/* -------------------------------------------------------------------- */
3925{
3926 const Scene *scene = CTX_data_scene(C);
3927 ViewLayer *view_layer = CTX_data_view_layer(C);
3929 scene, view_layer, CTX_wm_view3d(C));
3930 int ret_value = OPERATOR_CANCELLED;
3931
3932 for (Object *obedit : objects) {
3933 Main *bmain = CTX_data_main(C);
3934 View3D *v3d = CTX_wm_view3d(C);
3935 ListBase *editnurb = object_editcurve_get(obedit);
3936 bool changed = false;
3937 bool changed_size = false;
3938 const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
3939 const int type = RNA_enum_get(op->ptr, "type");
3940
3941 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
3942 if (ED_curve_nurb_select_check(v3d, nu)) {
3943 const int pntsu_prev = nu->pntsu;
3944 const char *err_msg = nullptr;
3945 if (BKE_nurb_type_convert(nu, type, use_handles, &err_msg)) {
3946 changed = true;
3947 if (pntsu_prev != nu->pntsu) {
3948 changed_size = true;
3949 }
3950 }
3951 else {
3952 BKE_report(op->reports, RPT_ERROR, err_msg);
3953 }
3954 }
3955 }
3956
3957 if (changed) {
3958 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
3960 }
3961
3962 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
3964
3965 if (changed_size) {
3966 Curve *cu = static_cast<Curve *>(obedit->data);
3967 cu->actvert = CU_ACT_NONE;
3968 }
3969
3970 ret_value = OPERATOR_FINISHED;
3971 }
3972 }
3973
3974 return ret_value;
3975}
3976
3978{
3979 static const EnumPropertyItem type_items[] = {
3980 {CU_POLY, "POLY", 0, "Poly", ""},
3981 {CU_BEZIER, "BEZIER", 0, "Bézier", ""},
3982 {CU_NURBS, "NURBS", 0, "NURBS", ""},
3983 {0, nullptr, 0, nullptr, nullptr},
3984 };
3985
3986 /* identifiers */
3987 ot->name = "Set Spline Type";
3988 ot->description = "Set type of active spline";
3989 ot->idname = "CURVE_OT_spline_type_set";
3990
3991 /* api callbacks */
3995
3996 /* flags */
3998
3999 /* properties */
4000 ot->prop = RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type");
4002 "use_handles",
4003 false,
4004 "Handles",
4005 "Use handles when converting Bézier curves into polygons");
4006}
4007
4010/* -------------------------------------------------------------------- */
4015{
4016 const Scene *scene = CTX_data_scene(C);
4017 ViewLayer *view_layer = CTX_data_view_layer(C);
4018 View3D *v3d = CTX_wm_view3d(C);
4019 const int handle_type = RNA_enum_get(op->ptr, "type");
4020 const bool hide_handles = (v3d && (v3d->overlay.handle_display == CURVE_HANDLE_NONE));
4021 const eNurbHandleTest_Mode handle_mode = hide_handles ? NURB_HANDLE_TEST_KNOT_ONLY :
4023
4025 scene, view_layer, CTX_wm_view3d(C));
4026 for (Object *obedit : objects) {
4027 Curve *cu = static_cast<Curve *>(obedit->data);
4028
4029 if (!ED_curve_select_check(v3d, cu->editnurb)) {
4030 continue;
4031 }
4032
4033 ListBase *editnurb = object_editcurve_get(obedit);
4034 BKE_nurbList_handles_set(editnurb, handle_mode, handle_type);
4035
4037 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
4038 }
4039 return OPERATOR_FINISHED;
4040}
4041
4043{
4044 /* keep in sync with graphkeys_handle_type_items */
4045 static const EnumPropertyItem editcurve_handle_type_items[] = {
4046 {HD_AUTO, "AUTOMATIC", 0, "Automatic", ""},
4047 {HD_VECT, "VECTOR", 0, "Vector", ""},
4048 {5, "ALIGNED", 0, "Aligned", ""},
4049 {6, "FREE_ALIGN", 0, "Free", ""},
4050 {3, "TOGGLE_FREE_ALIGN", 0, "Toggle Free/Align", ""},
4051 {0, nullptr, 0, nullptr, nullptr},
4052 };
4053
4054 /* identifiers */
4055 ot->name = "Set Handle Type";
4056 ot->description = "Set type of handles for selected control points";
4057 ot->idname = "CURVE_OT_handle_type_set";
4058
4059 /* api callbacks */
4063
4064 /* flags */
4066
4067 /* properties */
4068 ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
4069}
4070
4073/* -------------------------------------------------------------------- */
4078{
4079 const Scene *scene = CTX_data_scene(C);
4080 ViewLayer *view_layer = CTX_data_view_layer(C);
4081 View3D *v3d = CTX_wm_view3d(C);
4082
4083 const bool calc_length = RNA_boolean_get(op->ptr, "calc_length");
4084
4086 scene, view_layer, CTX_wm_view3d(C));
4087
4088 int totobjects = 0;
4089
4090 for (Object *obedit : objects) {
4091 Curve *cu = static_cast<Curve *>(obedit->data);
4092
4093 if (!ED_curve_select_check(v3d, cu->editnurb)) {
4094 continue;
4095 }
4096
4098 continue;
4099 }
4100
4101 totobjects++;
4102
4103 ListBase *editnurb = object_editcurve_get(obedit);
4104 BKE_nurbList_handles_recalculate(editnurb, calc_length, SELECT);
4105
4107 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
4108 }
4109 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
4110}
4111
4113{
4114 /* identifiers */
4115 ot->name = "Recalculate Handles";
4116 ot->description = "Recalculate the direction of selected handles";
4117 ot->idname = "CURVE_OT_normals_make_consistent";
4118
4119 /* api callbacks */
4122
4123 /* flags */
4125
4126 /* props */
4127 RNA_def_boolean(ot->srna, "calc_length", false, "Length", "Recalculate handle length");
4128}
4129
4132/* -------------------------------------------------------------------- */
4138static void switchdirection_knots(float *base, int tot)
4139{
4140 float *fp1, *fp2, *tempf;
4141 int a;
4142
4143 if (base == nullptr || tot == 0) {
4144 return;
4145 }
4146
4147 /* reverse knots */
4148 a = tot;
4149 fp1 = base;
4150 fp2 = fp1 + (a - 1);
4151 a /= 2;
4152 while (fp1 != fp2 && a > 0) {
4153 std::swap(*fp1, *fp2);
4154 a--;
4155 fp1++;
4156 fp2--;
4157 }
4158
4159 /* and make in increasing order again */
4160 a = tot - 1;
4161 fp1 = base;
4162 fp2 = tempf = static_cast<float *>(MEM_mallocN(sizeof(float) * tot, "switchdirect"));
4163 while (a--) {
4164 fp2[0] = fabsf(fp1[1] - fp1[0]);
4165 fp1++;
4166 fp2++;
4167 }
4168 fp2[0] = 0.0f;
4169
4170 a = tot - 1;
4171 fp1 = base;
4172 fp2 = tempf;
4173 fp1[0] = 0.0;
4174 fp1++;
4175 while (a--) {
4176 fp1[0] = fp1[-1] + fp2[0];
4177 fp1++;
4178 fp2++;
4179 }
4180 MEM_freeN(tempf);
4181}
4182
4184{
4185 BPoint *bp1, *bp2, *temp;
4186 int u, v;
4187
4188 std::swap(nu->pntsu, nu->pntsv);
4189 std::swap(nu->orderu, nu->orderv);
4190 std::swap(nu->resolu, nu->resolv);
4191 std::swap(nu->flagu, nu->flagv);
4192
4193 std::swap(nu->knotsu, nu->knotsv);
4195
4196 temp = static_cast<BPoint *>(MEM_dupallocN(nu->bp));
4197 bp1 = nu->bp;
4198 for (v = 0; v < nu->pntsv; v++) {
4199 for (u = 0; u < nu->pntsu; u++, bp1++) {
4200 bp2 = temp + (nu->pntsu - u - 1) * (nu->pntsv) + v;
4201 *bp1 = *bp2;
4202 }
4203 }
4204
4205 MEM_freeN(temp);
4206}
4207
4208static bool is_u_selected(Nurb *nu, int u)
4209{
4210 BPoint *bp;
4211 int v;
4212
4213 /* what about resolu == 2? */
4214 bp = &nu->bp[u];
4215 for (v = 0; v < nu->pntsv - 1; v++, bp += nu->pntsu) {
4216 if ((v != 0) && (bp->f1 & SELECT)) {
4217 return true;
4218 }
4219 }
4220
4221 return false;
4222}
4223
4224struct NurbSort {
4227 float vec[3];
4228};
4229
4230static void make_selection_list_nurb(View3D *v3d, ListBase *editnurb, ListBase *nsortbase)
4231{
4232 ListBase nbase = {nullptr, nullptr};
4233 NurbSort *nus, *nustest, *headdo, *taildo;
4234 BPoint *bp;
4235 float dist, headdist, taildist;
4236 int a;
4237
4238 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
4239 if (ED_curve_nurb_select_check(v3d, nu)) {
4240
4241 nus = (NurbSort *)MEM_callocN(sizeof(NurbSort), "sort");
4242 BLI_addhead(&nbase, nus);
4243 nus->nu = nu;
4244
4245 bp = nu->bp;
4246 a = nu->pntsu;
4247 while (a--) {
4248 add_v3_v3(nus->vec, bp->vec);
4249 bp++;
4250 }
4251 mul_v3_fl(nus->vec, 1.0f / float(nu->pntsu));
4252 }
4253 }
4254
4255 /* just add the first one */
4256 nus = static_cast<NurbSort *>(nbase.first);
4257 BLI_remlink(&nbase, nus);
4258 BLI_addtail(nsortbase, nus);
4259
4260 /* now add, either at head or tail, the closest one */
4261 while (nbase.first) {
4262
4263 headdist = taildist = 1.0e30;
4264 headdo = taildo = nullptr;
4265
4266 nustest = static_cast<NurbSort *>(nbase.first);
4267 while (nustest) {
4268 dist = len_v3v3(nustest->vec, ((NurbSort *)nsortbase->first)->vec);
4269
4270 if (dist < headdist) {
4271 headdist = dist;
4272 headdo = nustest;
4273 }
4274 dist = len_v3v3(nustest->vec, ((NurbSort *)nsortbase->last)->vec);
4275
4276 if (dist < taildist) {
4277 taildist = dist;
4278 taildo = nustest;
4279 }
4280 nustest = nustest->next;
4281 }
4282
4283 if (headdist < taildist) {
4284 BLI_remlink(&nbase, headdo);
4285 BLI_addhead(nsortbase, headdo);
4286 }
4287 else {
4288 BLI_remlink(&nbase, taildo);
4289 BLI_addtail(nsortbase, taildo);
4290 }
4291 }
4292}
4293
4294enum {
4299};
4300
4301static bool merge_2_nurb(Curve *cu, ListBase *editnurb, Nurb *nu1, Nurb *nu2)
4302{
4303 BPoint *bp, *bp1, *bp2, *temp;
4304 float len1, len2;
4305 int origu, u, v;
4306
4307 /* first nurbs will be changed to make u = resolu-1 selected */
4308 /* 2nd nurbs will be changed to make u = 0 selected */
4309
4310 /* first nurbs: u = resolu-1 selected */
4311
4312 if (is_u_selected(nu1, nu1->pntsu - 1)) {
4313 /* pass */
4314 }
4315 else {
4316 /* For 2D curves blender uses (orderv = 0). It doesn't make any sense mathematically. */
4317 /* but after rotating (orderu = 0) will be confusing. */
4318 if (nu1->orderv == 0) {
4319 nu1->orderv = 1;
4320 }
4321
4323 if (is_u_selected(nu1, nu1->pntsu - 1)) {
4324 /* pass */
4325 }
4326 else {
4328 if (is_u_selected(nu1, nu1->pntsu - 1)) {
4329 /* pass */
4330 }
4331 else {
4333 if (is_u_selected(nu1, nu1->pntsu - 1)) {
4334 /* pass */
4335 }
4336 else {
4337 /* rotate again, now its OK! */
4338 if (nu1->pntsv != 1) {
4340 }
4341 return true;
4342 }
4343 }
4344 }
4345 }
4346
4347 /* 2nd nurbs: u = 0 selected */
4348 if (is_u_selected(nu2, 0)) {
4349 /* pass */
4350 }
4351 else {
4352 if (nu2->orderv == 0) {
4353 nu2->orderv = 1;
4354 }
4356 if (is_u_selected(nu2, 0)) {
4357 /* pass */
4358 }
4359 else {
4361 if (is_u_selected(nu2, 0)) {
4362 /* pass */
4363 }
4364 else {
4366 if (is_u_selected(nu2, 0)) {
4367 /* pass */
4368 }
4369 else {
4370 /* rotate again, now its OK! */
4371 if (nu1->pntsu == 1) {
4373 }
4374 if (nu2->pntsv != 1) {
4376 }
4377 return true;
4378 }
4379 }
4380 }
4381 }
4382
4383 if (nu1->pntsv != nu2->pntsv) {
4384 return false;
4385 }
4386
4387 /* ok, now nu1 has the rightmost column and nu2 the leftmost column selected */
4388 /* maybe we need a 'v' flip of nu2? */
4389
4390 bp1 = &nu1->bp[nu1->pntsu - 1];
4391 bp2 = nu2->bp;
4392 len1 = 0.0;
4393
4394 for (v = 0; v < nu1->pntsv; v++, bp1 += nu1->pntsu, bp2 += nu2->pntsu) {
4395 len1 += len_v3v3(bp1->vec, bp2->vec);
4396 }
4397
4398 bp1 = &nu1->bp[nu1->pntsu - 1];
4399 bp2 = &nu2->bp[nu2->pntsu * (nu2->pntsv - 1)];
4400 len2 = 0.0;
4401
4402 for (v = 0; v < nu1->pntsv; v++, bp1 += nu1->pntsu, bp2 -= nu2->pntsu) {
4403 len2 += len_v3v3(bp1->vec, bp2->vec);
4404 }
4405
4406 /* merge */
4407 origu = nu1->pntsu;
4408 nu1->pntsu += nu2->pntsu;
4409 if (nu1->orderu < 3 && nu1->orderu < nu1->pntsu) {
4410 nu1->orderu++;
4411 }
4412 if (nu1->orderv < 3 && nu1->orderv < nu1->pntsv) {
4413 nu1->orderv++;
4414 }
4415 temp = nu1->bp;
4416 nu1->bp = static_cast<BPoint *>(
4417 MEM_mallocN(nu1->pntsu * nu1->pntsv * sizeof(BPoint), "mergeBP"));
4418
4419 bp = nu1->bp;
4420 bp1 = temp;
4421
4422 for (v = 0; v < nu1->pntsv; v++) {
4423
4424 /* switch direction? */
4425 if (len1 < len2) {
4426 bp2 = &nu2->bp[v * nu2->pntsu];
4427 }
4428 else {
4429 bp2 = &nu2->bp[(nu1->pntsv - v - 1) * nu2->pntsu];
4430 }
4431
4432 for (u = 0; u < nu1->pntsu; u++, bp++) {
4433 if (u < origu) {
4434 keyIndex_updateBP(cu->editnurb, bp1, bp, 1);
4435 *bp = *bp1;
4436 bp1++;
4437 select_bpoint(bp, true, SELECT, HIDDEN);
4438 }
4439 else {
4440 keyIndex_updateBP(cu->editnurb, bp2, bp, 1);
4441 *bp = *bp2;
4442 bp2++;
4443 }
4444 }
4445 }
4446
4447 if (nu1->type == CU_NURBS) {
4448 /* merge knots */
4450
4451 /* make knots, for merged curved for example */
4453 }
4454
4455 MEM_freeN(temp);
4456 BLI_remlink(editnurb, nu2);
4457 BKE_nurb_free(nu2);
4458 return true;
4459}
4460
4461static int merge_nurb(View3D *v3d, Object *obedit)
4462{
4463 Curve *cu = static_cast<Curve *>(obedit->data);
4464 ListBase *editnurb = object_editcurve_get(obedit);
4465 NurbSort *nus1, *nus2;
4466 bool ok = true;
4467 ListBase nsortbase = {nullptr, nullptr};
4468
4469 make_selection_list_nurb(v3d, editnurb, &nsortbase);
4470
4471 if (nsortbase.first == nsortbase.last) {
4472 BLI_freelistN(&nsortbase);
4474 }
4475
4476 nus1 = static_cast<NurbSort *>(nsortbase.first);
4477 nus2 = nus1->next;
4478
4479 /* resolution match, to avoid uv rotations */
4480 if (nus1->nu->pntsv == 1) {
4481 if (ELEM(nus1->nu->pntsu, nus2->nu->pntsu, nus2->nu->pntsv)) {
4482 /* pass */
4483 }
4484 else {
4485 ok = false;
4486 }
4487 }
4488 else if (nus2->nu->pntsv == 1) {
4489 if (ELEM(nus2->nu->pntsu, nus1->nu->pntsu, nus1->nu->pntsv)) {
4490 /* pass */
4491 }
4492 else {
4493 ok = false;
4494 }
4495 }
4496 else if (nus1->nu->pntsu == nus2->nu->pntsu || nus1->nu->pntsv == nus2->nu->pntsv) {
4497 /* pass */
4498 }
4499 else if (nus1->nu->pntsu == nus2->nu->pntsv || nus1->nu->pntsv == nus2->nu->pntsu) {
4500 /* pass */
4501 }
4502 else {
4503 ok = false;
4504 }
4505
4506 if (ok == false) {
4507 BLI_freelistN(&nsortbase);
4509 }
4510
4511 while (nus2) {
4512 /* There is a change a few curves merged properly, but not all.
4513 * In this case we still update the curve, yet report the error. */
4514 ok &= merge_2_nurb(cu, editnurb, nus1->nu, nus2->nu);
4515 nus2 = nus2->next;
4516 }
4517
4518 BLI_freelistN(&nsortbase);
4519 BKE_curve_nurb_active_set(static_cast<Curve *>(obedit->data), nullptr);
4520
4522}
4523
4525{
4526 Main *bmain = CTX_data_main(C);
4527 const Scene *scene = CTX_data_scene(C);
4528 ViewLayer *view_layer = CTX_data_view_layer(C);
4529 View3D *v3d = CTX_wm_view3d(C);
4530
4531 struct {
4532 int changed;
4533 int unselected;
4534 int error_selected_few;
4535 int error_resolution;
4536 int error_generic;
4537 } status = {0};
4538
4540 scene, view_layer, CTX_wm_view3d(C));
4541 for (Object *obedit : objects) {
4542 Curve *cu = static_cast<Curve *>(obedit->data);
4543
4544 if (!ED_curve_select_check(v3d, cu->editnurb)) {
4545 status.unselected++;
4546 continue;
4547 }
4548
4549 ListBase *nubase = object_editcurve_get(obedit);
4550 Nurb *nu, *nu1 = nullptr, *nu2 = nullptr;
4551 BPoint *bp;
4552 bool ok = false;
4553
4554 /* first decide if this is a surface merge! */
4555 if (obedit->type == OB_SURF) {
4556 nu = static_cast<Nurb *>(nubase->first);
4557 }
4558 else {
4559 nu = nullptr;
4560 }
4561
4562 while (nu) {
4563 const int nu_select_num = ED_curve_nurb_select_count(v3d, nu);
4564 if (nu_select_num) {
4565
4566 if (nu->pntsu > 1 && nu->pntsv > 1) {
4567 break;
4568 }
4569
4570 if (nu_select_num > 1) {
4571 break;
4572 }
4573 /* only 1 selected, not first or last, a little complex, but intuitive */
4574 if (nu->pntsv == 1) {
4575 if ((nu->bp->f1 & SELECT) || (nu->bp[nu->pntsu - 1].f1 & SELECT)) {
4576 /* pass */
4577 }
4578 else {
4579 break;
4580 }
4581 }
4582 }
4583 nu = nu->next;
4584 }
4585
4586 if (nu) {
4587 int merge_result = merge_nurb(v3d, obedit);
4588 switch (merge_result) {
4589 case CURVE_MERGE_OK:
4590 status.changed++;
4591 goto curve_merge_tag_object;
4593 status.error_resolution++;
4594 goto curve_merge_tag_object;
4596 status.error_selected_few++;
4597 break;
4599 status.error_resolution++;
4600 break;
4601 }
4602 continue;
4603 }
4604
4605 /* find both nurbs and points, nu1 will be put behind nu2 */
4606 LISTBASE_FOREACH (Nurb *, nu, nubase) {
4607 if (nu->pntsu == 1) {
4608 nu->flagu &= ~CU_NURB_CYCLIC;
4609 }
4610
4611 if ((nu->flagu & CU_NURB_CYCLIC) == 0) { /* not cyclic */
4612 if (nu->type == CU_BEZIER) {
4613 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &(nu->bezt[nu->pntsu - 1]))) {
4614 /* Last point is selected, preferred for nu2 */
4615 if (nu2 == nullptr) {
4616 nu2 = nu;
4617 }
4618 else if (nu1 == nullptr) {
4619 nu1 = nu;
4620
4621 /* Just in case both of first/last CV are selected check
4622 * whether we really need to switch the direction.
4623 */
4624 if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu1->bezt)) {
4627 }
4628 }
4629 }
4630 else if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu->bezt)) {
4631 /* First point is selected, preferred for nu1 */
4632 if (nu1 == nullptr) {
4633 nu1 = nu;
4634 }
4635 else if (nu2 == nullptr) {
4636 nu2 = nu;
4637
4638 /* Just in case both of first/last CV are selected check
4639 * whether we really need to switch the direction.
4640 */
4641 if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &(nu->bezt[nu2->pntsu - 1]))) {
4644 }
4645 }
4646 }
4647 }
4648 else if (nu->pntsv == 1) {
4649 /* Same logic as above: if first point is selected spline is
4650 * preferred for nu1, if last point is selected spline is
4651 * preferred for u2u.
4652 */
4653
4654 bp = nu->bp;
4655 if (bp[nu->pntsu - 1].f1 & SELECT) {
4656 if (nu2 == nullptr) {
4657 nu2 = nu;
4658 }
4659 else if (nu1 == nullptr) {
4660 nu1 = nu;
4661
4662 if ((bp->f1 & SELECT) == 0) {
4665 }
4666 }
4667 }
4668 else if (bp->f1 & SELECT) {
4669 if (nu1 == nullptr) {
4670 nu1 = nu;
4671 }
4672 else if (nu2 == nullptr) {
4673 nu2 = nu;
4674
4675 if ((bp[nu->pntsu - 1].f1 & SELECT) == 0) {
4678 }
4679 }
4680 }
4681 }
4682 }
4683
4684 if (nu1 && nu2) {
4685 /* Got second spline, no need to loop over rest of the splines. */
4686 break;
4687 }
4688 }
4689
4690 if ((nu1 && nu2) && (nu1 != nu2)) {
4691 if (nu1->type == nu2->type) {
4692 if (nu1->type == CU_BEZIER) {
4693 BezTriple *bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + nu2->pntsu) * sizeof(BezTriple),
4694 "addsegmentN");
4695 ED_curve_beztcpy(cu->editnurb, bezt, nu2->bezt, nu2->pntsu);
4696 ED_curve_beztcpy(cu->editnurb, bezt + nu2->pntsu, nu1->bezt, nu1->pntsu);
4697
4698 MEM_freeN(nu1->bezt);
4699 nu1->bezt = bezt;
4700 nu1->pntsu += nu2->pntsu;
4701 BLI_remlink(nubase, nu2);
4702 keyIndex_delNurb(cu->editnurb, nu2);
4703 BKE_nurb_free(nu2);
4704 nu2 = nullptr;
4706 }
4707 else {
4708 bp = (BPoint *)MEM_mallocN((nu1->pntsu + nu2->pntsu) * sizeof(BPoint), "addsegmentN2");
4709 ED_curve_bpcpy(cu->editnurb, bp, nu2->bp, nu2->pntsu);
4710 ED_curve_bpcpy(cu->editnurb, bp + nu2->pntsu, nu1->bp, nu1->pntsu);
4711 MEM_freeN(nu1->bp);
4712 nu1->bp = bp;
4713
4714 // a = nu1->pntsu + nu1->orderu; /* UNUSED */
4715
4716 nu1->pntsu += nu2->pntsu;
4717 BLI_remlink(nubase, nu2);
4718
4719 /* now join the knots */
4720 if (nu1->type == CU_NURBS) {
4721 MEM_SAFE_FREE(nu1->knotsu);
4722
4724 }
4725 keyIndex_delNurb(cu->editnurb, nu2);
4726 BKE_nurb_free(nu2);
4727 nu2 = nullptr;
4728 }
4729
4730 BKE_curve_nurb_active_set(cu, nu1); /* for selected */
4731 ok = true;
4732 }
4733 }
4734 else if ((nu1 && !nu2) || (!nu1 && nu2)) {
4735 if (nu2) {
4736 std::swap(nu1, nu2);
4737 }
4738
4739 if (!(nu1->flagu & CU_NURB_CYCLIC) && nu1->pntsu > 1) {
4740 if (nu1->type == CU_BEZIER && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu1->bezt) &&
4741 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu1->bezt[nu1->pntsu - 1]))
4742 {
4743 nu1->flagu |= CU_NURB_CYCLIC;
4745 ok = true;
4746 }
4747 else if (ELEM(nu1->type, CU_NURBS, CU_POLY) && nu1->bp->f1 & SELECT &&
4748 (nu1->bp[nu1->pntsu - 1].f1 & SELECT))
4749 {
4750 nu1->flagu |= CU_NURB_CYCLIC;
4752 ok = true;
4753 }
4754 }
4755 }
4756
4757 if (!ok) {
4758 status.error_generic++;
4759 continue;
4760 }
4761
4762 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
4764 }
4765
4766 status.changed++;
4767
4768 curve_merge_tag_object:
4770 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
4771 }
4772
4773 if (status.unselected == objects.size()) {
4774 BKE_report(op->reports, RPT_ERROR, "No points were selected");
4775 return OPERATOR_CANCELLED;
4776 }
4777
4778 const int tot_errors = status.error_selected_few + status.error_resolution +
4779 status.error_generic;
4780 if (tot_errors > 0) {
4781 /* Some curves changed, but some curves failed: don't explain why it failed. */
4782 if (status.changed) {
4783 BKE_reportf(op->reports, RPT_INFO, "%d curves could not make segments", tot_errors);
4784 return OPERATOR_FINISHED;
4785 }
4786
4787 /* All curves failed: If there is more than one error give a generic error report. */
4788 if (((status.error_selected_few ? 1 : 0) + (status.error_resolution ? 1 : 0) +
4789 (status.error_generic ? 1 : 0)) > 1)
4790 {
4791 BKE_report(op->reports, RPT_ERROR, "Could not make new segments");
4792 }
4793
4794 /* All curves failed due to the same error. */
4795 if (status.error_selected_few) {
4796 BKE_report(op->reports, RPT_ERROR, "Too few selections to merge");
4797 }
4798 else if (status.error_resolution) {
4799 BKE_report(op->reports, RPT_ERROR, "Resolution does not match");
4800 }
4801 else {
4802 BLI_assert(status.error_generic);
4803 BKE_report(op->reports, RPT_ERROR, "Cannot make segment");
4804 }
4805 return OPERATOR_CANCELLED;
4806 }
4807
4808 return OPERATOR_FINISHED;
4809}
4810
4812{
4813 /* identifiers */
4814 ot->name = "Make Segment";
4815 ot->idname = "CURVE_OT_make_segment";
4816 ot->description = "Join two curves by their selected ends";
4817
4818 /* api callbacks */
4821
4822 /* flags */
4824}
4825
4828/* -------------------------------------------------------------------- */
4833 const int mval[2],
4834 const int dist_px,
4836{
4838 Nurb *nu;
4839 BezTriple *bezt = nullptr;
4840 BPoint *bp = nullptr;
4841 Base *basact = nullptr;
4842 short hand;
4843 bool changed = false;
4844
4847 copy_v2_v2_int(vc.mval, mval);
4848
4849 const bool use_handle_select = (vc.v3d->overlay.handle_display != CURVE_HANDLE_NONE);
4850
4851 bool found = ED_curve_pick_vert_ex(&vc, true, dist_px, &nu, &bezt, &bp, &hand, &basact);
4852
4853 if (params->sel_op == SEL_OP_SET) {
4854 if ((found && params->select_passthrough) &&
4855 (((bezt ? (&bezt->f1)[hand] : bp->f1) & SELECT) != 0))
4856 {
4857 found = false;
4858 }
4859 else if (found || params->deselect_all) {
4860 /* Deselect everything. */
4862 vc.scene, vc.view_layer, vc.v3d);
4863 for (Object *ob_iter : objects) {
4864 ED_curve_deselect_all(((Curve *)ob_iter->data)->editnurb);
4865 DEG_id_tag_update(static_cast<ID *>(ob_iter->data),
4867 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
4868 }
4869 changed = true;
4870 }
4871 }
4872
4873 if (found) {
4874 Object *obedit = basact->object;
4875 Curve *cu = static_cast<Curve *>(obedit->data);
4876 ListBase *editnurb = object_editcurve_get(obedit);
4877 const void *vert = BKE_curve_vert_active_get(cu);
4878
4879 switch (params->sel_op) {
4880 case SEL_OP_ADD: {
4881 if (bezt) {
4882 if (hand == 1) {
4883 if (use_handle_select) {
4884 bezt->f2 |= SELECT;
4885 }
4886 else {
4887 select_beztriple(bezt, true, SELECT, HIDDEN);
4888 }
4889 }
4890 else {
4891 if (hand == 0) {
4892 bezt->f1 |= SELECT;
4893 }
4894 else {
4895 bezt->f3 |= SELECT;
4896 }
4897 }
4898 BKE_curve_nurb_vert_active_set(cu, nu, bezt);
4899 }
4900 else {
4901 select_bpoint(bp, true, SELECT, HIDDEN);
4903 }
4904 break;
4905 }
4906 case SEL_OP_SUB: {
4907 if (bezt) {
4908 if (hand == 1) {
4909 if (use_handle_select) {
4910 bezt->f2 &= ~SELECT;
4911 }
4912 else {
4913 select_beztriple(bezt, false, SELECT, HIDDEN);
4914 }
4915 if (bezt == vert) {
4916 cu->actvert = CU_ACT_NONE;
4917 }
4918 }
4919 else if (hand == 0) {
4920 bezt->f1 &= ~SELECT;
4921 }
4922 else {
4923 bezt->f3 &= ~SELECT;
4924 }
4925 }
4926 else {
4927 select_bpoint(bp, false, SELECT, HIDDEN);
4928 if (bp == vert) {
4929 cu->actvert = CU_ACT_NONE;
4930 }
4931 }
4932 break;
4933 }
4934 case SEL_OP_XOR: {
4935 if (bezt) {
4936 if (hand == 1) {
4937 if (bezt->f2 & SELECT) {
4938 if (use_handle_select) {
4939 bezt->f2 &= ~SELECT;
4940 }
4941 else {
4942 select_beztriple(bezt, false, SELECT, HIDDEN);
4943 }
4944 if (bezt == vert) {
4945 cu->actvert = CU_ACT_NONE;
4946 }
4947 }
4948 else {
4949 if (use_handle_select) {
4950 bezt->f2 |= SELECT;
4951 }
4952 else {
4953 select_beztriple(bezt, true, SELECT, HIDDEN);
4954 }
4955 BKE_curve_nurb_vert_active_set(cu, nu, bezt);
4956 }
4957 }
4958 else if (hand == 0) {
4959 bezt->f1 ^= SELECT;
4960 }
4961 else {
4962 bezt->f3 ^= SELECT;
4963 }
4964 }
4965 else {
4966 if (bp->f1 & SELECT) {
4967 select_bpoint(bp, false, SELECT, HIDDEN);
4968 if (bp == vert) {
4969 cu->actvert = CU_ACT_NONE;
4970 }
4971 }
4972 else {
4973 select_bpoint(bp, true, SELECT, HIDDEN);
4975 }
4976 }
4977 break;
4978 }
4979 case SEL_OP_SET: {
4980 BKE_nurbList_flag_set(editnurb, SELECT, false);
4981
4982 if (bezt) {
4983
4984 if (hand == 1) {
4985 if (use_handle_select) {
4986 bezt->f2 |= SELECT;
4987 }
4988 else {
4989 select_beztriple(bezt, true, SELECT, HIDDEN);
4990 }
4991 }
4992 else {
4993 if (hand == 0) {
4994 bezt->f1 |= SELECT;
4995 }
4996 else {
4997 bezt->f3 |= SELECT;
4998 }
4999 }
5000 BKE_curve_nurb_vert_active_set(cu, nu, bezt);
5001 }
5002 else {
5003 select_bpoint(bp, true, SELECT, HIDDEN);
5005 }
5006 break;
5007 }
5008 case SEL_OP_AND: {
5009 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
5010 break;
5011 }
5012 }
5013
5014 if (nu != BKE_curve_nurb_active_get(cu)) {
5015 cu->actvert = CU_ACT_NONE;
5017 }
5018
5019 /* Change active material on object. */
5020 if (nu->mat_nr != obedit->actcol - 1) {
5021 obedit->actcol = nu->mat_nr + 1;
5023 }
5024
5026 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
5028 }
5029
5032
5033 changed = true;
5034 }
5035
5036 return changed || found;
5037}
5038
5041/* -------------------------------------------------------------------- */
5046 float viewmat[4][4], View3D *v3d, Object *obedit, const float axis[3], const float cent[3])
5047{
5048 Curve *cu = (Curve *)obedit->data;
5049 ListBase *editnurb = object_editcurve_get(obedit);
5050 float cmat[3][3], tmat[3][3], imat[3][3];
5051 float bmat[3][3], rotmat[3][3], scalemat1[3][3], scalemat2[3][3];
5052 float persmat[3][3], persinv[3][3];
5053 bool ok, changed = false;
5054 int a;
5055
5056 copy_m3_m4(persmat, viewmat);
5057 invert_m3_m3(persinv, persmat);
5058
5059 /* imat and center and size */
5060 copy_m3_m4(bmat, obedit->object_to_world().ptr());
5061 invert_m3_m3(imat, bmat);
5062
5063 axis_angle_to_mat3(cmat, axis, M_PI_4);
5064 mul_m3_m3m3(tmat, cmat, bmat);
5065 mul_m3_m3m3(rotmat, imat, tmat);
5066
5067 unit_m3(scalemat1);
5068 scalemat1[0][0] = M_SQRT2;
5069 scalemat1[1][1] = M_SQRT2;
5070
5071 mul_m3_m3m3(tmat, persmat, bmat);
5072 mul_m3_m3m3(cmat, scalemat1, tmat);
5073 mul_m3_m3m3(tmat, persinv, cmat);
5074 mul_m3_m3m3(scalemat1, imat, tmat);
5075
5076 unit_m3(scalemat2);
5077 scalemat2[0][0] /= float(M_SQRT2);
5078 scalemat2[1][1] /= float(M_SQRT2);
5079
5080 mul_m3_m3m3(tmat, persmat, bmat);
5081 mul_m3_m3m3(cmat, scalemat2, tmat);
5082 mul_m3_m3m3(tmat, persinv, cmat);
5083 mul_m3_m3m3(scalemat2, imat, tmat);
5084
5085 ok = true;
5086
5087 for (a = 0; a < 7; a++) {
5089
5090 if (ok == false) {
5091 return changed;
5092 }
5093
5094 changed = true;
5095
5096 rotateflagNurb(editnurb, SELECT, cent, rotmat);
5097
5098 if ((a & 1) == 0) {
5099 rotateflagNurb(editnurb, SELECT, cent, scalemat1);
5100 weightflagNurb(editnurb, SELECT, 0.5 * M_SQRT2);
5101 }
5102 else {
5103 rotateflagNurb(editnurb, SELECT, cent, scalemat2);
5104 weightflagNurb(editnurb, SELECT, 2.0 / M_SQRT2);
5105 }
5106 }
5107
5108 if (ok) {
5109 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
5110 if (ED_curve_nurb_select_check(v3d, nu)) {
5111 nu->orderv = 3;
5112 /* It is challenging to create a good approximation of a circle with uniform knots vector
5113 * (which is forced in Blender for cyclic NURBS curves). Here a NURBS circle is constructed
5114 * by connecting four Bezier arcs. */
5117 }
5118 }
5119 }
5120
5121 return changed;
5122}
5123
5124static int spin_exec(bContext *C, wmOperator *op)
5125{
5126 Main *bmain = CTX_data_main(C);
5127 const Scene *scene = CTX_data_scene(C);
5128 ViewLayer *view_layer = CTX_data_view_layer(C);
5129 View3D *v3d = CTX_wm_view3d(C);
5131 float cent[3], axis[3], viewmat[4][4];
5132 bool changed = false;
5133 int count_failed = 0;
5134
5135 RNA_float_get_array(op->ptr, "center", cent);
5136 RNA_float_get_array(op->ptr, "axis", axis);
5137
5138 if (rv3d) {
5139 copy_m4_m4(viewmat, rv3d->viewmat);
5140 }
5141 else {
5142 unit_m4(viewmat);
5143 }
5144
5146 scene, view_layer, CTX_wm_view3d(C));
5147 for (Object *obedit : objects) {
5148 Curve *cu = (Curve *)obedit->data;
5149
5150 if (!ED_curve_select_check(v3d, cu->editnurb)) {
5151 continue;
5152 }
5153
5154 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
5155 mul_m4_v3(obedit->world_to_object().ptr(), cent);
5156
5157 if (!ed_editnurb_spin(viewmat, v3d, obedit, axis, cent)) {
5158 count_failed += 1;
5159 continue;
5160 }
5161
5162 changed = true;
5163 if (ED_curve_updateAnimPaths(bmain, cu)) {
5165 }
5166
5168 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
5169 }
5170
5171 if (changed == false) {
5172 if (count_failed != 0) {
5173 BKE_report(op->reports, RPT_ERROR, "Cannot spin");
5174 }
5175 return OPERATOR_CANCELLED;
5176 }
5177 return OPERATOR_FINISHED;
5178}
5179
5180static int spin_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
5181{
5182 Scene *scene = CTX_data_scene(C);
5184 float axis[3] = {0.0f, 0.0f, 1.0f};
5185
5186 if (rv3d) {
5187 copy_v3_v3(axis, rv3d->viewinv[2]);
5188 }
5189
5190 RNA_float_set_array(op->ptr, "center", scene->cursor.location);
5191 RNA_float_set_array(op->ptr, "axis", axis);
5192
5193 return spin_exec(C, op);
5194}
5195
5197{
5198 /* identifiers */
5199 ot->name = "Spin";
5200 ot->idname = "CURVE_OT_spin";
5201 ot->description = "Extrude selected boundary row around pivot point and current view axis";
5202
5203 /* api callbacks */
5204 ot->exec = spin_exec;
5207
5208 /* flags */
5210
5212 "center",
5213 3,
5214 nullptr,
5217 "Center",
5218 "Center in global view space",
5219 -1000.0f,
5220 1000.0f);
5222 ot->srna, "axis", 3, nullptr, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
5223}
5224
5227/* -------------------------------------------------------------------- */
5231static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb, View3D *v3d)
5232{
5233 bool changed = false;
5234
5235 Nurb *cu_actnu;
5236 union {
5237 BezTriple *bezt;
5238 BPoint *bp;
5239 void *p;
5240 } cu_actvert;
5241
5242 if (BLI_listbase_is_empty(&editnurb->nurbs)) {
5243 return changed;
5244 }
5245
5246 BKE_curve_nurb_vert_active_get(cu, &cu_actnu, &cu_actvert.p);
5247 int act_offset = 0;
5248
5249 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
5250 BLI_assert(nu->pntsu > 0);
5251 int i;
5252 int pnt_len = nu->pntsu;
5253 int new_points = 0;
5254 int offset = 0;
5255 bool is_prev_selected = false;
5256 bool duplic_first = false;
5257 bool duplic_last = false;
5258 if (nu->type == CU_BEZIER) {
5259 BezTriple *bezt, *bezt_prev = nullptr;
5260 BezTriple bezt_stack;
5261 bool is_cyclic = false;
5262 if (pnt_len == 1) {
5263 /* Single point extrusion.
5264 * Keep `is_prev_selected` false to force extrude. */
5265 bezt_prev = &nu->bezt[0];
5266 }
5267 else if (nu->flagu & CU_NURB_CYCLIC) {
5268 is_cyclic = true;
5269 bezt_prev = &nu->bezt[pnt_len - 1];
5270 is_prev_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt_prev);
5271 }
5272 else {
5273 duplic_first = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[0]) &&
5274 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[1]);
5275
5276 duplic_last = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[pnt_len - 2]) &&
5277 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[pnt_len - 1]);
5278
5279 if (duplic_first) {
5280 bezt_stack = nu->bezt[0];
5281 BEZT_DESEL_ALL(&bezt_stack);
5282 bezt_prev = &bezt_stack;
5283 }
5284 if (duplic_last) {
5285 new_points++;
5286 }
5287 }
5288 i = pnt_len;
5289 for (bezt = &nu->bezt[0]; i--; bezt++) {
5290 bool is_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
5291 if (bezt_prev && is_prev_selected != is_selected) {
5292 new_points++;
5293 }
5294 if (bezt == cu_actvert.bezt) {
5295 act_offset = new_points;
5296 }
5297 bezt_prev = bezt;
5298 is_prev_selected = is_selected;
5299 }
5300
5301 if (new_points) {
5302 if (pnt_len == 1) {
5303 /* Single point extrusion.
5304 * Set `is_prev_selected` as false to force extrude. */
5305 BLI_assert(bezt_prev == &nu->bezt[0]);
5306 is_prev_selected = false;
5307 }
5308 else if (is_cyclic) {
5309 BLI_assert(bezt_prev == &nu->bezt[pnt_len - 1]);
5310 BLI_assert(is_prev_selected == BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt_prev));
5311 }
5312 else if (duplic_first) {
5313 bezt_prev = &bezt_stack;
5314 is_prev_selected = false;
5315 }
5316 else {
5317 bezt_prev = nullptr;
5318 }
5319 BezTriple *bezt_src, *bezt_dst, *bezt_src_iter, *bezt_dst_iter;
5320 const int new_len = pnt_len + new_points;
5321
5322 bezt_src = nu->bezt;
5323 bezt_dst = static_cast<BezTriple *>(MEM_mallocN(new_len * sizeof(BezTriple), __func__));
5324 bezt_src_iter = &bezt_src[0];
5325 bezt_dst_iter = &bezt_dst[0];
5326 i = 0;
5327 for (bezt = &nu->bezt[0]; i < pnt_len; i++, bezt++) {
5328 bool is_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
5329 /* While this gets de-selected, selecting here ensures newly created verts are selected.
5330 * without this, the vertices are copied but only the handles are transformed.
5331 * which seems buggy from a user perspective. */
5332 if (is_selected) {
5333 bezt->f2 |= SELECT;
5334 }
5335 if (bezt_prev && is_prev_selected != is_selected) {
5336 int count = i - offset + 1;
5337 if (is_prev_selected) {
5338 ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, count - 1);
5339 ED_curve_beztcpy(editnurb, &bezt_dst_iter[count - 1], bezt_prev, 1);
5340 }
5341 else {
5342 ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, count);
5343 }
5344 ED_curve_beztcpy(editnurb, &bezt_dst_iter[count], bezt, 1);
5345 BEZT_DESEL_ALL(&bezt_dst_iter[count - 1]);
5346
5347 bezt_dst_iter += count + 1;
5348 bezt_src_iter += count;
5349 offset = i + 1;
5350 }
5351 bezt_prev = bezt;
5352 is_prev_selected = is_selected;
5353 }
5354
5355 int remain = pnt_len - offset;
5356 if (remain) {
5357 ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, remain);
5358 }
5359
5360 if (duplic_last) {
5361 ED_curve_beztcpy(editnurb, &bezt_dst[new_len - 1], &bezt_src[pnt_len - 1], 1);
5362 BEZT_DESEL_ALL(&bezt_dst[new_len - 1]);
5363 }
5364
5365 MEM_freeN(nu->bezt);
5366 nu->bezt = bezt_dst;
5367 nu->pntsu += new_points;
5368 changed = true;
5369 }
5370 }
5371 else {
5372 BPoint *bp, *bp_prev = nullptr;
5373 BPoint bp_stack;
5374 if (pnt_len == 1) {
5375 /* Single point extrusion.
5376 * Reference a `prev_bp` to force extrude. */
5377 bp_prev = &nu->bp[0];
5378 }
5379 else {
5380 duplic_first = (nu->bp[0].f1 & SELECT) && (nu->bp[1].f1 & SELECT);
5381 duplic_last = (nu->bp[pnt_len - 2].f1 & SELECT) && (nu->bp[pnt_len - 1].f1 & SELECT);
5382 if (duplic_first) {
5383 bp_stack = nu->bp[0];
5384 bp_stack.f1 &= ~SELECT;
5385 bp_prev = &bp_stack;
5386 }
5387 if (duplic_last) {
5388 new_points++;
5389 }
5390 }
5391
5392 i = pnt_len;
5393 for (bp = &nu->bp[0]; i--; bp++) {
5394 bool is_selected = (bp->f1 & SELECT) != 0;
5395 if (bp_prev && is_prev_selected != is_selected) {
5396 new_points++;
5397 }
5398 if (bp == cu_actvert.bp) {
5399 act_offset = new_points;
5400 }
5401 bp_prev = bp;
5402 is_prev_selected = is_selected;
5403 }
5404
5405 if (new_points) {
5406 BPoint *bp_src, *bp_dst, *bp_src_iter, *bp_dst_iter;
5407 const int new_len = pnt_len + new_points;
5408
5409 is_prev_selected = false;
5410 if (pnt_len == 1) {
5411 /* Single point extrusion.
5412 * Keep `is_prev_selected` false to force extrude. */
5413 BLI_assert(bp_prev == &nu->bp[0]);
5414 }
5415 else if (duplic_first) {
5416 bp_prev = &bp_stack;
5417 is_prev_selected = false;
5418 }
5419 else {
5420 bp_prev = nullptr;
5421 }
5422 bp_src = nu->bp;
5423 bp_dst = static_cast<BPoint *>(MEM_mallocN(new_len * sizeof(BPoint), __func__));
5424 bp_src_iter = &bp_src[0];
5425 bp_dst_iter = &bp_dst[0];
5426 i = 0;
5427 for (bp = &nu->bp[0]; i < pnt_len; i++, bp++) {
5428 bool is_selected = (bp->f1 & SELECT) != 0;
5429 if (bp_prev && is_prev_selected != is_selected) {
5430 int count = i - offset + 1;
5431 if (is_prev_selected) {
5432 ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, count - 1);
5433 ED_curve_bpcpy(editnurb, &bp_dst_iter[count - 1], bp_prev, 1);
5434 }
5435 else {
5436 ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, count);
5437 }
5438 ED_curve_bpcpy(editnurb, &bp_dst_iter[count], bp, 1);
5439 bp_dst_iter[count - 1].f1 &= ~SELECT;
5440
5441 bp_dst_iter += count + 1;
5442 bp_src_iter += count;
5443 offset = i + 1;
5444 }
5445 bp_prev = bp;
5446 is_prev_selected = is_selected;
5447 }
5448
5449 int remain = pnt_len - offset;
5450 if (remain) {
5451 ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, remain);
5452 }
5453
5454 if (duplic_last) {
5455 ED_curve_bpcpy(editnurb, &bp_dst[new_len - 1], &bp_src[pnt_len - 1], 1);
5456 bp_dst[new_len - 1].f1 &= ~SELECT;
5457 }
5458
5459 MEM_freeN(nu->bp);
5460 nu->bp = bp_dst;
5461 nu->pntsu += new_points;
5462
5464 changed = true;
5465 }
5466 }
5467 }
5468
5469 cu->actvert += act_offset;
5470
5471 return changed;
5472}
5473
5476/* -------------------------------------------------------------------- */
5480int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, View3D *v3d, const float location_init[3])
5481{
5482 float center[3];
5483 float temp[3];
5484 uint verts_len;
5485 bool changed = false;
5486
5487 zero_v3(center);
5488 verts_len = 0;
5489
5490 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
5491 int i;
5492 if (nu->type == CU_BEZIER) {
5493 BezTriple *bezt;
5494
5495 for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) {
5496 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
5497 add_v3_v3(center, bezt->vec[1]);
5498 verts_len += 1;
5499 }
5500 }
5501 }
5502 else {
5503 BPoint *bp;
5504
5505 for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) {
5506 if (bp->f1 & SELECT) {
5507 add_v3_v3(center, bp->vec);
5508 verts_len += 1;
5509 }
5510 }
5511 }
5512 }
5513
5514 if (verts_len && ed_editcurve_extrude(cu, editnurb, v3d)) {
5515 float ofs[3];
5516 int i;
5517
5518 mul_v3_fl(center, 1.0f / float(verts_len));
5519 sub_v3_v3v3(ofs, location_init, center);
5520
5521 if (CU_IS_2D(cu)) {
5522 ofs[2] = 0.0f;
5523 }
5524
5525 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
5526 if (nu->type == CU_BEZIER) {
5527 BezTriple *bezt;
5528 for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) {
5529 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
5530 add_v3_v3(bezt->vec[0], ofs);
5531 add_v3_v3(bezt->vec[1], ofs);
5532 add_v3_v3(bezt->vec[2], ofs);
5533
5534 if (((nu->flagu & CU_NURB_CYCLIC) == 0) && ELEM(i, 0, nu->pntsu - 1)) {
5536 }
5537 }
5538 }
5539
5541 }
5542 else {
5543 BPoint *bp;
5544
5545 for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) {
5546 if (bp->f1 & SELECT) {
5547 add_v3_v3(bp->vec, ofs);
5548 }
5549 }
5550 }
5551 }
5552 changed = true;
5553 }
5554 else {
5555 float location[3];
5556
5557 copy_v3_v3(location, location_init);
5558
5559 if (CU_IS_2D(cu)) {
5560 location[2] = 0.0f;
5561 }
5562
5563 /* nothing selected: create a new curve */
5565
5566 Nurb *nurb_new;
5567 if (!nu) {
5568 /* Bezier as default. */
5569 nurb_new = static_cast<Nurb *>(
5570 MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2"));
5571 nurb_new->type = CU_BEZIER;
5572 nurb_new->resolu = cu->resolu;
5573 nurb_new->orderu = 4;
5574 nurb_new->flag |= CU_SMOOTH;
5575 BKE_nurb_bezierPoints_add(nurb_new, 1);
5576 }
5577 else {
5578 /* Copy the active nurb settings. */
5579 nurb_new = BKE_nurb_copy(nu, 1, 1);
5580 if (nu->bezt) {
5581 memcpy(nurb_new->bezt, nu->bezt, sizeof(BezTriple));
5582 }
5583 else {
5584 memcpy(nurb_new->bp, nu->bp, sizeof(BPoint));
5585 }
5586 }
5587
5588 if (nurb_new->type == CU_BEZIER) {
5589 BezTriple *bezt_new = nurb_new->bezt;
5590
5591 BEZT_SEL_ALL(bezt_new);
5592
5593 bezt_new->h1 = HD_AUTO;
5594 bezt_new->h2 = HD_AUTO;
5595
5596 temp[0] = 1.0f;
5597 temp[1] = 0.0f;
5598 temp[2] = 0.0f;
5599
5600 copy_v3_v3(bezt_new->vec[1], location);
5601 sub_v3_v3v3(bezt_new->vec[0], location, temp);
5602 add_v3_v3v3(bezt_new->vec[2], location, temp);
5603 }
5604 else {
5605 BPoint *bp_new = nurb_new->bp;
5606
5607 bp_new->f1 |= SELECT;
5608
5609 copy_v3_v3(bp_new->vec, location);
5610
5611 BKE_nurb_knot_calc_u(nurb_new);
5612 }
5613
5614 BLI_addtail(&editnurb->nurbs, nurb_new);
5615 changed = true;
5616 }
5617
5618 return changed;
5619}
5620
5622{
5623 Main *bmain = CTX_data_main(C);
5624 Object *obedit = CTX_data_edit_object(C);
5625 View3D *v3d = CTX_wm_view3d(C);
5626 Curve *cu = static_cast<Curve *>(obedit->data);
5627 EditNurb *editnurb = cu->editnurb;
5628 float location[3];
5629 float imat[4][4];
5630
5631 RNA_float_get_array(op->ptr, "location", location);
5632
5633 invert_m4_m4(imat, obedit->object_to_world().ptr());
5634 mul_m4_v3(imat, location);
5635
5636 if (ed_editcurve_addvert(cu, editnurb, v3d, location)) {
5637 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
5639 }
5640
5643
5644 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
5645
5646 return OPERATOR_FINISHED;
5647 }
5648 return OPERATOR_CANCELLED;
5649}
5650
5651static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
5652{
5655
5656 if (vc.rv3d && !RNA_struct_property_is_set(op->ptr, "location")) {
5657 Curve *cu;
5658 float location[3];
5659 const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
5662
5663 Nurb *nu;
5664 BezTriple *bezt;
5665 BPoint *bp;
5666
5667 cu = static_cast<Curve *>(vc.obedit->data);
5668
5669 ED_curve_nurb_vert_selected_find(cu, vc.v3d, &nu, &bezt, &bp);
5670
5671 if (bezt) {
5672 mul_v3_m4v3(location, vc.obedit->object_to_world().ptr(), bezt->vec[1]);
5673 }
5674 else if (bp) {
5675 mul_v3_m4v3(location, vc.obedit->object_to_world().ptr(), bp->vec);
5676 }
5677 else {
5678 copy_v3_v3(location, vc.scene->cursor.location);
5679 }
5680
5681 ED_view3d_win_to_3d_int(vc.v3d, vc.region, location, event->mval, location);
5682
5683 if (use_proj) {
5684 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
5685
5687
5689 params.snap_target_select = (vc.obedit != nullptr) ? SCE_SNAP_TARGET_NOT_ACTIVE :
5691 params.edit_mode_type = SNAP_GEOM_FINAL;
5693 vc.depsgraph,
5694 vc.region,
5695 vc.v3d,
5697 &params,
5698 nullptr,
5699 mval,
5700 nullptr,
5701 nullptr,
5702 location,
5703 nullptr);
5704
5706 }
5707
5708 if (CU_IS_2D(cu)) {
5709 const float eps = 1e-6f;
5710
5711 /* get the view vector to 'location' */
5712 float view_dir[3];
5713 ED_view3d_global_to_vector(vc.rv3d, location, view_dir);
5714
5715 /* get the plane */
5716 const float *plane_co = vc.obedit->object_to_world().location();
5717 float plane_no[3];
5718 /* only normalize to avoid precision errors */
5719 normalize_v3_v3(plane_no, vc.obedit->object_to_world()[2]);
5720
5721 if (fabsf(dot_v3v3(view_dir, plane_no)) < eps) {
5722 /* can't project on an aligned plane. */
5723 }
5724 else {
5725 float lambda;
5726 if (isect_ray_plane_v3_factor(location, view_dir, plane_co, plane_no, &lambda)) {
5727 /* check if we're behind the viewport */
5728 float location_test[3];
5729 madd_v3_v3v3fl(location_test, location, view_dir, lambda);
5730 if ((vc.rv3d->is_persp == false) ||
5731 (mul_project_m4_v3_zfac(vc.rv3d->persmat, location_test) > 0.0f))
5732 {
5733 copy_v3_v3(location, location_test);
5734 }
5735 }
5736 }
5737 }
5738
5739 RNA_float_set_array(op->ptr, "location", location);
5740 }
5741
5742 /* Support dragging to move after extrude, see: #114282. */
5743 int retval = add_vertex_exec(C, op);
5744 if (retval & OPERATOR_FINISHED) {
5745 retval |= OPERATOR_PASS_THROUGH;
5746 }
5747 return WM_operator_flag_only_pass_through_on_press(retval, event);
5748}
5749
5751{
5752 /* identifiers */
5753 ot->name = "Extrude to Cursor or Add";
5754 ot->idname = "CURVE_OT_vertex_add";
5755 ot->description = "Add a new control point (linked to only selected end-curve one, if any)";
5756
5757 /* api callbacks */
5761
5762 /* flags */
5764
5765 /* properties */
5767 "location",
5768 3,
5769 nullptr,
5772 "Location",
5773 "Location to add new vertex at",
5774 -1.0e4f,
5775 1.0e4f);
5776}
5777
5780/* -------------------------------------------------------------------- */
5785{
5786 Main *bmain = CTX_data_main(C);
5787 const Scene *scene = CTX_data_scene(C);
5788 ViewLayer *view_layer = CTX_data_view_layer(C);
5789 View3D *v3d = CTX_wm_view3d(C);
5790
5792 scene, view_layer, CTX_wm_view3d(C));
5793 for (Object *obedit : objects) {
5794 Curve *cu = static_cast<Curve *>(obedit->data);
5795 EditNurb *editnurb = cu->editnurb;
5796 bool changed = false;
5797
5798 if (!ED_curve_select_check(v3d, cu->editnurb)) {
5799 continue;
5800 }
5801
5802 if (obedit->type == OB_CURVES_LEGACY) {
5803 changed = ed_editcurve_extrude(cu, editnurb, v3d);
5804 }
5805 else {
5806 changed = ed_editnurb_extrude_flag(editnurb, SELECT);
5807 }
5808
5809 if (changed) {
5810 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
5812 }
5813
5815 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
5816 }
5817 }
5818 return OPERATOR_FINISHED;
5819}
5820
5822{
5823 /* identifiers */
5824 ot->name = "Extrude";
5825 ot->description = "Extrude selected control point(s)";
5826 ot->idname = "CURVE_OT_extrude";
5827
5828 /* api callbacks */
5831
5832 /* flags */
5834
5835 /* to give to transform */
5837}
5838
5841/* -------------------------------------------------------------------- */
5845bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
5846{
5847 BezTriple *bezt;
5848 BPoint *bp;
5849 int a;
5850 bool changed = false;
5851
5852 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
5853 if (nu->pntsu > 1 || nu->pntsv > 1) {
5854 if (nu->type == CU_POLY) {
5855 a = nu->pntsu;
5856 bp = nu->bp;
5857 while (a--) {
5858 if (bp->f1 & SELECT) {
5859 nu->flagu ^= CU_NURB_CYCLIC;
5860 changed = true;
5861 break;
5862 }
5863 bp++;
5864 }
5865 }
5866 else if (nu->type == CU_BEZIER) {
5867 a = nu->pntsu;
5868 bezt = nu->bezt;
5869 while (a--) {
5870 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
5871 nu->flagu ^= CU_NURB_CYCLIC;
5872 changed = true;
5873 break;
5874 }
5875 bezt++;
5876 }
5878 }
5879 else if (nu->pntsv == 1 && nu->type == CU_NURBS) {
5880 if (nu->knotsu) { /* if check_valid_nurb_u fails the knotsu can be nullptr */
5881 a = nu->pntsu;
5882 bp = nu->bp;
5883 while (a--) {
5884 if (bp->f1 & SELECT) {
5885 nu->flagu ^= CU_NURB_CYCLIC;
5886 /* 1==u type is ignored for cyclic curves */
5888 changed = true;
5889 break;
5890 }
5891 bp++;
5892 }
5893 }
5894 }
5895 else if (nu->type == CU_NURBS) {
5896 a = nu->pntsu * nu->pntsv;
5897 bp = nu->bp;
5898 while (a--) {
5899
5900 if (bp->f1 & SELECT) {
5901 if (direction == 0 && nu->pntsu > 1) {
5902 nu->flagu ^= CU_NURB_CYCLIC;
5903 /* 1==u type is ignored for cyclic curves */
5905 changed = true;
5906 }
5907 if (direction == 1 && nu->pntsv > 1) {
5908 nu->flagv ^= CU_NURB_CYCLIC;
5909 /* 2==v type is ignored for cyclic curves */
5911 changed = true;
5912 }
5913 break;
5914 }
5915 bp++;
5916 }
5917 }
5918 }
5919 }
5920 return changed;
5921}
5922
5924{
5925 const int direction = RNA_enum_get(op->ptr, "direction");
5926 View3D *v3d = CTX_wm_view3d(C);
5927 const Scene *scene = CTX_data_scene(C);
5928 ViewLayer *view_layer = CTX_data_view_layer(C);
5929 bool changed_multi = false;
5930
5932 scene, view_layer, CTX_wm_view3d(C));
5933 for (Object *obedit : objects) {
5934 Curve *cu = static_cast<Curve *>(obedit->data);
5935
5936 if (!ED_curve_select_check(v3d, cu->editnurb)) {
5937 continue;
5938 }
5939
5940 ListBase *editnurb = object_editcurve_get(obedit);
5941 if (curve_toggle_cyclic(v3d, editnurb, direction)) {
5942 changed_multi = true;
5944 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
5945 }
5946 }
5947
5948 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
5949}
5950
5951static int toggle_cyclic_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
5952{
5953 Object *obedit = CTX_data_edit_object(C);
5954 ListBase *editnurb = object_editcurve_get(obedit);
5955 uiPopupMenu *pup;
5956 uiLayout *layout;
5957
5958 if (obedit->type == OB_SURF) {
5959 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
5960 if (nu->pntsu > 1 || nu->pntsv > 1) {
5961 if (nu->type == CU_NURBS) {
5962 pup = UI_popup_menu_begin(C, IFACE_("Direction"), ICON_NONE);
5963 layout = UI_popup_menu_layout(pup);
5964 uiItemsEnumO(layout, op->type->idname, "direction");
5965 UI_popup_menu_end(C, pup);
5966 return OPERATOR_INTERFACE;
5967 }
5968 }
5969 }
5970 }
5971
5972 return toggle_cyclic_exec(C, op);
5973}
5974
5976{
5977 static const EnumPropertyItem direction_items[] = {
5978 {0, "CYCLIC_U", 0, "Cyclic U", ""},
5979 {1, "CYCLIC_V", 0, "Cyclic V", ""},
5980 {0, nullptr, 0, nullptr, nullptr},
5981 };
5982
5983 /* identifiers */
5984 ot->name = "Toggle Cyclic";
5985 ot->description = "Make active spline closed/opened loop";
5986 ot->idname = "CURVE_OT_cyclic_toggle";
5987
5988 /* api callbacks */
5992
5993 /* flags */
5995
5996 /* properties */
5998 "direction",
5999 direction_items,
6000 0,
6001 "Direction",
6002 "Direction to make surface cyclic in");
6003}
6004
6007/* -------------------------------------------------------------------- */
6012{
6013 const Scene *scene = CTX_data_scene(C);
6014 ViewLayer *view_layer = CTX_data_view_layer(C);
6015 View3D *v3d = CTX_wm_view3d(C);
6016
6017 bool changed = false;
6018 int count_failed = 0;
6019
6021 scene, view_layer, CTX_wm_view3d(C));
6022 for (Object *obedit : objects) {
6023 Curve *cu = static_cast<Curve *>(obedit->data);
6024
6025 if (!ED_curve_select_check(v3d, cu->editnurb)) {
6026 continue;
6027 }
6028
6029 ListBase newnurb = {nullptr, nullptr};
6030 adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, false);
6031
6032 if (BLI_listbase_is_empty(&newnurb)) {
6033 count_failed += 1;
6034 continue;
6035 }
6036
6037 changed = true;
6038 BLI_movelisttolist(object_editcurve_get(obedit), &newnurb);
6041 }
6042
6043 if (changed == false) {
6044 if (count_failed != 0) {
6045 BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection");
6046 }
6047 return OPERATOR_CANCELLED;
6048 }
6049 return OPERATOR_FINISHED;
6050}
6051
6053{
6054 /* identifiers */
6055 ot->name = "Duplicate Curve";
6056 ot->description = "Duplicate selected control points";
6057 ot->idname = "CURVE_OT_duplicate";
6058
6059 /* api callbacks */
6062
6063 /* flags */
6065}
6066
6069/* -------------------------------------------------------------------- */
6073static bool curve_delete_vertices(Object *obedit, View3D *v3d)
6074{
6075 if (obedit->type == OB_SURF) {
6077 }
6078 else {
6079 ed_curve_delete_selected(obedit, v3d);
6080 }
6081
6082 return true;
6083}
6084
6085static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split)
6086{
6087 Curve *cu = static_cast<Curve *>(obedit->data);
6088 EditNurb *editnurb = cu->editnurb;
6089 ListBase *nubase = &editnurb->nurbs, newnurb = {nullptr, nullptr};
6090 Nurb *nu1;
6091 BezTriple *bezt, *bezt1, *bezt2;
6092 BPoint *bp, *bp1, *bp2;
6093 int a, b, starta, enda, cut, cyclicut;
6094
6095 LISTBASE_FOREACH (Nurb *, nu, nubase) {
6096 nu1 = nullptr;
6097 starta = enda = cut = -1;
6098 cyclicut = 0;
6099
6100 if (nu->type == CU_BEZIER) {
6101 for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
6102 if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
6103 enda = a;
6104 if (starta == -1) {
6105 starta = a;
6106 }
6107 if (a < nu->pntsu - 1) {
6108 continue;
6109 }
6110 }
6111 else if (a < nu->pntsu - 1 && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt + 1)) {
6112 /* if just single selected point then continue */
6113 continue;
6114 }
6115
6116 if (starta >= 0) {
6117 /* got selected segment, now check where and copy */
6118 if (starta <= 1 && a == nu->pntsu - 1) {
6119 /* copying all points in spline */
6120 if (starta == 1 && enda != a) {
6121 nu->flagu &= ~CU_NURB_CYCLIC;
6122 }
6123
6124 starta = 0;
6125 enda = a;
6126 cut = enda - starta + 1;
6127 nu1 = BKE_nurb_copy(nu, cut, 1);
6128 }
6129 else if (starta == 0) {
6130 /* if start of curve copy next end point */
6131 enda++;
6132 cut = enda - starta + 1;
6133 bezt1 = &nu->bezt[nu->pntsu - 1];
6134 bezt2 = &nu->bezt[nu->pntsu - 2];
6135
6136 if ((nu->flagu & CU_NURB_CYCLIC) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) &&
6137 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2))
6138 {
6139 /* check if need to join start of spline to end */
6140 nu1 = BKE_nurb_copy(nu, cut + 1, 1);
6141 ED_curve_beztcpy(editnurb, &nu1->bezt[1], nu->bezt, cut);
6142 starta = nu->pntsu - 1;
6143 cut = 1;
6144 }
6145 else {
6146 if (nu->flagu & CU_NURB_CYCLIC) {
6147 cyclicut = cut;
6148 }
6149 else {
6150 nu1 = BKE_nurb_copy(nu, cut, 1);
6151 }
6152 }
6153 }
6154 else if (enda == nu->pntsu - 1) {
6155 /* if end of curve copy previous start point */
6156 starta--;
6157 cut = enda - starta + 1;
6158 bezt1 = nu->bezt;
6159 bezt2 = &nu->bezt[1];
6160
6161 if ((nu->flagu & CU_NURB_CYCLIC) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) &&
6162 BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2))
6163 {
6164 /* check if need to join start of spline to end */
6165 nu1 = BKE_nurb_copy(nu, cut + 1, 1);
6166 ED_curve_beztcpy(editnurb, &nu1->bezt[cut], nu->bezt, 1);
6167 }
6168 else if (cyclicut != 0) {
6169 /* if cyclicut exists it is a cyclic spline, start and end should be connected */
6170 nu1 = BKE_nurb_copy(nu, cut + cyclicut, 1);
6171 ED_curve_beztcpy(editnurb, &nu1->bezt[cut], nu->bezt, cyclicut);
6172 cyclicut = 0;
6173 }
6174 else {
6175 nu1 = BKE_nurb_copy(nu, cut, 1);
6176 }
6177 }
6178 else {
6179 /* mid spline selection, copy adjacent start and end */
6180 starta--;
6181 enda++;
6182 cut = enda - starta + 1;
6183 nu1 = BKE_nurb_copy(nu, cut, 1);
6184 }
6185
6186 if (nu1 != nullptr) {
6187 ED_curve_beztcpy(editnurb, nu1->bezt, &nu->bezt[starta], cut);
6188 BLI_addtail(&newnurb, nu1);
6189
6190 if (starta != 0 || enda != nu->pntsu - 1) {
6191 nu1->flagu &= ~CU_NURB_CYCLIC;
6192 }
6193 nu1 = nullptr;
6194 }
6195 starta = enda = -1;
6196 }
6197 }
6198
6199 if (!split && cut != -1 && nu->pntsu > 2 && !(nu->flagu & CU_NURB_CYCLIC)) {
6200 /* start and points copied if connecting segment was deleted and not cyclic spline */
6201 bezt1 = nu->bezt;
6202 bezt2 = &nu->bezt[1];
6203
6204 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
6205 nu1 = BKE_nurb_copy(nu, 1, 1);
6206 ED_curve_beztcpy(editnurb, nu1->bezt, bezt1, 1);
6207 BLI_addtail(&newnurb, nu1);
6208 }
6209
6210 bezt1 = &nu->bezt[nu->pntsu - 1];
6211 bezt2 = &nu->bezt[nu->pntsu - 2];
6212
6213 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
6214 nu1 = BKE_nurb_copy(nu, 1, 1);
6215 ED_curve_beztcpy(editnurb, nu1->bezt, bezt1, 1);
6216 BLI_addtail(&newnurb, nu1);
6217 }
6218 }
6219 }
6220 else if (nu->pntsv >= 1) {
6221 int u, v;
6222
6223 if (isNurbselV(nu, &u, SELECT)) {
6224 for (a = 0, bp = nu->bp; a < nu->pntsu; a++, bp++) {
6225 if (!(bp->f1 & SELECT)) {
6226 enda = a;
6227 if (starta == -1) {
6228 starta = a;
6229 }
6230 if (a < nu->pntsu - 1) {
6231 continue;
6232 }
6233 }
6234 else if (a < nu->pntsu - 1 && !((bp + 1)->f1 & SELECT)) {
6235 /* if just single selected point then continue */
6236 continue;
6237 }
6238
6239 if (starta >= 0) {
6240 /* got selected segment, now check where and copy */
6241 if (starta <= 1 && a == nu->pntsu - 1) {
6242 /* copying all points in spline */
6243 if (starta == 1 && enda != a) {
6244 nu->flagu &= ~CU_NURB_CYCLIC;
6245 }
6246
6247 starta = 0;
6248 enda = a;
6249 cut = enda - starta + 1;
6250 nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
6251 }
6252 else if (starta == 0) {
6253 /* if start of curve copy next end point */
6254 enda++;
6255 cut = enda - starta + 1;
6256 bp1 = &nu->bp[nu->pntsu - 1];
6257 bp2 = &nu->bp[nu->pntsu - 2];
6258
6259 if ((nu->flagu & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6260 /* check if need to join start of spline to end */
6261 nu1 = BKE_nurb_copy(nu, cut + 1, nu->pntsv);
6262 for (b = 0; b < nu->pntsv; b++) {
6264 editnurb, &nu1->bp[b * nu1->pntsu + 1], &nu->bp[b * nu->pntsu], cut);
6265 }
6266 starta = nu->pntsu - 1;
6267 cut = 1;
6268 }
6269 else {
6270 if (nu->flagu & CU_NURB_CYCLIC) {
6271 cyclicut = cut;
6272 }
6273 else {
6274 nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
6275 }
6276 }
6277 }
6278 else if (enda == nu->pntsu - 1) {
6279 /* if end of curve copy previous start point */
6280 starta--;
6281 cut = enda - starta + 1;
6282 bp1 = nu->bp;
6283 bp2 = &nu->bp[1];
6284
6285 if ((nu->flagu & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6286 /* check if need to join start of spline to end */
6287 nu1 = BKE_nurb_copy(nu, cut + 1, nu->pntsv);
6288 for (b = 0; b < nu->pntsv; b++) {
6290 editnurb, &nu1->bp[b * nu1->pntsu + cut], &nu->bp[b * nu->pntsu], 1);
6291 }
6292 }
6293 else if (cyclicut != 0) {
6294 /* if cyclicut exists it is a cyclic spline, start and end should be connected */
6295 nu1 = BKE_nurb_copy(nu, cut + cyclicut, nu->pntsv);
6296 for (b = 0; b < nu->pntsv; b++) {
6298 editnurb, &nu1->bp[b * nu1->pntsu + cut], &nu->bp[b * nu->pntsu], cyclicut);
6299 }
6300 }
6301 else {
6302 nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
6303 }
6304 }
6305 else {
6306 /* mid spline selection, copy adjacent start and end */
6307 starta--;
6308 enda++;
6309 cut = enda - starta + 1;
6310 nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
6311 }
6312
6313 if (nu1 != nullptr) {
6314 for (b = 0; b < nu->pntsv; b++) {
6316 editnurb, &nu1->bp[b * nu1->pntsu], &nu->bp[b * nu->pntsu + starta], cut);
6317 }
6318 BLI_addtail(&newnurb, nu1);
6319
6320 if (starta != 0 || enda != nu->pntsu - 1) {
6321 nu1->flagu &= ~CU_NURB_CYCLIC;
6322 }
6323 nu1 = nullptr;
6324 }
6325 starta = enda = -1;
6326 }
6327 }
6328
6329 if (!split && cut != -1 && nu->pntsu > 2 && !(nu->flagu & CU_NURB_CYCLIC)) {
6330 /* start and points copied if connecting segment was deleted and not cyclic spline */
6331 bp1 = nu->bp;
6332 bp2 = &nu->bp[1];
6333
6334 if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6335 nu1 = BKE_nurb_copy(nu, 1, nu->pntsv);
6336 for (b = 0; b < nu->pntsv; b++) {
6337 ED_curve_bpcpy(editnurb, &nu1->bp[b], &nu->bp[b * nu->pntsu], 1);
6338 }
6339 BLI_addtail(&newnurb, nu1);
6340 }
6341
6342 bp1 = &nu->bp[nu->pntsu - 1];
6343 bp2 = &nu->bp[nu->pntsu - 2];
6344
6345 if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6346 nu1 = BKE_nurb_copy(nu, 1, nu->pntsv);
6347 for (b = 0; b < nu->pntsv; b++) {
6348 ED_curve_bpcpy(editnurb, &nu1->bp[b], &nu->bp[b * nu->pntsu + nu->pntsu - 1], 1);
6349 }
6350 BLI_addtail(&newnurb, nu1);
6351 }
6352 }
6353 }
6354 else if (isNurbselU(nu, &v, SELECT)) {
6355 for (a = 0, bp = nu->bp; a < nu->pntsv; a++, bp += nu->pntsu) {
6356 if (!(bp->f1 & SELECT)) {
6357 enda = a;
6358 if (starta == -1) {
6359 starta = a;
6360 }
6361 if (a < nu->pntsv - 1) {
6362 continue;
6363 }
6364 }
6365 else if (a < nu->pntsv - 1 && !((bp + nu->pntsu)->f1 & SELECT)) {
6366 /* if just single selected point then continue */
6367 continue;
6368 }
6369
6370 if (starta >= 0) {
6371 /* got selected segment, now check where and copy */
6372 if (starta <= 1 && a == nu->pntsv - 1) {
6373 /* copying all points in spline */
6374 if (starta == 1 && enda != a) {
6375 nu->flagv &= ~CU_NURB_CYCLIC;
6376 }
6377
6378 starta = 0;
6379 enda = a;
6380 cut = enda - starta + 1;
6381 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
6382 }
6383 else if (starta == 0) {
6384 /* if start of curve copy next end point */
6385 enda++;
6386 cut = enda - starta + 1;
6387 bp1 = &nu->bp[nu->pntsv * nu->pntsu - nu->pntsu];
6388 bp2 = &nu->bp[nu->pntsv * nu->pntsu - (nu->pntsu * 2)];
6389
6390 if ((nu->flagv & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6391 /* check if need to join start of spline to end */
6392 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + 1);
6393 ED_curve_bpcpy(editnurb, &nu1->bp[nu->pntsu], nu->bp, cut * nu->pntsu);
6394 starta = nu->pntsv - 1;
6395 cut = 1;
6396 }
6397 else {
6398 if (nu->flagv & CU_NURB_CYCLIC) {
6399 cyclicut = cut;
6400 }
6401 else {
6402 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
6403 }
6404 }
6405 }
6406 else if (enda == nu->pntsv - 1) {
6407 /* if end of curve copy previous start point */
6408 starta--;
6409 cut = enda - starta + 1;
6410 bp1 = nu->bp;
6411 bp2 = &nu->bp[nu->pntsu];
6412
6413 if ((nu->flagv & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6414 /* check if need to join start of spline to end */
6415 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + 1);
6416 ED_curve_bpcpy(editnurb, &nu1->bp[cut * nu->pntsu], nu->bp, nu->pntsu);
6417 }
6418 else if (cyclicut != 0) {
6419 /* if cyclicut exists it is a cyclic spline, start and end should be connected */
6420 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + cyclicut);
6421 ED_curve_bpcpy(editnurb, &nu1->bp[cut * nu->pntsu], nu->bp, nu->pntsu * cyclicut);
6422 cyclicut = 0;
6423 }
6424 else {
6425 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
6426 }
6427 }
6428 else {
6429 /* mid spline selection, copy adjacent start and end */
6430 starta--;
6431 enda++;
6432 cut = enda - starta + 1;
6433 nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
6434 }
6435
6436 if (nu1 != nullptr) {
6437 ED_curve_bpcpy(editnurb, nu1->bp, &nu->bp[starta * nu->pntsu], cut * nu->pntsu);
6438 BLI_addtail(&newnurb, nu1);
6439
6440 if (starta != 0 || enda != nu->pntsv - 1) {
6441 nu1->flagv &= ~CU_NURB_CYCLIC;
6442 }
6443 nu1 = nullptr;
6444 }
6445 starta = enda = -1;
6446 }
6447 }
6448
6449 if (!split && cut != -1 && nu->pntsv > 2 && !(nu->flagv & CU_NURB_CYCLIC)) {
6450 /* start and points copied if connecting segment was deleted and not cyclic spline */
6451 bp1 = nu->bp;
6452 bp2 = &nu->bp[nu->pntsu];
6453
6454 if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6455 nu1 = BKE_nurb_copy(nu, nu->pntsu, 1);
6456 ED_curve_bpcpy(editnurb, nu1->bp, nu->bp, nu->pntsu);
6457 BLI_addtail(&newnurb, nu1);
6458 }
6459
6460 bp1 = &nu->bp[nu->pntsu * nu->pntsv - nu->pntsu];
6461 bp2 = &nu->bp[nu->pntsu * nu->pntsv - (nu->pntsu * 2)];
6462
6463 if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
6464 nu1 = BKE_nurb_copy(nu, nu->pntsu, 1);
6466 editnurb, nu1->bp, &nu->bp[nu->pntsu * nu->pntsv - nu->pntsu], nu->pntsu);
6467 BLI_addtail(&newnurb, nu1);
6468 }
6469 }
6470 }
6471 else {
6472 /* selection not valid, just copy nurb to new list */
6473 nu1 = BKE_nurb_copy(nu, nu->pntsu, nu->pntsv);
6474 ED_curve_bpcpy(editnurb, nu1->bp, nu->bp, nu->pntsu * nu->pntsv);
6475 BLI_addtail(&newnurb, nu1);
6476 }
6477 }
6478 }
6479
6480 LISTBASE_FOREACH (Nurb *, nu, &newnurb) {
6481 if (nu->type == CU_BEZIER) {
6482 if (split) {
6483 /* deselect for split operator */
6484 for (b = 0, bezt1 = nu->bezt; b < nu->pntsu; b++, bezt1++) {
6485 select_beztriple(bezt1, false, SELECT, eVisible_Types(true));
6486 }
6487 }
6488
6490 }
6491 else {
6492 if (split) {
6493 /* deselect for split operator */
6494 for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
6495 select_bpoint(bp1, false, SELECT, HIDDEN);
6496 }
6497 }
6498
6501
6502 if (nu->pntsv > 1) {
6505 }
6506 }
6507 }
6508
6509 keyIndex_delNurbList(editnurb, nubase);
6510 BKE_nurbList_free(nubase);
6511 BLI_movelisttolist(nubase, &newnurb);
6512
6513 return true;
6514}
6515
6517{
6518 Main *bmain = CTX_data_main(C);
6519 View3D *v3d = CTX_wm_view3d(C);
6521 const Scene *scene = CTX_data_scene(C);
6522 ViewLayer *view_layer = CTX_data_view_layer(C);
6524 scene, view_layer, CTX_wm_view3d(C));
6525 bool changed_multi = false;
6526
6527 for (Object *obedit : objects) {
6528 Curve *cu = (Curve *)obedit->data;
6529 bool changed = false;
6530
6531 if (!ED_curve_select_check(v3d, cu->editnurb)) {
6532 continue;
6533 }
6534
6535 if (type == CURVE_VERTEX) {
6536 changed = curve_delete_vertices(obedit, v3d);
6537 }
6538 else if (type == CURVE_SEGMENT) {
6539 changed = curve_delete_segments(obedit, v3d, false);
6540 cu->actnu = CU_ACT_NONE;
6541 }
6542 else {
6543 BLI_assert(0);
6544 }
6545
6546 if (changed) {
6547 changed_multi = true;
6548 cu->actvert = CU_ACT_NONE;
6549
6550 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
6552 }
6553
6555 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
6556 }
6557 }
6558
6559 if (changed_multi) {
6560 return OPERATOR_FINISHED;
6561 }
6562 return OPERATOR_CANCELLED;
6563}
6564
6566 {CURVE_VERTEX, "VERT", 0, "Vertices", ""},
6567 {CURVE_SEGMENT, "SEGMENT", 0, "Segments", ""},
6568 {0, nullptr, 0, nullptr, nullptr},
6569};
6570
6572 PointerRNA * /*ptr*/,
6573 PropertyRNA * /*prop*/,
6574 bool *r_free)
6575{
6576 EnumPropertyItem *item = nullptr;
6577 int totitem = 0;
6578
6579 if (!C) { /* needed for docs and i18n tools */
6581 }
6582
6585 RNA_enum_item_end(&item, &totitem);
6586 *r_free = true;
6587
6588 return item;
6589}
6590
6592{
6593 PropertyRNA *prop;
6594
6595 /* identifiers */
6596 ot->name = "Delete";
6597 ot->description = "Delete selected control points or segments";
6598 ot->idname = "CURVE_OT_delete";
6599
6600 /* api callbacks */
6604
6605 /* flags */
6607
6608 /* properties */
6609 prop = RNA_def_enum(
6610 ot->srna, "type", curve_delete_type_items, 0, "Type", "Which elements to delete");
6613 ot->prop = prop;
6614}
6615
6618/* -------------------------------------------------------------------- */
6622static bool test_bezt_is_sel_any(const void *bezt_v, void *user_data)
6623{
6624 View3D *v3d = static_cast<View3D *>(user_data);
6625 const BezTriple *bezt = static_cast<const BezTriple *>(bezt_v);
6626 return BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
6627}
6628
6630 BezTriple *bezt_next,
6631 const Nurb *nu,
6632 const Curve *cu,
6633 const uint span_len,
6634 const uint span_step[2])
6635{
6636 int i_span_edge_len = span_len + 1;
6637 const int dims = 3;
6638
6639 const int points_len = ((cu->resolu - 1) * i_span_edge_len) + 1;
6640 float *points = static_cast<float *>(MEM_mallocN(points_len * dims * sizeof(float), __func__));
6641 float *points_stride = points;
6642 const int points_stride_len = (cu->resolu - 1);
6643
6644 for (int segment = 0; segment < i_span_edge_len; segment++) {
6645 BezTriple *bezt_a = &nu->bezt[mod_i((span_step[0] + segment) - 1, nu->pntsu)];
6646 BezTriple *bezt_b = &nu->bezt[mod_i((span_step[0] + segment), nu->pntsu)];
6647
6648 for (int axis = 0; axis < dims; axis++) {
6649 BKE_curve_forward_diff_bezier(bezt_a->vec[1][axis],
6650 bezt_a->vec[2][axis],
6651 bezt_b->vec[0][axis],
6652 bezt_b->vec[1][axis],
6653 points_stride + axis,
6654 points_stride_len,
6655 dims * sizeof(float));
6656 }
6657
6658 points_stride += dims * points_stride_len;
6659 }
6660
6661 BLI_assert(points_stride + dims == points + (points_len * dims));
6662
6663 float tan_l[3], tan_r[3], error_sq_dummy;
6664 uint error_index_dummy;
6665
6666 sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]);
6667 normalize_v3(tan_l);
6668 sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]);
6669 normalize_v3(tan_r);
6670
6671 curve_fit_cubic_to_points_single_fl(points,
6672 points_len,
6673 nullptr,
6674 dims,
6675 FLT_EPSILON,
6676 tan_l,
6677 tan_r,
6678 bezt_prev->vec[2],
6679 bezt_next->vec[0],
6680 &error_sq_dummy,
6681 &error_index_dummy);
6682
6683 if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) {
6684 bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN;
6685 }
6686 if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) {
6687 bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN;
6688 }
6689
6690 MEM_freeN(points);
6691}
6692
6694{
6695 Main *bmain = CTX_data_main(C);
6696 const Scene *scene = CTX_data_scene(C);
6697 ViewLayer *view_layer = CTX_data_view_layer(C);
6698 View3D *v3d = CTX_wm_view3d(C);
6699
6701 scene, view_layer, CTX_wm_view3d(C));
6702 for (Object *obedit : objects) {
6703 Curve *cu = (Curve *)obedit->data;
6704
6705 if (!ED_curve_select_check(v3d, cu->editnurb)) {
6706 continue;
6707 }
6708
6709 ListBase *editnurb = object_editcurve_get(obedit);
6710
6711 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
6712 if ((nu->type == CU_BEZIER) && (nu->pntsu > 2)) {
6713 uint span_step[2] = {uint(nu->pntsu), uint(nu->pntsu)};
6714 uint span_len;
6715
6716 while (BLI_array_iter_span(nu->bezt,
6717 nu->pntsu,
6718 (nu->flagu & CU_NURB_CYCLIC) != 0,
6719 false,
6721 v3d,
6722 span_step,
6723 &span_len))
6724 {
6725 BezTriple *bezt_prev = &nu->bezt[mod_i(span_step[0] - 1, nu->pntsu)];
6726 BezTriple *bezt_next = &nu->bezt[mod_i(span_step[1] + 1, nu->pntsu)];
6727
6728 ed_dissolve_bez_segment(bezt_prev, bezt_next, nu, cu, span_len, span_step);
6729 }
6730 }
6731 }
6732
6733 ed_curve_delete_selected(obedit, v3d);
6734
6735 cu->actnu = cu->actvert = CU_ACT_NONE;
6736
6737 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
6739 }
6740
6742 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
6743 }
6744 return OPERATOR_FINISHED;
6745}
6746
6748{
6749 /* identifiers */
6750 ot->name = "Dissolve Vertices";
6751 ot->description = "Delete selected control points, correcting surrounding handles";
6752 ot->idname = "CURVE_OT_dissolve_verts";
6753
6754 /* api callbacks */
6757
6758 /* flags */
6760}
6761
6764/* -------------------------------------------------------------------- */
6768static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test)
6769{
6770 const BezTriple *bezt;
6771 int i;
6772
6773 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
6774 if (bezt->f2 & flag_test) {
6775 return true;
6776 }
6777 }
6778
6779 return false;
6780}
6781
6783{
6784 Main *bmain = CTX_data_main(C);
6785 const float error_sq_max = FLT_MAX;
6786 float ratio = RNA_float_get(op->ptr, "ratio");
6787 bool all_supported_multi = true;
6788
6789 const Scene *scene = CTX_data_scene(C);
6790 ViewLayer *view_layer = CTX_data_view_layer(C);
6792 scene, view_layer, CTX_wm_view3d(C));
6793 for (Object *obedit : objects) {
6794 Curve *cu = (Curve *)obedit->data;
6795 bool all_supported = true;
6796 bool changed = false;
6797
6798 {
6799 ListBase *editnurb = object_editcurve_get(obedit);
6800
6801 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
6802 if (nu->type == CU_BEZIER) {
6803 if ((nu->pntsu > 2) && nurb_bezt_flag_any(nu, SELECT)) {
6804 const int error_target_len = max_ii(2, nu->pntsu * ratio);
6805 if (error_target_len != nu->pntsu) {
6806 BKE_curve_decimate_nurb(nu, cu->resolu, error_sq_max, error_target_len);
6807 changed = true;
6808 }
6809 }
6810 }
6811 else {
6812 all_supported = false;
6813 }
6814 }
6815 }
6816
6817 if (all_supported == false) {
6818 all_supported_multi = false;
6819 }
6820
6821 if (changed) {
6822 cu->actnu = cu->actvert = CU_ACT_NONE;
6823 if (ED_curve_updateAnimPaths(bmain, static_cast<Curve *>(obedit->data))) {
6825 }
6826
6828 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
6829 }
6830 }
6831
6832 if (all_supported_multi == false) {
6833 BKE_report(op->reports, RPT_WARNING, "Only Bézier curves are supported");
6834 }
6835
6836 return OPERATOR_FINISHED;
6837}
6838
6840{
6841 /* identifiers */
6842 ot->name = "Decimate Curve";
6843 ot->description = "Simplify selected curves";
6844 ot->idname = "CURVE_OT_decimate";
6845
6846 /* api callbacks */
6849
6850 /* flags */
6852
6853 /* properties */
6854 RNA_def_float_factor(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
6855}
6856
6859/* -------------------------------------------------------------------- */
6864{
6865 View3D *v3d = CTX_wm_view3d(C);
6866 const Scene *scene = CTX_data_scene(C);
6867 ViewLayer *view_layer = CTX_data_view_layer(C);
6868 int clear = STREQ(op->idname, "CURVE_OT_shade_flat");
6870 scene, view_layer, CTX_wm_view3d(C));
6871 int ret_value = OPERATOR_CANCELLED;
6872
6873 for (Object *obedit : objects) {
6874 ListBase *editnurb = object_editcurve_get(obedit);
6875
6876 if (obedit->type != OB_CURVES_LEGACY) {
6877 continue;
6878 }
6879
6880 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
6881 if (ED_curve_nurb_select_check(v3d, nu)) {
6882 if (!clear) {
6883 nu->flag |= CU_SMOOTH;
6884 }
6885 else {
6886 nu->flag &= ~CU_SMOOTH;
6887 }
6888 }
6889 }
6890
6892 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
6893 ret_value = OPERATOR_FINISHED;
6894 }
6895
6896 return ret_value;
6897}
6898
6900{
6901 /* identifiers */
6902 ot->name = "Shade Smooth";
6903 ot->idname = "CURVE_OT_shade_smooth";
6904 ot->description = "Set shading to smooth";
6905
6906 /* api callbacks */
6909
6910 /* flags */
6912}
6913
6915{
6916 /* identifiers */
6917 ot->name = "Shade Flat";
6918 ot->idname = "CURVE_OT_shade_flat";
6919 ot->description = "Set shading to flat";
6920
6921 /* api callbacks */
6924
6925 /* flags */
6927}
6928
6931/* -------------------------------------------------------------------- */
6936{
6937 Main *bmain = CTX_data_main(C);
6938 Scene *scene = CTX_data_scene(C);
6939 Object *ob_active = CTX_data_active_object(C);
6940 Curve *cu;
6941 BezTriple *bezt;
6942 BPoint *bp;
6943 ListBase tempbase;
6944 float imat[4][4], cmat[4][4];
6945 int a;
6946 bool ok = false;
6947
6948 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
6949 if (ob_iter == ob_active) {
6950 ok = true;
6951 break;
6952 }
6953 }
6955
6956 /* that way the active object is always selected */
6957 if (ok == false) {
6958 BKE_report(op->reports, RPT_WARNING, "Active object is not a selected curve");
6959 return OPERATOR_CANCELLED;
6960 }
6961
6962 BLI_listbase_clear(&tempbase);
6963
6964 /* Inverse transform for all selected curves in this object,
6965 * See object_join_exec for detailed comment on why the safe version is used. */
6966 invert_m4_m4_safe_ortho(imat, ob_active->object_to_world().ptr());
6967
6968 Curve *cu_active = static_cast<Curve *>(ob_active->data);
6969
6970 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
6971 if (ob_iter->type == ob_active->type) {
6972 if (ob_iter != ob_active) {
6973
6974 cu = static_cast<Curve *>(ob_iter->data);
6975
6976 if (cu->nurb.first) {
6977 /* watch it: switch order here really goes wrong */
6978 mul_m4_m4m4(cmat, imat, ob_iter->object_to_world().ptr());
6979
6980 /* Compensate for different bevel depth. */
6981 bool do_radius = false;
6982 float compensate_radius = 0.0f;
6983 if (cu->bevel_radius != 0.0f && cu_active->bevel_radius != 0.0f) {
6984 float compensate_scale = mat4_to_scale(cmat);
6985 compensate_radius = cu->bevel_radius / cu_active->bevel_radius * compensate_scale;
6986 do_radius = true;
6987 }
6988
6989 LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
6990 Nurb *newnu = BKE_nurb_duplicate(nu);
6991 if (ob_active->totcol) { /* TODO: merge material lists. */
6992 CLAMP(newnu->mat_nr, 0, ob_active->totcol - 1);
6993 }
6994 else {
6995 newnu->mat_nr = 0;
6996 }
6997 BLI_addtail(&tempbase, newnu);
6998
6999 if ((bezt = newnu->bezt)) {
7000 a = newnu->pntsu;
7001 while (a--) {
7002 /* Compensate for different bevel depth. */
7003 if (do_radius) {
7004 bezt->radius *= compensate_radius;
7005 }
7006
7007 mul_m4_v3(cmat, bezt->vec[0]);
7008 mul_m4_v3(cmat, bezt->vec[1]);
7009 mul_m4_v3(cmat, bezt->vec[2]);
7010 bezt++;
7011 }
7012 BKE_nurb_handles_calc(newnu);
7013 }
7014 if ((bp = newnu->bp)) {
7015 a = newnu->pntsu * nu->pntsv;
7016 while (a--) {
7017 mul_m4_v3(cmat, bp->vec);
7018 bp++;
7019 }
7020 }
7021 }
7022 }
7023
7024 blender::ed::object::base_free_and_unlink(bmain, scene, ob_iter);
7025 }
7026 }
7027 }
7029
7030 cu = static_cast<Curve *>(ob_active->data);
7031 BLI_movelisttolist(&cu->nurb, &tempbase);
7032
7033 if (ob_active->type == OB_CURVES_LEGACY && CU_IS_2D(cu)) {
7034 /* Account for mixed 2D/3D curves when joining */
7036 }
7037
7038 DEG_relations_tag_update(bmain); /* because we removed object(s), call before editmode! */
7039
7042
7045
7046 return OPERATOR_FINISHED;
7047}
7048
7051/* -------------------------------------------------------------------- */
7056{
7057 const Scene *scene = CTX_data_scene(C);
7058 ViewLayer *view_layer = CTX_data_view_layer(C);
7059 View3D *v3d = CTX_wm_view3d(C);
7060
7062 scene, view_layer, CTX_wm_view3d(C));
7063
7064 int totobjects = 0;
7065
7066 for (Object *obedit : objects) {
7067 Curve *cu = static_cast<Curve *>(obedit->data);
7068
7069 if (!ED_curve_select_check(v3d, cu->editnurb)) {
7070 continue;
7071 }
7072
7074 continue;
7075 }
7076
7077 totobjects++;
7078
7079 ListBase *editnurb = object_editcurve_get(obedit);
7080 BezTriple *bezt;
7081 BPoint *bp;
7082 int a;
7083
7084 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
7085 if (nu->bezt) {
7086 bezt = nu->bezt;
7087 a = nu->pntsu;
7088 while (a--) {
7089 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
7090 bezt->tilt = 0.0;
7091 }
7092 bezt++;
7093 }
7094 }
7095 else if (nu->bp) {
7096 bp = nu->bp;
7097 a = nu->pntsu * nu->pntsv;
7098 while (a--) {
7099 if (bp->f1 & SELECT) {
7100 bp->tilt = 0.0f;
7101 }
7102 bp++;
7103 }
7104 }
7105 }
7106
7108 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
7109 }
7110 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7111}
7112
7114{
7115 /* identifiers */
7116 ot->name = "Clear Tilt";
7117 ot->idname = "CURVE_OT_tilt_clear";
7118 ot->description = "Clear the tilt of selected control points";
7119
7120 /* api callbacks */
7123
7124 /* flags */
7126}
7127
7128void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
7129{
7130 memcpy(dst, src, count * sizeof(BezTriple));
7131 keyIndex_updateBezt(editnurb, src, dst, count);
7132}
7133
7134void ED_curve_bpcpy(EditNurb *editnurb, BPoint *dst, BPoint *src, int count)
7135{
7136 memcpy(dst, src, count * sizeof(BPoint));
7137 keyIndex_updateBP(editnurb, src, dst, count);
7138}
7139
7142/* -------------------------------------------------------------------- */
7147{
7148 Object *object = CTX_data_active_object(C);
7149
7150 return object && ELEM(object->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT);
7151}
7152
7154{
7155 /* Need to ensure the dependency graph is fully evaluated, so the display list is at a correct
7156 * state. */
7158 (void)depsgraph;
7159
7160 Object *object = CTX_data_active_object(C);
7161 Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
7162 Curve *curve = (Curve *)object->data;
7163 float min[3], max[3], texspace_size[3], texspace_location[3];
7164 int a;
7165
7166 BLI_assert(object_eval->runtime->curve_cache != nullptr);
7167
7168 INIT_MINMAX(min, max);
7169 BKE_displist_minmax(&object_eval->runtime->curve_cache->disp, min, max);
7170
7171 mid_v3_v3v3(texspace_location, min, max);
7172
7173 texspace_size[0] = (max[0] - min[0]) / 2.0f;
7174 texspace_size[1] = (max[1] - min[1]) / 2.0f;
7175 texspace_size[2] = (max[2] - min[2]) / 2.0f;
7176
7177 for (a = 0; a < 3; a++) {
7178 if (texspace_size[a] == 0.0f) {
7179 texspace_size[a] = 1.0f;
7180 }
7181 else if (texspace_size[a] > 0.0f && texspace_size[a] < 0.00001f) {
7182 texspace_size[a] = 0.00001f;
7183 }
7184 else if (texspace_size[a] < 0.0f && texspace_size[a] > -0.00001f) {
7185 texspace_size[a] = -0.00001f;
7186 }
7187 }
7188
7189 copy_v3_v3(curve->texspace_location, texspace_location);
7190 copy_v3_v3(curve->texspace_size, texspace_size);
7191
7192 curve->texspace_flag &= ~CU_TEXSPACE_FLAG_AUTO;
7193
7196
7197 return OPERATOR_FINISHED;
7198}
7199
7201{
7202 /* identifiers */
7203 ot->name = "Match Texture Space";
7204 ot->idname = "CURVE_OT_match_texture_space";
7205 ot->description = "Match texture space to object's bounding box";
7206
7207 /* api callbacks */
7210
7211 /* flags */
7213}
7214
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
Blender kernel action and pose functionality.
void BKE_action_groups_reconstruct(bAction *act)
void action_groups_remove_channel(bAction *act, FCurve *fcu)
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:89
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
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
bool BKE_nurb_type_convert(Nurb *nu, short type, bool use_handles, const char **r_err_msg)
Definition curve.cc:4821
void BKE_nurb_handle_calc_simple_auto(Nurb *nu, BezTriple *bezt)
Definition curve.cc:3994
void BKE_nurbList_handles_set(ListBase *editnurb, eNurbHandleTest_Mode handle_mode, char code)
Definition curve.cc:4191
void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt)
Definition curve.cc:3985
void BKE_nurb_handles_calc(Nurb *nu)
Definition curve.cc:3955
void BKE_curve_editNurb_free(Curve *cu)
Definition curve.cc:337
#define KNOTSU(nu)
Definition BKE_curve.hh:71
void BKE_nurb_project_2d(Nurb *nu)
Definition curve.cc:683
void BKE_curve_decimate_nurb(Nurb *nu, unsigned int resolu, float error_sq_max, unsigned int error_target_len)
#define CU_IS_2D(cu)
Definition BKE_curve.hh:86
void BKE_nurb_free(Nurb *nu)
Definition curve.cc:569
void BKE_curve_nurb_active_set(Curve *cu, const Nurb *nu)
Definition curve.cc:4983
Nurb * BKE_curve_nurb_active_get(Curve *cu)
Definition curve.cc:4995
#define KNOTSV(nu)
Definition BKE_curve.hh:73
void BKE_curve_editNurb_keyIndex_free(GHash **keyindex)
Definition curve.cc:328
void BKE_nurbList_handles_recalculate(ListBase *editnurb, bool calc_length, uint8_t flag)
Definition curve.cc:4282
void BKE_curve_nurb_vert_active_set(Curve *cu, const Nurb *nu, const void *vert)
Definition curve.cc:5021
void BKE_nurb_knot_calc_u(Nurb *nu)
Definition curve.cc:1181
void BKE_curve_dimension_update(Curve *cu)
Definition curve.cc:437
void * BKE_curve_vert_active_get(Curve *cu)
Definition curve.cc:5001
bool BKE_nurb_order_clamp_u(Nurb *nu)
Definition curve.cc:4801
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1663
void BKE_nurbList_free(ListBase *lb)
Definition curve.cc:596
void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, bool is_fcurve, char smoothing)
Definition curve.cc:3938
void BKE_nurb_knot_calc_v(Nurb *nu)
Definition curve.cc:1186
void BKE_nurb_direction_switch(Nurb *nu)
Definition curve.cc:4405
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5065
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5038
void BKE_nurb_bezierPoints_add(Nurb *nu, int number)
Definition curve.cc:875
BPoint * BKE_nurb_bpoint_get_next(Nurb *nu, BPoint *bp)
Definition curve.cc:941
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4339
BezTriple * BKE_nurb_bezt_get_next(Nurb *nu, BezTriple *bezt)
Definition curve.cc:920
Nurb * BKE_nurb_duplicate(const Nurb *nu)
Definition curve.cc:608
bool BKE_nurb_order_clamp_v(Nurb *nu)
Definition curve.cc:4811
void BKE_curve_editNurb_keyIndex_delCV(GHash *keyindex, const void *cv)
Definition curve.cc:322
Nurb * BKE_nurb_copy(Nurb *src, int pntsu, int pntsv)
Definition curve.cc:648
display list (or rather multi purpose list) stuff.
void BKE_displist_minmax(const struct ListBase *dispbase, float min[3], float max[3])
void BKE_fcurve_free(FCurve *fcu)
@ G_DEBUG
KeyBlock * BKE_keyblock_from_object(Object *ob)
Definition key.cc:1905
int BKE_keyblock_curve_element_count(const ListBase *nurb)
Definition key.cc:2029
KeyBlock * BKE_keyblock_find_by_index(Key *key, int index)
Definition key.cc:1923
void BKE_keyblock_convert_to_curve(KeyBlock *kb, Curve *cu, ListBase *nurb)
Definition key.cc:2163
std::optional< blender::Array< bool > > BKE_keyblock_get_dependent_keys(const Key *key, int index)
Definition key.cc:2578
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:765
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
Generic array manipulation API.
#define BLI_array_iter_span(arr, arr_len, use_wrap, use_delimit_bounds, test_fn, user_data, span_step, r_span_len)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:303
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:322
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_popkey(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:802
GHash * BLI_ghash_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:702
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define M_SQRT2
MINLINE int max_ii(int a, int b)
MINLINE int mod_i(int i, int n)
MINLINE float interpf(float target, float origin, float t)
#define M_PI_4
bool isect_ray_plane_v3_factor(const float ray_origin[3], const float ray_direction[3], const float plane_co[3], const float plane_no[3], float *r_lambda)
float mat4_to_scale(const float mat[4][4])
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void unit_m4(float m[4][4])
Definition rct.c:1127
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
void axis_angle_to_mat3(float R[3][3], const float axis[3], float angle)
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition math_vector.c:45
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], const float co[3]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void swap_v3_v3(float a[3], float b[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned int uint
#define CLAMP(a, b, c)
#define STRPREFIX(a, b)
#define INIT_MINMAX(min, max)
#define STREQLEN(a, b, n)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define IN_RANGE_INCL(a, b, c)
#define STREQ(a, b)
#define IFACE_(msgid)
int max_i(int a, int b)
Definition Basic.c:11
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define SURF_SEEN
@ CU_SMOOTH
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
@ CU_BEZIER
@ CU_POLY
@ CU_NURBS
#define BEZT_SEL_ALL(bezt)
struct BPoint BPoint
#define CU_ACT_NONE
#define BEZT_DESEL_ALL(bezt)
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
struct BezTriple BezTriple
@ CU_NURB_CYCLIC
@ CU_NURB_ENDPOINT
@ CU_NURB_BEZIER
@ KEY_RELATIVE
#define KEYELEM_FLOAT_LEN_BEZTRIPLE
#define KEYELEM_FLOAT_LEN_BPOINT
@ eModifierType_Hook
Object is a sort of wrapper for general info.
@ PARVERT1
@ PARVERT3
@ OB_SURF
@ OB_FONT
@ OB_CURVES_LEGACY
@ SCE_SNAP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_ALL
@ SCE_SNAP_TO_FACE
@ SCE_SNAP_INDIVIDUAL_PROJECT
eDupli_ID_Flags
@ USER_DUP_ACT
@ CURVE_HANDLE_NONE
@ OPERATOR_PASS_THROUGH
#define OBJECT_ADD_SIZE_MAXF
Definition ED_object.hh:295
void ED_outliner_select_sync_from_object_tag(bContext *C)
bool ED_operator_editsurfcurve(bContext *C)
bool ED_operator_editcurve(bContext *C)
bool ED_operator_editsurf(bContext *C)
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
@ TFM_TRANSLATION
SnapObjectContext * ED_transform_snap_object_context_create(Scene *scene, int flag)
void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx)
eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3])
void ED_view3d_global_to_vector(const RegionView3D *rv3d, const float coord[3], float r_out[3])
RegionView3D * ED_view3d_context_rv3d(bContext *C)
void ED_view3d_win_to_3d_int(const View3D *v3d, const ARegion *region, const float depth_pt[3], const int mval[2], float r_out[3])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void view3d_operator_needs_opengl(const bContext *C)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiItemsEnumO(uiLayout *layout, const char *opname, const char *propname)
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DRAW
Definition WM_types.hh:428
#define ND_OB_ACTIVE
Definition WM_types.hh:407
#define ND_DATA
Definition WM_types.hh:475
#define NC_SCENE
Definition WM_types.hh:345
#define ND_LAYER_CONTENT
Definition WM_types.hh:420
#define NC_MATERIAL
Definition WM_types.hh:347
#define ND_SELECT
Definition WM_types.hh:474
#define ND_KEYS
Definition WM_types.hh:430
#define NC_OBJECT
Definition WM_types.hh:346
#define ND_SHADING_LINKS
Definition WM_types.hh:446
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void append(const T &value)
local_group_size(16, 16) .push_constant(Type b
#define printf
#define SELECT
bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Types hidden)
void ED_curve_nurb_vert_selected_find(Curve *cu, View3D *v3d, Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp)
eEndPoint_Types
eCurveElem_Types
@ CURVE_VERTEX
@ CURVE_SEGMENT
bool ED_curve_pick_vert_ex(ViewContext *vc, bool select, int dist_px, Nurb **r_nurb, BezTriple **r_bezt, BPoint **r_bp, short *r_handle, Base **r_base)
eVisible_Types
@ HIDDEN
const Depsgraph * depsgraph
#define offsetof(t, d)
#define fabsf(x)
int len
#define BEZT_VALUE(bezt)
static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb, View3D *v3d)
static void keyIndex_updateBP(EditNurb *editnurb, BPoint *bp, BPoint *newbp, int count)
Definition editcurve.cc:349
static int hide_exec(bContext *C, wmOperator *op)
static bool isNurbselV(Nurb *nu, int *u, int flag)
static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split)
static void keyIndex_swap(EditNurb *editnurb, void *a, void *b)
Definition editcurve.cc:364
static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit)
void selectend_nurb(Object *obedit, enum eEndPoint_Types selfirst, bool doswap, bool selstatus)
static int * init_index_map(Object *obedit, int *r_old_totvert)
int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
static int add_vertex_exec(bContext *C, wmOperator *op)
static int smooth_exec(bContext *C, wmOperator *op)
static bool is_u_selected(Nurb *nu, int u)
static int shade_smooth_exec(bContext *C, wmOperator *op)
bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag)
void CURVE_OT_dissolve_verts(wmOperatorType *ot)
static CVKeyIndex * popCVKeyIndex(EditNurb *editnurb, const void *cv)
Definition editcurve.cc:231
static void weightflagNurb(ListBase *editnurb, short flag, float w)
void CURVE_OT_match_texture_space(wmOperatorType *ot)
void CURVE_OT_switch_direction(wmOperatorType *ot)
static const EnumPropertyItem * rna_curve_delete_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static bool test_bezt_is_sel_any(const void *bezt_v, void *user_data)
void CURVE_OT_spline_weight_set(wmOperatorType *ot)
static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu)
static bool calc_duplicate_actvert(const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert)
static int set_spline_type_exec(bContext *C, wmOperator *op)
static void keyIndex_updateBezt(EditNurb *editnurb, BezTriple *bezt, BezTriple *newbezt, int count)
Definition editcurve.cc:344
static void keyIndex_switchDirection(EditNurb *editnurb, Nurb *nu)
Definition editcurve.cc:377
void CURVE_OT_extrude(wmOperatorType *ot)
void CURVE_OT_make_segment(wmOperatorType *ot)
void ED_curve_keyindex_update_nurb(EditNurb *editnurb, Nurb *nu, Nurb *newnu)
Definition editcurve.cc:354
void CURVE_OT_hide(wmOperatorType *ot)
void CURVE_OT_normals_make_consistent(wmOperatorType *ot)
static void curve_smooth_value(ListBase *editnurb, const int bezt_offsetof, const int bp_offset)
static CVKeyIndex * init_cvKeyIndex(void *cv, int key_index, int nu_index, int pt_index, int vertex_index)
Definition editcurve.cc:142
static NurbDim editnurb_find_max_points_num(const EditNurb *editnurb)
void CURVE_OT_shade_smooth(wmOperatorType *ot)
void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3], bool is_2d)
static void subdividenurb(Object *obedit, View3D *v3d, int number_cuts)
static void calc_keyHandles(ListBase *nurb, float *key)
Definition editcurve.cc:580
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
static int switch_direction_exec(bContext *C, wmOperator *)
#define BP_VALUE(bp)
static void calc_shapeKeys(Object *obedit, ListBase *newnurbs)
Definition editcurve.cc:649
void CURVE_OT_delete(wmOperatorType *ot)
static void rotateflagNurb(ListBase *editnurb, short flag, const float cent[3], const float rotmat[3][3])
static int curve_smooth_tilt_exec(bContext *C, wmOperator *op)
void ED_curve_editnurb_make(Object *obedit)
static int curve_smooth_radius_exec(bContext *C, wmOperator *op)
static void bezt_to_key(BezTriple *bezt, float *key)
Definition editcurve.cc:573
@ CURVE_MERGE_ERR_RESOLUTION_ALL
@ CURVE_MERGE_OK
@ CURVE_MERGE_ERR_FEW_SELECTION
@ CURVE_MERGE_ERR_RESOLUTION_SOME
void CURVE_OT_spin(wmOperatorType *ot)
static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test)
static void make_selection_list_nurb(View3D *v3d, ListBase *editnurb, ListBase *nsortbase)
static int set_handle_type_exec(bContext *C, wmOperator *op)
static CVKeyIndex * getCVKeyIndex(EditNurb *editnurb, const void *cv)
Definition editcurve.cc:226
static int clear_tilt_exec(bContext *C, wmOperator *op)
void CURVE_OT_spline_type_set(wmOperatorType *ot)
static bool match_texture_space_poll(bContext *C)
void CURVE_OT_shade_flat(wmOperatorType *ot)
static int curve_decimate_exec(bContext *C, wmOperator *op)
static int curve_dissolve_exec(bContext *C, wmOperator *)
static void ed_curve_delete_selected(Object *obedit, View3D *v3d)
static void keyData_switchDirectionNurb(Curve *cu, Nurb *nu)
Definition editcurve.cc:528
void ED_curve_editnurb_free(Object *obedit)
static bool isNurbselU(Nurb *nu, int *v, int flag)
static int toggle_cyclic_invoke(bContext *C, wmOperator *op, const wmEvent *)
void CURVE_OT_vertex_add(wmOperatorType *ot)
static int reveal_exec(bContext *C, wmOperator *op)
static int merge_nurb(View3D *v3d, Object *obedit)
static void smooth_single_bp(BPoint *bp, const BPoint *bp_orig_prev, const BPoint *bp_orig_next, float factor)
void ed_dissolve_bez_segment(BezTriple *bezt_prev, BezTriple *bezt_next, const Nurb *nu, const Curve *cu, const uint span_len, const uint span_step[2])
static void adduplicateflagNurb(Object *obedit, View3D *v3d, ListBase *newnurb, const uint8_t flag, const bool split)
static int getKeyIndexOrig_keyIndex(EditNurb *editnurb, void *cv)
Definition editcurve.cc:258
static int match_texture_space_exec(bContext *C, wmOperator *)
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:88
static void switch_keys_direction(Curve *cu, Nurb *actnu)
Definition editcurve.cc:480
static void key_to_bezt(float *key, BezTriple *basebezt, BezTriple *bezt)
Definition editcurve.cc:565
static bool curve_is_animated(Curve *cu)
Definition editcurve.cc:905
static void select_bpoints(BPoint *bp, const int stride, const int count, const bool selstatus, const uint8_t flag, const bool hidden)
static int curve_smooth_weight_exec(bContext *C, wmOperator *)
static int make_segment_exec(bContext *C, wmOperator *op)
static int spin_invoke(bContext *C, wmOperator *op, const wmEvent *)
KeyBlock * ED_curve_get_edit_shape_key(const Curve *cu)
Definition editcurve.cc:97
static int curve_split_exec(bContext *C, wmOperator *op)
int ED_curve_updateAnimPaths(Main *bmain, Curve *cu)
static void switchdirection_knots(float *base, int tot)
static int duplicate_exec(bContext *C, wmOperator *op)
static bool curve_delete_vertices(Object *obedit, View3D *v3d)
bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
void CURVE_OT_separate(wmOperatorType *ot)
void CURVE_OT_smooth_tilt(wmOperatorType *ot)
static void keyIndex_delNurbList(EditNurb *editnurb, ListBase *nubase)
Definition editcurve.cc:315
void CURVE_OT_reveal(wmOperatorType *ot)
static int spin_exec(bContext *C, wmOperator *op)
static int subdivide_exec(bContext *C, wmOperator *op)
static int toggle_cyclic_exec(bContext *C, wmOperator *op)
void CURVE_OT_cyclic_toggle(wmOperatorType *ot)
void CURVE_OT_duplicate(wmOperatorType *ot)
static void smooth_single_bezt(BezTriple *bezt, const BezTriple *bezt_orig_prev, const BezTriple *bezt_orig_next, float factor)
bool ED_curve_editnurb_select_pick(bContext *C, const int mval[2], const int dist_px, const SelectPick_Params *params)
static void keyIndex_updateCV(EditNurb *editnurb, char *cv, char *newcv, int count, int size)
Definition editcurve.cc:322
static void fcurve_path_rename(const char *orig_rna_path, const char *rna_path, const blender::Span< FCurve * > orig_curves, blender::Set< FCurve * > &processed_fcurves)
Definition editcurve.cc:915
void CURVE_OT_smooth_radius(wmOperatorType *ot)
static int curve_extrude_exec(bContext *C, wmOperator *)
void ED_curve_editnurb_load(Main *bmain, Object *obedit)
void ED_curve_bpcpy(EditNurb *editnurb, BPoint *dst, BPoint *src, int count)
static blender::Vector< FCurve * > curve_rename_fcurves(Curve *cu, blender::Span< FCurve * > orig_curves)
Definition editcurve.cc:945
static int set_goal_weight_exec(bContext *C, wmOperator *op)
static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op)
static void init_editNurb_keyIndex(EditNurb *editnurb, ListBase *origBase)
Definition editcurve.cc:157
GHash * ED_curve_keyindex_hash_duplicate(GHash *keyindex)
Definition editcurve.cc:543
void CURVE_OT_smooth(wmOperatorType *ot)
static void rotate_direction_nurb(Nurb *nu)
bool ed_editnurb_spin(float viewmat[4][4], View3D *v3d, Object *obedit, const float axis[3], const float cent[3])
static int separate_exec(bContext *C, wmOperator *op)
void CURVE_OT_split(wmOperatorType *ot)
static int set_radius_exec(bContext *C, wmOperator *op)
int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, View3D *v3d, const float location_init[3])
void CURVE_OT_smooth_weight(wmOperatorType *ot)
void CURVE_OT_radius_set(wmOperatorType *ot)
static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void keyIndex_delBP(EditNurb *editnurb, BPoint *bp)
Definition editcurve.cc:278
static void ed_surf_delete_selected(Object *obedit)
static int curve_delete_exec(bContext *C, wmOperator *op)
static bool merge_2_nurb(Curve *cu, ListBase *editnurb, Nurb *nu1, Nurb *nu2)
void CURVE_OT_decimate(wmOperatorType *ot)
static BPoint * getKeyIndexOrig_bp(EditNurb *editnurb, BPoint *bp)
Definition editcurve.cc:247
static BezTriple * getKeyIndexOrig_bezt(EditNurb *editnurb, const BezTriple *bezt)
Definition editcurve.cc:236
static const EnumPropertyItem curve_delete_type_items[]
static int sel_to_copy_ints(const BPoint *bp, const int next_j, const int max_j, const int next_i, const int max_i, const uint8_t flag, int copy_intervals[], int *interval_count, bool *out_is_first_sel)
void CURVE_OT_handle_type_set(wmOperatorType *ot)
void CURVE_OT_subdivide(wmOperatorType *ot)
static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu)
Definition editcurve.cc:287
static void keyIndex_delBezt(EditNurb *editnurb, BezTriple *bezt)
Definition editcurve.cc:269
void CURVE_OT_tilt_clear(wmOperatorType *ot)
static bool is_cyclic(const Nurb *nu)
bool ED_curve_select_check(const View3D *v3d, const EditNurb *editnurb)
bool ED_curve_nurb_select_check(const View3D *v3d, const Nurb *nu)
bool ED_curve_deselect_all(EditNurb *editnurb)
int ED_curve_nurb_select_count(const View3D *v3d, const Nurb *nu)
draw_view in_light_buf[] float
IndexRange range
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
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_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static ulong * next
#define G(x, y, z)
static void clear(Message &msg)
Definition msgfmt.cc:218
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
bool action_fcurve_remove(Action &action, FCurve &fcu)
bool shape_key_report_if_locked(const Object *obedit, ReportList *reports)
void base_activate(bContext *C, Base *base)
Base * add_duplicate(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, eDupli_ID_Flags dupflag)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
Vector< T * > listbase_to_vector(ListBase &list)
const btScalar eps
Definition poly34.cpp:11
return ret
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_factor(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_float_vector_xyz(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_enum_items_add_value(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item, int value)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
unsigned char uint8_t
Definition stdint.h:78
bAction * action
ListBase drivers
uint8_t f1
float vec[4]
struct Object * object
float vec[3][3]
int key_index
Definition BKE_curve.hh:52
int vertex_index
Definition BKE_curve.hh:52
void * orig_cv
Definition BKE_curve.hh:51
bool switched
Definition BKE_curve.hh:53
short resolu
float bevel_radius
struct Key * key
EditNurb * editnurb
ListBase nurb
struct GHash * keyindex
ListBase nurbs
Definition DNA_ID.h:413
int elemsize
char type
ListBase block
KeyBlock * refkey
void * last
void * first
ListBase objects
Definition BKE_main.hh:212
NurbSort * prev
Nurb * nu
float vec[3]
NurbSort * next
short flagu
short orderu
struct Nurb * next
short orderv
float * knotsu
short flag
short type
float * knotsv
BezTriple * bezt
BPoint * bp
short resolu
short resolv
short hide
short flagv
short mat_nr
ObjectRuntimeHandle * runtime
float persmat[4][4]
float viewmat[4][4]
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
View3DOverlay overlay
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
const EnumPropertyItem rna_enum_transform_mode_type_items[]
void WM_cursor_wait(bool val)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_operator_flag_only_pass_through_on_press(int retval, const wmEvent *event)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138