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