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