This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/curve/editcurve.c
Germano Cavalcante aca9a1bac3 Transform Snap Refactor: Use enum for cage snap options
This allows the addition of the `SNAP_GEOM_CAGE` option.
Currently unused.
2021-04-17 15:45:08 -03:00

7098 lines
197 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup edcurve
*/
#include "DNA_anim_types.h"
#include "DNA_key_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.h"
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLT_translation.h"
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_curve.h"
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_transform.h"
#include "ED_transform_snap_object_context.h"
#include "ED_types.h"
#include "ED_view3d.h"
#include "curve_intern.h"
#include "curve_fit_nd.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
void selectend_nurb(Object *obedit, enum eEndPoint_Types selfirst, bool doswap, bool selstatus);
static void adduplicateflagNurb(
Object *obedit, View3D *v3d, ListBase *newnurb, const uint8_t flag, const bool split);
static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split);
static bool curve_delete_vertices(Object *obedit, View3D *v3d);
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
ListBase *object_editcurve_get(Object *ob)
{
if (ob && ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu = ob->data;
return &cu->editnurb->nurbs;
}
return NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debug Printing
* \{ */
#if 0
void printknots(Object *obedit)
{
ListBase *editnurb = object_editcurve_get(obedit);
Nurb *nu;
int a, num;
for (nu = editnurb->first; nu; nu = nu->next) {
if (ED_curve_nurb_select_check(nu) && nu->type == CU_NURBS) {
if (nu->knotsu) {
num = KNOTSU(nu);
for (a = 0; a < num; a++) {
printf("knotu %d: %f\n", a, nu->knotsu[a]);
}
}
if (nu->knotsv) {
num = KNOTSV(nu);
for (a = 0; a < num; a++) {
printf("knotv %d: %f\n", a, nu->knotsv[a]);
}
}
}
}
}
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shape keys
* \{ */
static CVKeyIndex *init_cvKeyIndex(
void *cv, int key_index, int nu_index, int pt_index, int vertex_index)
{
CVKeyIndex *cvIndex = MEM_callocN(sizeof(CVKeyIndex), __func__);
cvIndex->orig_cv = cv;
cvIndex->key_index = key_index;
cvIndex->nu_index = nu_index;
cvIndex->pt_index = pt_index;
cvIndex->vertex_index = vertex_index;
cvIndex->switched = false;
return cvIndex;
}
static void init_editNurb_keyIndex(EditNurb *editnurb, ListBase *origBase)
{
Nurb *nu = editnurb->nurbs.first;
Nurb *orignu = origBase->first;
GHash *gh;
BezTriple *bezt, *origbezt;
BPoint *bp, *origbp;
CVKeyIndex *keyIndex;
int a, key_index = 0, nu_index = 0, pt_index = 0, vertex_index = 0;
if (editnurb->keyindex) {
return;
}
gh = BLI_ghash_ptr_new("editNurb keyIndex");
while (orignu) {
if (orignu->bezt) {
a = orignu->pntsu;
bezt = nu->bezt;
origbezt = orignu->bezt;
pt_index = 0;
while (a--) {
/* We cannot keep *any* reference to curve obdata,
* it might be replaced and freed while editcurve remain in use
* (in viewport render case e.g.). Note that we could use a pool to avoid
* lots of malloc's here, but... not really a problem for now. */
BezTriple *origbezt_cpy = MEM_mallocN(sizeof(*origbezt), __func__);
*origbezt_cpy = *origbezt;
keyIndex = init_cvKeyIndex(origbezt_cpy, key_index, nu_index, pt_index, vertex_index);
BLI_ghash_insert(gh, bezt, keyIndex);
key_index += KEYELEM_FLOAT_LEN_BEZTRIPLE;
vertex_index += 3;
bezt++;
origbezt++;
pt_index++;
}
}
else {
a = orignu->pntsu * orignu->pntsv;
bp = nu->bp;
origbp = orignu->bp;
pt_index = 0;
while (a--) {
/* We cannot keep *any* reference to curve obdata,
* it might be replaced and freed while editcurve remain in use
* (in viewport render case e.g.). Note that we could use a pool to avoid
* lots of malloc's here, but... not really a problem for now. */
BPoint *origbp_cpy = MEM_mallocN(sizeof(*origbp_cpy), __func__);
*origbp_cpy = *origbp;
keyIndex = init_cvKeyIndex(origbp_cpy, key_index, nu_index, pt_index, vertex_index);
BLI_ghash_insert(gh, bp, keyIndex);
key_index += KEYELEM_FLOAT_LEN_BPOINT;
bp++;
origbp++;
pt_index++;
vertex_index++;
}
}
nu = nu->next;
orignu = orignu->next;
nu_index++;
}
editnurb->keyindex = gh;
}
static CVKeyIndex *getCVKeyIndex(EditNurb *editnurb, const void *cv)
{
return BLI_ghash_lookup(editnurb->keyindex, cv);
}
static CVKeyIndex *popCVKeyIndex(EditNurb *editnurb, const void *cv)
{
return BLI_ghash_popkey(editnurb->keyindex, cv, NULL);
}
static BezTriple *getKeyIndexOrig_bezt(EditNurb *editnurb, const BezTriple *bezt)
{
CVKeyIndex *index = getCVKeyIndex(editnurb, bezt);
if (!index) {
return NULL;
}
return (BezTriple *)index->orig_cv;
}
static BPoint *getKeyIndexOrig_bp(EditNurb *editnurb, BPoint *bp)
{
CVKeyIndex *index = getCVKeyIndex(editnurb, bp);
if (!index) {
return NULL;
}
return (BPoint *)index->orig_cv;
}
static int getKeyIndexOrig_keyIndex(EditNurb *editnurb, void *cv)
{
CVKeyIndex *index = getCVKeyIndex(editnurb, cv);
if (!index) {
return -1;
}
return index->key_index;
}
static void keyIndex_delBezt(EditNurb *editnurb, BezTriple *bezt)
{
if (!editnurb->keyindex) {
return;
}
BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, bezt);
}
static void keyIndex_delBP(EditNurb *editnurb, BPoint *bp)
{
if (!editnurb->keyindex) {
return;
}
BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, bp);
}
static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu)
{
int a;
if (!editnurb->keyindex) {
return;
}
if (nu->bezt) {
const BezTriple *bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, bezt);
bezt++;
}
}
else {
const BPoint *bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, bp);
bp++;
}
}
}
static void keyIndex_delNurbList(EditNurb *editnurb, ListBase *nubase)
{
LISTBASE_FOREACH (Nurb *, nu, nubase) {
keyIndex_delNurb(editnurb, nu);
}
}
static void keyIndex_updateCV(EditNurb *editnurb, char *cv, char *newcv, int count, int size)
{
int i;
CVKeyIndex *index;
if (editnurb->keyindex == NULL) {
/* No shape keys - updating not needed */
return;
}
for (i = 0; i < count; i++) {
index = popCVKeyIndex(editnurb, cv);
if (index) {
BLI_ghash_insert(editnurb->keyindex, newcv, index);
}
newcv += size;
cv += size;
}
}
static void keyIndex_updateBezt(EditNurb *editnurb, BezTriple *bezt, BezTriple *newbezt, int count)
{
keyIndex_updateCV(editnurb, (char *)bezt, (char *)newbezt, count, sizeof(BezTriple));
}
static void keyIndex_updateBP(EditNurb *editnurb, BPoint *bp, BPoint *newbp, int count)
{
keyIndex_updateCV(editnurb, (char *)bp, (char *)newbp, count, sizeof(BPoint));
}
void ED_curve_keyindex_update_nurb(EditNurb *editnurb, Nurb *nu, Nurb *newnu)
{
if (nu->bezt) {
keyIndex_updateBezt(editnurb, nu->bezt, newnu->bezt, newnu->pntsu);
}
else {
keyIndex_updateBP(editnurb, nu->bp, newnu->bp, newnu->pntsu * newnu->pntsv);
}
}
static void keyIndex_swap(EditNurb *editnurb, void *a, void *b)
{
CVKeyIndex *index1 = popCVKeyIndex(editnurb, a);
CVKeyIndex *index2 = popCVKeyIndex(editnurb, b);
if (index2) {
BLI_ghash_insert(editnurb->keyindex, a, index2);
}
if (index1) {
BLI_ghash_insert(editnurb->keyindex, b, index1);
}
}
static void keyIndex_switchDirection(EditNurb *editnurb, Nurb *nu)
{
int a;
CVKeyIndex *index1, *index2;
if (nu->bezt) {
BezTriple *bezt1, *bezt2;
a = nu->pntsu;
bezt1 = nu->bezt;
bezt2 = bezt1 + (a - 1);
if (a & 1) {
a++;
}
a /= 2;
while (a--) {
index1 = getCVKeyIndex(editnurb, bezt1);
index2 = getCVKeyIndex(editnurb, bezt2);
if (index1) {
index1->switched = !index1->switched;
}
if (bezt1 != bezt2) {
keyIndex_swap(editnurb, bezt1, bezt2);
if (index2) {
index2->switched = !index2->switched;
}
}
bezt1++;
bezt2--;
}
}
else {
BPoint *bp1, *bp2;
if (nu->pntsv == 1) {
a = nu->pntsu;
bp1 = nu->bp;
bp2 = bp1 + (a - 1);
a /= 2;
while (bp1 != bp2 && a > 0) {
index1 = getCVKeyIndex(editnurb, bp1);
index2 = getCVKeyIndex(editnurb, bp2);
if (index1) {
index1->switched = !index1->switched;
}
if (bp1 != bp2) {
if (index2) {
index2->switched = !index2->switched;
}
keyIndex_swap(editnurb, bp1, bp2);
}
a--;
bp1++;
bp2--;
}
}
else {
int b;
for (b = 0; b < nu->pntsv; b++) {
bp1 = &nu->bp[b * nu->pntsu];
a = nu->pntsu;
bp2 = bp1 + (a - 1);
a /= 2;
while (bp1 != bp2 && a > 0) {
index1 = getCVKeyIndex(editnurb, bp1);
index2 = getCVKeyIndex(editnurb, bp2);
if (index1) {
index1->switched = !index1->switched;
}
if (bp1 != bp2) {
if (index2) {
index2->switched = !index2->switched;
}
keyIndex_swap(editnurb, bp1, bp2);
}
a--;
bp1++;
bp2--;
}
}
}
}
}
static void switch_keys_direction(Curve *cu, Nurb *actnu)
{
EditNurb *editnurb = cu->editnurb;
ListBase *nubase = &editnurb->nurbs;
float *fp;
int a;
LISTBASE_FOREACH (KeyBlock *, currkey, &cu->key->block) {
fp = currkey->data;
LISTBASE_FOREACH (Nurb *, nu, nubase) {
if (nu->bezt) {
BezTriple *bezt = nu->bezt;
a = nu->pntsu;
if (nu == actnu) {
while (a--) {
if (getKeyIndexOrig_bezt(editnurb, bezt)) {
swap_v3_v3(fp, fp + 6);
*(fp + 9) = -*(fp + 9);
fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
bezt++;
}
}
else {
fp += a * KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
}
else {
BPoint *bp = nu->bp;
a = nu->pntsu * nu->pntsv;
if (nu == actnu) {
while (a--) {
if (getKeyIndexOrig_bp(editnurb, bp)) {
*(fp + 3) = -*(fp + 3);
fp += KEYELEM_FLOAT_LEN_BPOINT;
}
bp++;
}
}
else {
fp += a * KEYELEM_FLOAT_LEN_BPOINT;
}
}
}
}
}
static void keyData_switchDirectionNurb(Curve *cu, Nurb *nu)
{
EditNurb *editnurb = cu->editnurb;
if (!editnurb->keyindex) {
/* no shape keys - nothing to do */
return;
}
keyIndex_switchDirection(editnurb, nu);
if (cu->key) {
switch_keys_direction(cu, nu);
}
}
GHash *ED_curve_keyindex_hash_duplicate(GHash *keyindex)
{
GHash *gh;
GHashIterator gh_iter;
gh = BLI_ghash_ptr_new_ex("dupli_keyIndex gh", BLI_ghash_len(keyindex));
GHASH_ITER (gh_iter, keyindex) {
void *cv = BLI_ghashIterator_getKey(&gh_iter);
CVKeyIndex *index = BLI_ghashIterator_getValue(&gh_iter);
CVKeyIndex *newIndex = MEM_mallocN(sizeof(CVKeyIndex), "dupli_keyIndexHash index");
memcpy(newIndex, index, sizeof(CVKeyIndex));
newIndex->orig_cv = MEM_dupallocN(index->orig_cv);
BLI_ghash_insert(gh, cv, newIndex);
}
return gh;
}
static void key_to_bezt(float *key, BezTriple *basebezt, BezTriple *bezt)
{
memcpy(bezt, basebezt, sizeof(BezTriple));
memcpy(bezt->vec, key, sizeof(float[9]));
bezt->tilt = key[9];
bezt->radius = key[10];
}
static void bezt_to_key(BezTriple *bezt, float *key)
{
memcpy(key, bezt->vec, sizeof(float[9]));
key[9] = bezt->tilt;
key[10] = bezt->radius;
}
static void calc_keyHandles(ListBase *nurb, float *key)
{
int a;
float *fp = key;
BezTriple *bezt;
LISTBASE_FOREACH (Nurb *, nu, nurb) {
if (nu->bezt) {
BezTriple *prevp, *nextp;
BezTriple cur, prev, next;
float *startfp, *prevfp, *nextfp;
bezt = nu->bezt;
a = nu->pntsu;
startfp = fp;
if (nu->flagu & CU_NURB_CYCLIC) {
prevp = bezt + (a - 1);
prevfp = fp + (KEYELEM_FLOAT_LEN_BEZTRIPLE * (a - 1));
}
else {
prevp = NULL;
prevfp = NULL;
}
nextp = bezt + 1;
nextfp = fp + KEYELEM_FLOAT_LEN_BEZTRIPLE;
while (a--) {
key_to_bezt(fp, bezt, &cur);
if (nextp) {
key_to_bezt(nextfp, nextp, &next);
}
if (prevp) {
key_to_bezt(prevfp, prevp, &prev);
}
BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0, 0);
bezt_to_key(&cur, fp);
prevp = bezt;
prevfp = fp;
if (a == 1) {
if (nu->flagu & CU_NURB_CYCLIC) {
nextp = nu->bezt;
nextfp = startfp;
}
else {
nextp = NULL;
nextfp = NULL;
}
}
else {
nextp++;
nextfp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
bezt++;
fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
}
else {
a = nu->pntsu * nu->pntsv;
fp += a * KEYELEM_FLOAT_LEN_BPOINT;
}
}
}
static void calc_shapeKeys(Object *obedit, ListBase *newnurbs)
{
Curve *cu = (Curve *)obedit->data;
if (cu->key == NULL) {
return;
}
int a, i;
EditNurb *editnurb = cu->editnurb;
KeyBlock *actkey = BLI_findlink(&cu->key->block, editnurb->shapenr - 1);
BezTriple *bezt, *oldbezt;
BPoint *bp, *oldbp;
Nurb *newnu;
int totvert = BKE_keyblock_curve_element_count(&editnurb->nurbs);
float(*ofs)[3] = NULL;
float *oldkey, *newkey, *ofp;
/* editing the base key should update others */
if (cu->key->type == KEY_RELATIVE) {
if (BKE_keyblock_is_basis(cu->key, editnurb->shapenr - 1)) { /* active key is a base */
int totvec = 0;
/* Calculate needed memory to store offset */
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if (nu->bezt) {
/* Three vects to store handles and one for tilt. */
totvec += nu->pntsu * 4;
}
else {
totvec += 2 * nu->pntsu * nu->pntsv;
}
}
ofs = MEM_callocN(sizeof(float[3]) * totvec, "currkey->data");
i = 0;
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if (nu->bezt) {
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
oldbezt = getKeyIndexOrig_bezt(editnurb, bezt);
if (oldbezt) {
int j;
for (j = 0; j < 3; j++) {
sub_v3_v3v3(ofs[i], bezt->vec[j], oldbezt->vec[j]);
i++;
}
ofs[i][0] = bezt->tilt - oldbezt->tilt;
ofs[i][1] = bezt->radius - oldbezt->radius;
i++;
}
else {
i += 4;
}
bezt++;
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
oldbp = getKeyIndexOrig_bp(editnurb, bp);
if (oldbp) {
sub_v3_v3v3(ofs[i], bp->vec, oldbp->vec);
ofs[i + 1][0] = bp->tilt - oldbp->tilt;
ofs[i + 1][1] = bp->radius - oldbp->radius;
}
i += 2;
bp++;
}
}
}
}
}
LISTBASE_FOREACH (KeyBlock *, currkey, &cu->key->block) {
const bool apply_offset = (ofs && (currkey != actkey) &&
(editnurb->shapenr - 1 == currkey->relative));
float *fp = newkey = MEM_callocN(cu->key->elemsize * totvert, "currkey->data");
ofp = oldkey = currkey->data;
Nurb *nu = editnurb->nurbs.first;
/* We need to restore to original curve into newnurb, *not* editcurve's nurbs.
* Otherwise, in case we update obdata *without* leaving editmode (e.g. viewport render),
* we would invalidate editcurve. */
newnu = newnurbs->first;
i = 0;
while (nu) {
if (currkey == actkey) {
const bool restore = actkey != cu->key->refkey;
if (nu->bezt) {
bezt = nu->bezt;
a = nu->pntsu;
BezTriple *newbezt = newnu->bezt;
while (a--) {
int j;
oldbezt = getKeyIndexOrig_bezt(editnurb, bezt);
for (j = 0; j < 3; j++, i++) {
copy_v3_v3(&fp[j * 3], bezt->vec[j]);
if (restore && oldbezt) {
copy_v3_v3(newbezt->vec[j], oldbezt->vec[j]);
}
}
fp[9] = bezt->tilt;
fp[10] = bezt->radius;
if (restore && oldbezt) {
newbezt->tilt = oldbezt->tilt;
newbezt->radius = oldbezt->radius;
}
fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
i++;
bezt++;
newbezt++;
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
BPoint *newbp = newnu->bp;
while (a--) {
oldbp = getKeyIndexOrig_bp(editnurb, bp);
copy_v3_v3(fp, bp->vec);
fp[3] = bp->tilt;
fp[4] = bp->radius;
if (restore && oldbp) {
copy_v3_v3(newbp->vec, oldbp->vec);
newbp->tilt = oldbp->tilt;
newbp->radius = oldbp->radius;
}
fp += KEYELEM_FLOAT_LEN_BPOINT;
bp++;
newbp++;
i += 2;
}
}
}
else {
int index;
const float *curofp;
if (oldkey) {
if (nu->bezt) {
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
index = getKeyIndexOrig_keyIndex(editnurb, bezt);
if (index >= 0) {
int j;
curofp = ofp + index;
for (j = 0; j < 3; j++, i++) {
copy_v3_v3(&fp[j * 3], &curofp[j * 3]);
if (apply_offset) {
add_v3_v3(&fp[j * 3], ofs[i]);
}
}
fp[9] = curofp[9];
fp[10] = curofp[10];
if (apply_offset) {
/* Apply tilt offsets. */
add_v3_v3(fp + 9, ofs[i]);
i++;
}
fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
else {
int j;
for (j = 0; j < 3; j++, i++) {
copy_v3_v3(&fp[j * 3], bezt->vec[j]);
}
fp[9] = bezt->tilt;
fp[10] = bezt->radius;
fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
bezt++;
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
index = getKeyIndexOrig_keyIndex(editnurb, bp);
if (index >= 0) {
curofp = ofp + index;
copy_v3_v3(fp, curofp);
fp[3] = curofp[3];
fp[4] = curofp[4];
if (apply_offset) {
add_v3_v3(fp, ofs[i]);
add_v3_v3(&fp[3], ofs[i + 1]);
}
}
else {
copy_v3_v3(fp, bp->vec);
fp[3] = bp->tilt;
fp[4] = bp->radius;
}
fp += KEYELEM_FLOAT_LEN_BPOINT;
bp++;
i += 2;
}
}
}
}
nu = nu->next;
newnu = newnu->next;
}
if (apply_offset) {
/* handles could become malicious after offsets applying */
calc_keyHandles(&editnurb->nurbs, newkey);
}
currkey->totelem = totvert;
if (currkey->data) {
MEM_freeN(currkey->data);
}
currkey->data = newkey;
}
if (ofs) {
MEM_freeN(ofs);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Animation Data
* \{ */
static bool curve_is_animated(Curve *cu)
{
AnimData *ad = BKE_animdata_from_id(&cu->id);
return ad && (ad->action || ad->drivers.first);
}
static void fcurve_path_rename(AnimData *adt,
const char *orig_rna_path,
const char *rna_path,
ListBase *orig_curves,
ListBase *curves)
{
FCurve *nfcu;
int len = strlen(orig_rna_path);
LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, orig_curves) {
if (STREQLEN(fcu->rna_path, orig_rna_path, len)) {
char *spath, *suffix = fcu->rna_path + len;
nfcu = BKE_fcurve_copy(fcu);
spath = nfcu->rna_path;
nfcu->rna_path = BLI_sprintfN("%s%s", rna_path, suffix);
/* BKE_fcurve_copy() sets nfcu->grp to NULL. To maintain the groups, we need to keep the
* pointer. As a result, the group's 'channels' pointers will be wrong, which is fixed by
* calling `action_groups_reconstruct(action)` later, after all fcurves have been renamed. */
nfcu->grp = fcu->grp;
BLI_addtail(curves, nfcu);
if (fcu->grp) {
action_groups_remove_channel(adt->action, fcu);
}
else if ((adt->action) && (&adt->action->curves == orig_curves)) {
BLI_remlink(&adt->action->curves, fcu);
}
else {
BLI_remlink(&adt->drivers, fcu);
}
BKE_fcurve_free(fcu);
MEM_freeN(spath);
}
}
}
static void fcurve_remove(AnimData *adt, ListBase *orig_curves, FCurve *fcu)
{
if (orig_curves == &adt->drivers) {
BLI_remlink(&adt->drivers, fcu);
}
else {
action_groups_remove_channel(adt->action, fcu);
}
BKE_fcurve_free(fcu);
}
static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves)
{
int a, pt_index;
EditNurb *editnurb = cu->editnurb;
CVKeyIndex *keyIndex;
char rna_path[64], orig_rna_path[64];
AnimData *adt = BKE_animdata_from_id(&cu->id);
ListBase curves = {NULL, NULL};
int nu_index = 0;
LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, nu_index) {
if (nu->bezt) {
BezTriple *bezt = nu->bezt;
a = nu->pntsu;
pt_index = 0;
while (a--) {
keyIndex = getCVKeyIndex(editnurb, bezt);
if (keyIndex) {
BLI_snprintf(
rna_path, sizeof(rna_path), "splines[%d].bezier_points[%d]", nu_index, pt_index);
BLI_snprintf(orig_rna_path,
sizeof(orig_rna_path),
"splines[%d].bezier_points[%d]",
keyIndex->nu_index,
keyIndex->pt_index);
if (keyIndex->switched) {
char handle_path[64], orig_handle_path[64];
BLI_snprintf(orig_handle_path, sizeof(orig_rna_path), "%s.handle_left", orig_rna_path);
BLI_snprintf(handle_path, sizeof(rna_path), "%s.handle_right", rna_path);
fcurve_path_rename(adt, orig_handle_path, handle_path, orig_curves, &curves);
BLI_snprintf(
orig_handle_path, sizeof(orig_rna_path), "%s.handle_right", orig_rna_path);
BLI_snprintf(handle_path, sizeof(rna_path), "%s.handle_left", rna_path);
fcurve_path_rename(adt, orig_handle_path, handle_path, orig_curves, &curves);
}
fcurve_path_rename(adt, orig_rna_path, rna_path, orig_curves, &curves);
keyIndex->nu_index = nu_index;
keyIndex->pt_index = pt_index;
}
bezt++;
pt_index++;
}
}
else {
BPoint *bp = nu->bp;
a = nu->pntsu * nu->pntsv;
pt_index = 0;
while (a--) {
keyIndex = getCVKeyIndex(editnurb, bp);
if (keyIndex) {
BLI_snprintf(rna_path, sizeof(rna_path), "splines[%d].points[%d]", nu_index, pt_index);
BLI_snprintf(orig_rna_path,
sizeof(orig_rna_path),
"splines[%d].points[%d]",
keyIndex->nu_index,
keyIndex->pt_index);
fcurve_path_rename(adt, orig_rna_path, rna_path, orig_curves, &curves);
keyIndex->nu_index = nu_index;
keyIndex->pt_index = pt_index;
}
bp++;
pt_index++;
}
}
}
/* remove paths for removed control points
* need this to make further step with copying non-cv related curves copying
* not touching cv's f-curves */
LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, orig_curves) {
if (STRPREFIX(fcu->rna_path, "splines")) {
const char *ch = strchr(fcu->rna_path, '.');
if (ch && (STRPREFIX(ch, ".bezier_points") || STRPREFIX(ch, ".points"))) {
fcurve_remove(adt, orig_curves, fcu);
}
}
}
nu_index = 0;
LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, nu_index) {
keyIndex = NULL;
if (nu->pntsu) {
if (nu->bezt) {
keyIndex = getCVKeyIndex(editnurb, &nu->bezt[0]);
}
else {
keyIndex = getCVKeyIndex(editnurb, &nu->bp[0]);
}
}
if (keyIndex) {
BLI_snprintf(rna_path, sizeof(rna_path), "splines[%d]", nu_index);
BLI_snprintf(orig_rna_path, sizeof(orig_rna_path), "splines[%d]", keyIndex->nu_index);
fcurve_path_rename(adt, orig_rna_path, rna_path, orig_curves, &curves);
}
}
/* the remainders in orig_curves can be copied back (like follow path) */
/* (if it's not path to spline) */
LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, orig_curves) {
if (STRPREFIX(fcu->rna_path, "splines")) {
fcurve_remove(adt, orig_curves, fcu);
}
else {
BLI_addtail(&curves, fcu);
}
}
*orig_curves = curves;
if (adt != NULL) {
BKE_action_groups_reconstruct(adt->action);
}
}
/* return 0 if animation data wasn't changed, 1 otherwise */
int ED_curve_updateAnimPaths(Main *bmain, Curve *cu)
{
AnimData *adt = BKE_animdata_from_id(&cu->id);
EditNurb *editnurb = cu->editnurb;
if (!editnurb->keyindex) {
return 0;
}
if (!curve_is_animated(cu)) {
return 0;
}
if (adt->action != NULL) {
curve_rename_fcurves(cu, &adt->action->curves);
DEG_id_tag_update(&adt->action->id, ID_RECALC_COPY_ON_WRITE);
}
curve_rename_fcurves(cu, &adt->drivers);
DEG_id_tag_update(&cu->id, ID_RECALC_COPY_ON_WRITE);
/* TODO(sergey): Only update if something actually changed. */
DEG_relations_tag_update(bmain);
return 1;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit Mode Conversion (Make & Load)
* \{ */
static int *init_index_map(Object *obedit, int *r_old_totvert)
{
Curve *curve = (Curve *)obedit->data;
EditNurb *editnurb = curve->editnurb;
CVKeyIndex *keyIndex;
int *old_to_new_map;
int old_totvert = 0;
LISTBASE_FOREACH (Nurb *, nu, &curve->nurb) {
if (nu->bezt) {
old_totvert += nu->pntsu * 3;
}
else {
old_totvert += nu->pntsu * nu->pntsv;
}
}
old_to_new_map = MEM_mallocN(old_totvert * sizeof(int), "curve old to new index map");
for (int i = 0; i < old_totvert; i++) {
old_to_new_map[i] = -1;
}
int vertex_index = 0;
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if (nu->bezt) {
BezTriple *bezt = nu->bezt;
int a = nu->pntsu;
while (a--) {
keyIndex = getCVKeyIndex(editnurb, bezt);
if (keyIndex && keyIndex->vertex_index + 2 < old_totvert) {
if (keyIndex->switched) {
old_to_new_map[keyIndex->vertex_index] = vertex_index + 2;
old_to_new_map[keyIndex->vertex_index + 1] = vertex_index + 1;
old_to_new_map[keyIndex->vertex_index + 2] = vertex_index;
}
else {
old_to_new_map[keyIndex->vertex_index] = vertex_index;
old_to_new_map[keyIndex->vertex_index + 1] = vertex_index + 1;
old_to_new_map[keyIndex->vertex_index + 2] = vertex_index + 2;
}
}
vertex_index += 3;
bezt++;
}
}
else {
BPoint *bp = nu->bp;
int a = nu->pntsu * nu->pntsv;
while (a--) {
keyIndex = getCVKeyIndex(editnurb, bp);
if (keyIndex) {
old_to_new_map[keyIndex->vertex_index] = vertex_index;
}
vertex_index++;
bp++;
}
}
}
*r_old_totvert = old_totvert;
return old_to_new_map;
}
static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit)
{
Curve *curve = (Curve *)obedit->data;
EditNurb *editnurb = curve->editnurb;
int *old_to_new_map = NULL;
int old_totvert;
if (editnurb->keyindex == NULL) {
/* TODO(sergey): Happens when separating curves, this would lead to
* the wrong indices in the hook modifier, address this together with
* other indices issues.
*/
return;
}
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
int index;
if ((object->parent) && (object->parent->data == curve) &&
ELEM(object->partype, PARVERT1, PARVERT3)) {
if (old_to_new_map == NULL) {
old_to_new_map = init_index_map(obedit, &old_totvert);
}
if (object->par1 < old_totvert) {
index = old_to_new_map[object->par1];
if (index != -1) {
object->par1 = index;
}
}
if (object->par2 < old_totvert) {
index = old_to_new_map[object->par2];
if (index != -1) {
object->par2 = index;
}
}
if (object->par3 < old_totvert) {
index = old_to_new_map[object->par3];
if (index != -1) {
object->par3 = index;
}
}
}
if (object->data == curve) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
int i, j;
if (old_to_new_map == NULL) {
old_to_new_map = init_index_map(obedit, &old_totvert);
}
for (i = j = 0; i < hmd->totindex; i++) {
if (hmd->indexar[i] < old_totvert) {
index = old_to_new_map[hmd->indexar[i]];
if (index != -1) {
hmd->indexar[j++] = index;
}
}
else {
j++;
}
}
hmd->totindex = j;
}
}
}
}
if (old_to_new_map != NULL) {
MEM_freeN(old_to_new_map);
}
}
/* load editNurb in object */
void ED_curve_editnurb_load(Main *bmain, Object *obedit)
{
ListBase *editnurb = object_editcurve_get(obedit);
if (obedit == NULL) {
return;
}
if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
Curve *cu = obedit->data;
ListBase newnurb = {NULL, NULL}, oldnurb = cu->nurb;
remap_hooks_and_vertex_parents(bmain, obedit);
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
Nurb *newnu = BKE_nurb_duplicate(nu);
BLI_addtail(&newnurb, newnu);
if (nu->type == CU_NURBS) {
BKE_nurb_order_clamp_u(nu);
}
}
/* We have to pass also new copied nurbs, since we want to restore original curve
* (without edited shapekey) on obdata, but *not* on editcurve itself
* (ED_curve_editnurb_load call does not always implies freeing
* of editcurve, e.g. when called to generate render data). */
calc_shapeKeys(obedit, &newnurb);
cu->nurb = newnurb;
ED_curve_updateAnimPaths(bmain, obedit->data);
BKE_nurbList_free(&oldnurb);
}
}
/* make copy in cu->editnurb */
void ED_curve_editnurb_make(Object *obedit)
{
Curve *cu = (Curve *)obedit->data;
EditNurb *editnurb = cu->editnurb;
KeyBlock *actkey;
if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
actkey = BKE_keyblock_from_object(obedit);
if (actkey) {
// XXX strcpy(G.editModeTitleExtra, "(Key) ");
/* TODO(campbell): undo_system: investigate why this was needed. */
#if 0
undo_editmode_clear();
#endif
}
if (editnurb) {
BKE_nurbList_free(&editnurb->nurbs);
BKE_curve_editNurb_keyIndex_free(&editnurb->keyindex);
}
else {
editnurb = MEM_callocN(sizeof(EditNurb), "editnurb");
cu->editnurb = editnurb;
}
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
Nurb *newnu = BKE_nurb_duplicate(nu);
BLI_addtail(&editnurb->nurbs, newnu);
}
/* animation could be added in editmode even if there was no animdata in
* object mode hence we always need CVs index be created */
init_editNurb_keyIndex(editnurb, &cu->nurb);
if (actkey) {
editnurb->shapenr = obedit->shapenr;
/* Apply shapekey to new nurbs of editnurb, not those of original curve
* (and *after* we generated keyIndex), else we do not have valid 'original' data
* to properly restore curve when leaving editmode. */
BKE_keyblock_convert_to_curve(actkey, cu, &editnurb->nurbs);
}
}
}
void ED_curve_editnurb_free(Object *obedit)
{
Curve *cu = obedit->data;
BKE_curve_editNurb_free(cu);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Separate Operator
* \{ */
static int separate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
struct {
int changed;
int unselected;
int error_vertex_keys;
int error_generic;
} status = {0};
WM_cursor_wait(true);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &bases_len);
for (uint b_index = 0; b_index < bases_len; b_index++) {
Base *oldbase = bases[b_index];
Base *newbase;
Object *oldob, *newob;
Curve *oldcu, *newcu;
EditNurb *newedit;
ListBase newnurb = {NULL, NULL};
oldob = oldbase->object;
oldcu = oldob->data;
if (oldcu->key) {
status.error_vertex_keys++;
continue;
}
if (!ED_curve_select_check(v3d, oldcu->editnurb)) {
status.unselected++;
continue;
}
/* 1. Duplicate geometry and check for valid selection for separate. */
adduplicateflagNurb(oldob, v3d, &newnurb, SELECT, true);
if (BLI_listbase_is_empty(&newnurb)) {
status.error_generic++;
continue;
}
/* 2. Duplicate the object and data. */
/* Take into account user preferences for duplicating actions. */
const eDupli_ID_Flags dupflag = (U.dupflag & USER_DUP_ACT);
newbase = ED_object_add_duplicate(bmain, scene, view_layer, oldbase, dupflag);
DEG_relations_tag_update(bmain);
newob = newbase->object;
newcu = newob->data = BKE_id_copy(bmain, &oldcu->id);
newcu->editnurb = NULL;
id_us_min(&oldcu->id); /* Because new curve is a copy: reduce user count. */
/* 3. Put new object in editmode, clear it and set separated nurbs. */
ED_curve_editnurb_make(newob);
newedit = newcu->editnurb;
BKE_nurbList_free(&newedit->nurbs);
BKE_curve_editNurb_keyIndex_free(&newedit->keyindex);
BLI_movelisttolist(&newedit->nurbs, &newnurb);
/* 4. Put old object out of editmode and delete separated geometry. */
ED_curve_editnurb_load(bmain, newob);
ED_curve_editnurb_free(newob);
curve_delete_segments(oldob, v3d, true);
DEG_id_tag_update(&oldob->id, ID_RECALC_GEOMETRY); /* This is the original one. */
DEG_id_tag_update(&newob->id, ID_RECALC_GEOMETRY); /* This is the separated one. */
WM_event_add_notifier(C, NC_GEOM | ND_DATA, oldob->data);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, newob);
status.changed++;
}
MEM_freeN(bases);
WM_cursor_wait(false);
if (status.unselected == bases_len) {
BKE_report(op->reports, RPT_ERROR, "No point was selected");
return OPERATOR_CANCELLED;
}
const int tot_errors = status.error_vertex_keys + status.error_generic;
if (tot_errors > 0) {
/* Some curves changed, but some curves failed: don't explain why it failed. */
if (status.changed) {
BKE_reportf(op->reports,
RPT_INFO,
tot_errors == 1 ? "%d curve could not be separated" :
"%d curves could not be separated",
tot_errors);
return OPERATOR_FINISHED;
}
/* All curves failed: If there is more than one error give a generic error report. */
if (((status.error_vertex_keys ? 1 : 0) + (status.error_generic ? 1 : 0)) > 1) {
BKE_report(op->reports,
RPT_ERROR,
tot_errors == 1 ? "Could not separate selected curves" :
"Could not separate selected curve");
}
/* All curves failed due to the same error. */
if (status.error_vertex_keys) {
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with vertex keys");
}
else {
BLI_assert(status.error_generic);
BKE_report(op->reports, RPT_ERROR, "Cannot separate current selection");
}
return OPERATOR_CANCELLED;
}
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
void CURVE_OT_separate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Separate";
ot->idname = "CURVE_OT_separate";
ot->description = "Separate selected points from connected unselected points into a new object";
/* api callbacks */
ot->invoke = WM_operator_confirm;
ot->exec = separate_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Split Operator
* \{ */
static int curve_split_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
int ok = -1;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase newnurb = {NULL, NULL};
adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, true);
if (BLI_listbase_is_empty(&newnurb)) {
ok = MAX2(ok, 0);
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
const int len_orig = BLI_listbase_count(editnurb);
curve_delete_segments(obedit, v3d, true);
cu->actnu -= len_orig - BLI_listbase_count(editnurb);
BLI_movelisttolist(editnurb, &newnurb);
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
ok = 1;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
if (ok == 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot split current selection");
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void CURVE_OT_split(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Split";
ot->idname = "CURVE_OT_split";
ot->description = "Split off selected points from connected unselected points";
/* api callbacks */
ot->exec = curve_split_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Flag Utility Functions
* \{ */
static bool isNurbselUV(const Nurb *nu, uint8_t flag, int *r_u, int *r_v)
{
/* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv
* return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu
*/
BPoint *bp;
int a, b, sel;
*r_u = *r_v = -1;
bp = nu->bp;
for (b = 0; b < nu->pntsv; b++) {
sel = 0;
for (a = 0; a < nu->pntsu; a++, bp++) {
if (bp->f1 & flag) {
sel++;
}
}
if (sel == nu->pntsu) {
if (*r_u == -1) {
*r_u = b;
}
else {
return 0;
}
}
else if (sel > 1) {
return 0; /* because sel == 1 is still ok */
}
}
for (a = 0; a < nu->pntsu; a++) {
sel = 0;
bp = &nu->bp[a];
for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) {
if (bp->f1 & flag) {
sel++;
}
}
if (sel == nu->pntsv) {
if (*r_v == -1) {
*r_v = a;
}
else {
return 0;
}
}
else if (sel > 1) {
return 0;
}
}
if (*r_u == -1 && *r_v > -1) {
return 1;
}
if (*r_v == -1 && *r_u > -1) {
return 1;
}
return 0;
}
/* return true if U direction is selected and number of selected columns v */
static bool isNurbselU(Nurb *nu, int *v, int flag)
{
BPoint *bp;
int a, b, sel;
*v = 0;
for (b = 0, bp = nu->bp; b < nu->pntsv; b++) {
sel = 0;
for (a = 0; a < nu->pntsu; a++, bp++) {
if (bp->f1 & flag) {
sel++;
}
}
if (sel == nu->pntsu) {
(*v)++;
}
else if (sel >= 1) {
*v = 0;
return 0;
}
}
return 1;
}
/* return true if V direction is selected and number of selected rows u */
static bool isNurbselV(Nurb *nu, int *u, int flag)
{
BPoint *bp;
int a, b, sel;
*u = 0;
for (a = 0; a < nu->pntsu; a++) {
bp = &nu->bp[a];
sel = 0;
for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) {
if (bp->f1 & flag) {
sel++;
}
}
if (sel == nu->pntsv) {
(*u)++;
}
else if (sel >= 1) {
*u = 0;
return 0;
}
}
return 1;
}
static void rotateflagNurb(ListBase *editnurb,
short flag,
const float cent[3],
const float rotmat[3][3])
{
/* all verts with (flag & 'flag') rotate */
BPoint *bp;
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_NURBS) {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
if (bp->f1 & flag) {
sub_v3_v3(bp->vec, cent);
mul_m3_v3(rotmat, bp->vec);
add_v3_v3(bp->vec, cent);
}
bp++;
}
}
}
}
void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3], bool is_2d)
{
/* all verts with ('flag' & flag) translate */
BezTriple *bezt;
BPoint *bp;
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
a = nu->pntsu;
bezt = nu->bezt;
while (a--) {
if (bezt->f1 & flag) {
add_v3_v3(bezt->vec[0], vec);
}
if (bezt->f2 & flag) {
add_v3_v3(bezt->vec[1], vec);
}
if (bezt->f3 & flag) {
add_v3_v3(bezt->vec[2], vec);
}
bezt++;
}
}
else {
a = nu->pntsu * nu->pntsv;
bp = nu->bp;
while (a--) {
if (bp->f1 & flag) {
add_v3_v3(bp->vec, vec);
}
bp++;
}
}
if (is_2d) {
BKE_nurb_project_2d(nu);
}
}
}
static void weightflagNurb(ListBase *editnurb, short flag, float w)
{
BPoint *bp;
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_NURBS) {
a = nu->pntsu * nu->pntsv;
bp = nu->bp;
while (a--) {
if (bp->f1 & flag) {
/* a mode used to exist for replace/multiple but is was unused */
bp->vec[3] *= w;
}
bp++;
}
}
}
}
static void ed_surf_delete_selected(Object *obedit)
{
Curve *cu = obedit->data;
ListBase *editnurb = object_editcurve_get(obedit);
BPoint *bp, *bpn, *newbp;
int a, b, newu, newv;
BLI_assert(obedit->type == OB_SURF);
LISTBASE_FOREACH_MUTABLE (Nurb *, nu, editnurb) {
/* is entire nurb selected */
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a) {
a--;
if (bp->f1 & SELECT) {
/* pass */
}
else {
break;
}
bp++;
}
if (a == 0) {
BLI_remlink(editnurb, nu);
keyIndex_delNurb(cu->editnurb, nu);
BKE_nurb_free(nu);
nu = NULL;
}
else {
if (isNurbselU(nu, &newv, SELECT)) {
/* U direction selected */
newv = nu->pntsv - newv;
if (newv != nu->pntsv) {
/* delete */
bp = nu->bp;
bpn = newbp = (BPoint *)MEM_mallocN(newv * nu->pntsu * sizeof(BPoint), "deleteNurb");
for (b = 0; b < nu->pntsv; b++) {
if ((bp->f1 & SELECT) == 0) {
memcpy(bpn, bp, nu->pntsu * sizeof(BPoint));
keyIndex_updateBP(cu->editnurb, bp, bpn, nu->pntsu);
bpn += nu->pntsu;
}
else {
keyIndex_delBP(cu->editnurb, bp);
}
bp += nu->pntsu;
}
nu->pntsv = newv;
MEM_freeN(nu->bp);
nu->bp = newbp;
BKE_nurb_order_clamp_v(nu);
BKE_nurb_knot_calc_v(nu);
}
}
else if (isNurbselV(nu, &newu, SELECT)) {
/* V direction selected */
newu = nu->pntsu - newu;
if (newu != nu->pntsu) {
/* delete */
bp = nu->bp;
bpn = newbp = (BPoint *)MEM_mallocN(newu * nu->pntsv * sizeof(BPoint), "deleteNurb");
for (b = 0; b < nu->pntsv; b++) {
for (a = 0; a < nu->pntsu; a++, bp++) {
if ((bp->f1 & SELECT) == 0) {
*bpn = *bp;
keyIndex_updateBP(cu->editnurb, bp, bpn, 1);
bpn++;
}
else {
keyIndex_delBP(cu->editnurb, bp);
}
}
}
MEM_freeN(nu->bp);
nu->bp = newbp;
if (newu == 1 && nu->pntsv > 1) { /* make a U spline */
nu->pntsu = nu->pntsv;
nu->pntsv = 1;
SWAP(short, nu->orderu, nu->orderv);
BKE_nurb_order_clamp_u(nu);
if (nu->knotsv) {
MEM_freeN(nu->knotsv);
}
nu->knotsv = NULL;
}
else {
nu->pntsu = newu;
BKE_nurb_order_clamp_u(nu);
}
BKE_nurb_knot_calc_u(nu);
}
}
}
}
}
static void ed_curve_delete_selected(Object *obedit, View3D *v3d)
{
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
ListBase *nubase = &editnurb->nurbs;
BezTriple *bezt, *bezt1;
BPoint *bp, *bp1;
int a, type, nuindex = 0;
/* first loop, can we remove entire pieces? */
LISTBASE_FOREACH_MUTABLE (Nurb *, nu, nubase) {
if (nu->type == CU_BEZIER) {
bezt = nu->bezt;
a = nu->pntsu;
if (a) {
while (a) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
/* pass */
}
else {
break;
}
a--;
bezt++;
}
if (a == 0) {
if (cu->actnu == nuindex) {
cu->actnu = CU_ACT_NONE;
}
BLI_remlink(nubase, nu);
keyIndex_delNurb(editnurb, nu);
BKE_nurb_free(nu);
nu = NULL;
}
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
if (a) {
while (a) {
if (bp->f1 & SELECT) {
/* pass */
}
else {
break;
}
a--;
bp++;
}
if (a == 0) {
if (cu->actnu == nuindex) {
cu->actnu = CU_ACT_NONE;
}
BLI_remlink(nubase, nu);
keyIndex_delNurb(editnurb, nu);
BKE_nurb_free(nu);
nu = NULL;
}
}
}
/* Never allow the order to exceed the number of points
* - note, this is ok but changes unselected nurbs, disable for now */
#if 0
if ((nu != NULL) && (nu->type == CU_NURBS)) {
clamp_nurb_order_u(nu);
}
#endif
nuindex++;
}
/* 2nd loop, delete small pieces: just for curves */
LISTBASE_FOREACH_MUTABLE (Nurb *, nu, nubase) {
type = 0;
if (nu->type == CU_BEZIER) {
bezt = nu->bezt;
for (a = 0; a < nu->pntsu; a++) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
memmove(bezt, bezt + 1, (nu->pntsu - a - 1) * sizeof(BezTriple));
keyIndex_delBezt(editnurb, bezt);
keyIndex_updateBezt(editnurb, bezt + 1, bezt, nu->pntsu - a - 1);
nu->pntsu--;
a--;
type = 1;
}
else {
bezt++;
}
}
if (type) {
bezt1 = (BezTriple *)MEM_mallocN((nu->pntsu) * sizeof(BezTriple), "delNurb");
memcpy(bezt1, nu->bezt, (nu->pntsu) * sizeof(BezTriple));
keyIndex_updateBezt(editnurb, nu->bezt, bezt1, nu->pntsu);
MEM_freeN(nu->bezt);
nu->bezt = bezt1;
BKE_nurb_handles_calc(nu);
}
}
else if (nu->pntsv == 1) {
bp = nu->bp;
for (a = 0; a < nu->pntsu; a++) {
if (bp->f1 & SELECT) {
memmove(bp, bp + 1, (nu->pntsu - a - 1) * sizeof(BPoint));
keyIndex_delBP(editnurb, bp);
keyIndex_updateBP(editnurb, bp + 1, bp, nu->pntsu - a - 1);
nu->pntsu--;
a--;
type = 1;
}
else {
bp++;
}
}
if (type) {
bp1 = (BPoint *)MEM_mallocN(nu->pntsu * sizeof(BPoint), "delNurb2");
memcpy(bp1, nu->bp, (nu->pntsu) * sizeof(BPoint));
keyIndex_updateBP(editnurb, nu->bp, bp1, nu->pntsu);
MEM_freeN(nu->bp);
nu->bp = bp1;
/* Never allow the order to exceed the number of points
* - note, this is ok but changes unselected nurbs, disable for now */
#if 0
if (nu->type == CU_NURBS) {
clamp_nurb_order_u(nu);
}
#endif
}
BKE_nurb_order_clamp_u(nu);
BKE_nurb_knot_calc_u(nu);
}
}
}
/* only for OB_SURF */
bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag)
{
BPoint *bp, *bpn, *newbp;
int a, u, v, len;
bool ok = false;
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if (nu->pntsv == 1) {
bp = nu->bp;
a = nu->pntsu;
while (a) {
if (bp->f1 & flag) {
/* pass */
}
else {
break;
}
bp++;
a--;
}
if (a == 0) {
ok = true;
newbp = (BPoint *)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1");
ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu);
bp = newbp + nu->pntsu;
ED_curve_bpcpy(editnurb, bp, nu->bp, nu->pntsu);
MEM_freeN(nu->bp);
nu->bp = newbp;
a = nu->pntsu;
while (a--) {
select_bpoint(bp, SELECT, flag, HIDDEN);
select_bpoint(newbp, DESELECT, flag, HIDDEN);
bp++;
newbp++;
}
nu->pntsv = 2;
nu->orderv = 2;
BKE_nurb_knot_calc_v(nu);
}
}
else {
/* which row or column is selected */
if (isNurbselUV(nu, flag, &u, &v)) {
/* deselect all */
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
select_bpoint(bp, DESELECT, flag, HIDDEN);
bp++;
}
if (ELEM(u, 0, nu->pntsv - 1)) { /* row in u-direction selected */
ok = true;
newbp = (BPoint *)MEM_mallocN(nu->pntsu * (nu->pntsv + 1) * sizeof(BPoint),
"extrudeNurb1");
if (u == 0) {
len = nu->pntsv * nu->pntsu;
ED_curve_bpcpy(editnurb, newbp + nu->pntsu, nu->bp, len);
ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu);
bp = newbp;
}
else {
len = nu->pntsv * nu->pntsu;
ED_curve_bpcpy(editnurb, newbp, nu->bp, len);
ED_curve_bpcpy(editnurb, newbp + len, &nu->bp[len - nu->pntsu], nu->pntsu);
bp = newbp + len;
}
a = nu->pntsu;
while (a--) {
select_bpoint(bp, SELECT, flag, HIDDEN);
bp++;
}
MEM_freeN(nu->bp);
nu->bp = newbp;
nu->pntsv++;
BKE_nurb_knot_calc_v(nu);
}
else if (ELEM(v, 0, nu->pntsu - 1)) { /* column in v-direction selected */
ok = true;
bpn = newbp = (BPoint *)MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint),
"extrudeNurb1");
bp = nu->bp;
for (a = 0; a < nu->pntsv; a++) {
if (v == 0) {
*bpn = *bp;
bpn->f1 |= flag;
bpn++;
}
ED_curve_bpcpy(editnurb, bpn, bp, nu->pntsu);
bp += nu->pntsu;
bpn += nu->pntsu;
if (v == nu->pntsu - 1) {
*bpn = *(bp - 1);
bpn->f1 |= flag;
bpn++;
}
}
MEM_freeN(nu->bp);
nu->bp = newbp;
nu->pntsu++;
BKE_nurb_knot_calc_u(nu);
}
}
}
}
return ok;
}
static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu)
{
cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb);
}
static bool calc_duplicate_actvert(
const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert)
{
if (cu->actvert == -1) {
calc_duplicate_actnurb(editnurb, newnurb, cu);
return true;
}
if ((start <= cu->actvert) && (end > cu->actvert)) {
calc_duplicate_actnurb(editnurb, newnurb, cu);
cu->actvert = vert;
return true;
}
return false;
}
static void adduplicateflagNurb(
Object *obedit, View3D *v3d, ListBase *newnurb, const uint8_t flag, const bool split)
{
ListBase *editnurb = object_editcurve_get(obedit);
Nurb *newnu;
BezTriple *bezt, *bezt1;
BPoint *bp, *bp1, *bp2, *bp3;
Curve *cu = (Curve *)obedit->data;
int a, b, c, starta, enda, diffa, cyclicu, cyclicv, newu, newv;
char *usel;
int i = 0;
LISTBASE_FOREACH_INDEX (Nurb *, nu, editnurb, i) {
cyclicu = cyclicv = 0;
if (nu->type == CU_BEZIER) {
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
enda = -1;
starta = a;
while ((bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag)) {
if (!split) {
select_beztriple(bezt, DESELECT, flag, HIDDEN);
}
enda = a;
if (a >= nu->pntsu - 1) {
break;
}
a++;
bezt++;
}
if (enda >= starta) {
newu = diffa = enda - starta + 1; /* set newu and diffa, may use both */
if (starta == 0 && newu != nu->pntsu && (nu->flagu & CU_NURB_CYCLIC)) {
cyclicu = newu;
}
else {
if (enda == nu->pntsu - 1) {
newu += cyclicu;
}
if (i == cu->actnu) {
calc_duplicate_actvert(
editnurb, newnurb, cu, starta, starta + diffa, cu->actvert - starta);
}
newnu = BKE_nurb_copy(nu, newu, 1);
memcpy(newnu->bezt, &nu->bezt[starta], diffa * sizeof(BezTriple));
if (newu != diffa) {
memcpy(&newnu->bezt[diffa], nu->bezt, cyclicu * sizeof(BezTriple));
if (i == cu->actnu) {
calc_duplicate_actvert(
editnurb, newnurb, cu, 0, cyclicu, newu - cyclicu + cu->actvert);
}
cyclicu = 0;
}
if (newu != nu->pntsu) {
newnu->flagu &= ~CU_NURB_CYCLIC;
}
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
select_beztriple(bezt1, SELECT, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
}
}
}
if (cyclicu != 0) {
if (i == cu->actnu) {
calc_duplicate_actvert(editnurb, newnurb, cu, 0, cyclicu, cu->actvert);
}
newnu = BKE_nurb_copy(nu, cyclicu, 1);
memcpy(newnu->bezt, nu->bezt, cyclicu * sizeof(BezTriple));
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
select_beztriple(bezt1, SELECT, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
}
}
else if (nu->pntsv == 1) { /* because UV Nurb has a different method for dupli */
for (a = 0, bp = nu->bp; a < nu->pntsu; a++, bp++) {
enda = -1;
starta = a;
while (bp->f1 & flag) {
if (!split) {
select_bpoint(bp, DESELECT, flag, HIDDEN);
}
enda = a;
if (a >= nu->pntsu - 1) {
break;
}
a++;
bp++;
}
if (enda >= starta) {
newu = diffa = enda - starta + 1; /* set newu and diffa, may use both */
if (starta == 0 && newu != nu->pntsu && (nu->flagu & CU_NURB_CYCLIC)) {
cyclicu = newu;
}
else {
if (enda == nu->pntsu - 1) {
newu += cyclicu;
}
if (i == cu->actnu) {
calc_duplicate_actvert(
editnurb, newnurb, cu, starta, starta + diffa, cu->actvert - starta);
}
newnu = BKE_nurb_copy(nu, newu, 1);
memcpy(newnu->bp, &nu->bp[starta], diffa * sizeof(BPoint));
if (newu != diffa) {
memcpy(&newnu->bp[diffa], nu->bp, cyclicu * sizeof(BPoint));
if (i == cu->actnu) {
calc_duplicate_actvert(
editnurb, newnurb, cu, 0, cyclicu, newu - cyclicu + cu->actvert);
}
cyclicu = 0;
}
if (newu != nu->pntsu) {
newnu->flagu &= ~CU_NURB_CYCLIC;
}
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
select_bpoint(bp1, SELECT, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
}
}
}
if (cyclicu != 0) {
if (i == cu->actnu) {
calc_duplicate_actvert(editnurb, newnurb, cu, 0, cyclicu, cu->actvert);
}
newnu = BKE_nurb_copy(nu, cyclicu, 1);
memcpy(newnu->bp, nu->bp, cyclicu * sizeof(BPoint));
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
select_bpoint(bp1, SELECT, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
}
}
else {
if (ED_curve_nurb_select_check(v3d, nu)) {
/* A rectangular area in nurb has to be selected and if splitting
* must be in U or V direction. */
usel = MEM_callocN(nu->pntsu, "adduplicateN3");
bp = nu->bp;
for (a = 0; a < nu->pntsv; a++) {
for (b = 0; b < nu->pntsu; b++, bp++) {
if (bp->f1 & flag) {
usel[b]++;
}
}
}
newu = 0;
newv = 0;
for (a = 0; a < nu->pntsu; a++) {
if (usel[a]) {
if (ELEM(newv, 0, usel[a])) {
newv = usel[a];
newu++;
}
else {
newv = 0;
break;
}
}
}
MEM_freeN(usel);
if ((newu == 0 || newv == 0) ||
(split && !isNurbselU(nu, &newv, SELECT) && !isNurbselV(nu, &newu, SELECT))) {
if (G.debug & G_DEBUG) {
printf("Can't duplicate Nurb\n");
}
}
else {
for (a = 0, bp1 = nu->bp; a < nu->pntsu * nu->pntsv; a++, bp1++) {
newv = newu = 0;
if ((bp1->f1 & flag) && !(bp1->f1 & SURF_SEEN)) {
/* point selected, now loop over points in U and V directions */
for (b = a % nu->pntsu, bp2 = bp1; b < nu->pntsu; b++, bp2++) {
if (bp2->f1 & flag) {
newu++;
for (c = a / nu->pntsu, bp3 = bp2; c < nu->pntsv; c++, bp3 += nu->pntsu) {
if (bp3->f1 & flag) {
/* flag as seen so skipped on future iterations */
bp3->f1 |= SURF_SEEN;
if (newu == 1) {
newv++;
}
}
else {
break;
}
}
}
else {
break;
}
}
}
if ((newu + newv) > 2) {
/* ignore single points */
if (a == 0) {
/* check if need to save cyclic selection and continue if so */
if (newu == nu->pntsu && (nu->flagv & CU_NURB_CYCLIC)) {
cyclicv = newv;
}
if (newv == nu->pntsv && (nu->flagu & CU_NURB_CYCLIC)) {
cyclicu = newu;
}
if (cyclicu != 0 || cyclicv != 0) {
continue;
}
}
if (a + newu == nu->pntsu && cyclicu != 0) {
/* cyclic in U direction */
newnu = BKE_nurb_copy(nu, newu + cyclicu, newv);
for (b = 0; b < newv; b++) {
memcpy(&newnu->bp[b * newnu->pntsu],
&nu->bp[b * nu->pntsu + a],
newu * sizeof(BPoint));
memcpy(&newnu->bp[b * newnu->pntsu + newu],
&nu->bp[b * nu->pntsu],
cyclicu * sizeof(BPoint));
}
if (cu->actnu == i) {
if (cu->actvert == -1) {
calc_duplicate_actnurb(editnurb, newnurb, cu);
}
else {
for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
starta = b * nu->pntsu + a;
if (calc_duplicate_actvert(editnurb,
newnurb,
cu,
cu->actvert,
starta,
cu->actvert % nu->pntsu + newu +
b * newnu->pntsu)) {
/* actvert in cyclicu selection */
break;
}
if (calc_duplicate_actvert(editnurb,
newnurb,
cu,
starta,
starta + newu,
cu->actvert - starta + b * newnu->pntsu)) {
/* actvert in 'current' iteration selection */
break;
}
}
}
}
cyclicu = cyclicv = 0;
}
else if ((a / nu->pntsu) + newv == nu->pntsv && cyclicv != 0) {
/* cyclic in V direction */
newnu = BKE_nurb_copy(nu, newu, newv + cyclicv);
memcpy(newnu->bp, &nu->bp[a], newu * newv * sizeof(BPoint));
memcpy(&newnu->bp[newu * newv], nu->bp, newu * cyclicv * sizeof(BPoint));
/* check for actvert in cyclicv selection */
if (cu->actnu == i) {
calc_duplicate_actvert(
editnurb, newnurb, cu, cu->actvert, a, (newu * newv) + cu->actvert);
}
cyclicu = cyclicv = 0;
}
else {
newnu = BKE_nurb_copy(nu, newu, newv);
for (b = 0; b < newv; b++) {
memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu + a], newu * sizeof(BPoint));
}
}
/* general case if not handled by cyclicu or cyclicv */
if (cu->actnu == i) {
if (cu->actvert == -1) {
calc_duplicate_actnurb(editnurb, newnurb, cu);
}
else {
for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
starta = b * nu->pntsu + a;
if (calc_duplicate_actvert(editnurb,
newnurb,
cu,
starta,
starta + newu,
cu->actvert - (a / nu->pntsu * nu->pntsu + diffa +
(starta % nu->pntsu)))) {
break;
}
}
}
}
BLI_addtail(newnurb, newnu);
if (newu != nu->pntsu) {
newnu->flagu &= ~CU_NURB_CYCLIC;
}
if (newv != nu->pntsv) {
newnu->flagv &= ~CU_NURB_CYCLIC;
}
}
}
if (cyclicu != 0 || cyclicv != 0) {
/* copy start of a cyclic surface, or copying all selected points */
newu = cyclicu == 0 ? nu->pntsu : cyclicu;
newv = cyclicv == 0 ? nu->pntsv : cyclicv;
newnu = BKE_nurb_copy(nu, newu, newv);
for (b = 0; b < newv; b++) {
memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu], newu * sizeof(BPoint));
}
/* Check for `actvert` in the unused cyclic-UV selection. */
if (cu->actnu == i) {
if (cu->actvert == -1) {
calc_duplicate_actnurb(editnurb, newnurb, cu);
}
else {
for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
starta = b * nu->pntsu;
if (calc_duplicate_actvert(editnurb,
newnurb,
cu,
starta,
starta + newu,
cu->actvert - (diffa + (starta % nu->pntsu)))) {
break;
}
}
}
}
BLI_addtail(newnurb, newnu);
if (newu != nu->pntsu) {
newnu->flagu &= ~CU_NURB_CYCLIC;
}
if (newv != nu->pntsv) {
newnu->flagv &= ~CU_NURB_CYCLIC;
}
}
for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
bp1->f1 &= ~SURF_SEEN;
if (!split) {
select_bpoint(bp1, DESELECT, flag, HIDDEN);
}
}
}
}
}
}
if (BLI_listbase_is_empty(newnurb) == false) {
LISTBASE_FOREACH (Nurb *, nu, newnurb) {
if (nu->type == CU_BEZIER) {
if (split) {
/* recalc first and last */
BKE_nurb_handle_calc_simple(nu, &nu->bezt[0]);
BKE_nurb_handle_calc_simple(nu, &nu->bezt[nu->pntsu - 1]);
}
}
else {
/* knots done after duplicate as pntsu may change */
BKE_nurb_order_clamp_u(nu);
BKE_nurb_knot_calc_u(nu);
if (obedit->type == OB_SURF) {
for (a = 0, bp = nu->bp; a < nu->pntsu * nu->pntsv; a++, bp++) {
bp->f1 &= ~SURF_SEEN;
}
BKE_nurb_order_clamp_v(nu);
BKE_nurb_knot_calc_v(nu);
}
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Switch Direction Operator
* \{ */
static int switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
EditNurb *editnurb = cu->editnurb;
int i = 0;
LISTBASE_FOREACH_INDEX (Nurb *, nu, &editnurb->nurbs, i) {
if (ED_curve_nurb_select_check(v3d, nu)) {
BKE_nurb_direction_switch(nu);
keyData_switchDirectionNurb(cu, nu);
if ((i == cu->actnu) && (cu->actvert != CU_ACT_NONE)) {
cu->actvert = (nu->pntsu - 1) - cu->actvert;
}
}
}
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
DEG_id_tag_update(obedit->data, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_switch_direction(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Switch Direction";
ot->description = "Switch direction of selected splines";
ot->idname = "CURVE_OT_switch_direction";
/* api callbacks */
ot->exec = switch_direction_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Set Weight Operator
* \{ */
static int set_goal_weight_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
BezTriple *bezt;
BPoint *bp;
float weight = RNA_float_get(op->ptr, "weight");
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->bezt) {
for (bezt = nu->bezt, a = 0; a < nu->pntsu; a++, bezt++) {
if (bezt->f2 & SELECT) {
bezt->weight = weight;
}
}
}
else if (nu->bp) {
for (bp = nu->bp, a = 0; a < nu->pntsu * nu->pntsv; a++, bp++) {
if (bp->f1 & SELECT) {
bp->weight = weight;
}
}
}
}
DEG_id_tag_update(obedit->data, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_spline_weight_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Goal Weight";
ot->description = "Set softbody goal weight for selected points";
ot->idname = "CURVE_OT_spline_weight_set";
/* api callbacks */
ot->exec = set_goal_weight_exec;
ot->invoke = WM_operator_props_popup;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_factor(ot->srna, "weight", 1.0f, 0.0f, 1.0f, "Weight", "", 0.0f, 1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Set Radius Operator
* \{ */
static int set_radius_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
BezTriple *bezt;
BPoint *bp;
float radius = RNA_float_get(op->ptr, "radius");
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->bezt) {
for (bezt = nu->bezt, a = 0; a < nu->pntsu; a++, bezt++) {
if (bezt->f2 & SELECT) {
bezt->radius = radius;
}
}
}
else if (nu->bp) {
for (bp = nu->bp, a = 0; a < nu->pntsu * nu->pntsv; a++, bp++) {
if (bp->f1 & SELECT) {
bp->radius = radius;
}
}
}
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_radius_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Curve Radius";
ot->description = "Set per-point radius which is used for bevel tapering";
ot->idname = "CURVE_OT_radius_set";
/* api callbacks */
ot->exec = set_radius_exec;
ot->invoke = WM_operator_props_popup;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float(
ot->srna, "radius", 1.0f, 0.0f, OBJECT_ADD_SIZE_MAXF, "Radius", "", 0.0001f, 10.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Vertices Operator
* \{ */
static void smooth_single_bezt(BezTriple *bezt,
const BezTriple *bezt_orig_prev,
const BezTriple *bezt_orig_next,
float factor)
{
BLI_assert(IN_RANGE_INCL(factor, 0.0f, 1.0f));
for (int i = 0; i < 3; i++) {
/* get single dimension pos of the mid handle */
float val_old = bezt->vec[1][i];
/* get the weights of the previous/next mid handles and calc offset */
float val_new = (bezt_orig_prev->vec[1][i] * 0.5f) + (bezt_orig_next->vec[1][i] * 0.5f);
float offset = (val_old * (1.0f - factor)) + (val_new * factor) - val_old;
/* offset midpoint and 2 handles */
bezt->vec[1][i] += offset;
bezt->vec[0][i] += offset;
bezt->vec[2][i] += offset;
}
}
/**
* Same as #smooth_single_bezt(), keep in sync.
*/
static void smooth_single_bp(BPoint *bp,
const BPoint *bp_orig_prev,
const BPoint *bp_orig_next,
float factor)
{
BLI_assert(IN_RANGE_INCL(factor, 0.0f, 1.0f));
for (int i = 0; i < 3; i++) {
float val_old, val_new, offset;
val_old = bp->vec[i];
val_new = (bp_orig_prev->vec[i] * 0.5f) + (bp_orig_next->vec[i] * 0.5f);
offset = (val_old * (1.0f - factor)) + (val_new * factor) - val_old;
bp->vec[i] += offset;
}
}
static int smooth_exec(bContext *C, wmOperator *UNUSED(op))
{
const float factor = 1.0f / 6.0f;
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
int a, a_end;
bool changed = false;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->bezt) {
/* duplicate the curve to use in weight calculation */
const BezTriple *bezt_orig = MEM_dupallocN(nu->bezt);
BezTriple *bezt;
changed = false;
/* check whether its cyclic or not, and set initial & final conditions */
if (nu->flagu & CU_NURB_CYCLIC) {
a = 0;
a_end = nu->pntsu;
}
else {
a = 1;
a_end = nu->pntsu - 1;
}
/* for all the curve points */
for (; a < a_end; a++) {
/* respect selection */
bezt = &nu->bezt[a];
if (bezt->f2 & SELECT) {
const BezTriple *bezt_orig_prev, *bezt_orig_next;
bezt_orig_prev = &bezt_orig[mod_i(a - 1, nu->pntsu)];
bezt_orig_next = &bezt_orig[mod_i(a + 1, nu->pntsu)];
smooth_single_bezt(bezt, bezt_orig_prev, bezt_orig_next, factor);
changed = true;
}
}
MEM_freeN((void *)bezt_orig);
if (changed) {
BKE_nurb_handles_calc(nu);
}
}
else if (nu->bp) {
/* Same as above, keep these the same! */
const BPoint *bp_orig = MEM_dupallocN(nu->bp);
BPoint *bp;
if (nu->flagu & CU_NURB_CYCLIC) {
a = 0;
a_end = nu->pntsu;
}
else {
a = 1;
a_end = nu->pntsu - 1;
}
for (; a < a_end; a++) {
bp = &nu->bp[a];
if (bp->f1 & SELECT) {
const BPoint *bp_orig_prev, *bp_orig_next;
bp_orig_prev = &bp_orig[mod_i(a - 1, nu->pntsu)];
bp_orig_next = &bp_orig[mod_i(a + 1, nu->pntsu)];
smooth_single_bp(bp, bp_orig_prev, bp_orig_next, factor);
}
}
MEM_freeN((void *)bp_orig);
}
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_smooth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth";
ot->description = "Flatten angles of selected points";
ot->idname = "CURVE_OT_smooth";
/* api callbacks */
ot->exec = smooth_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Operator (Radius/Weight/Tilt) Utilities
*
* To do:
* - Make smoothing distance based.
* - Support cyclic curves.
* \{ */
static void curve_smooth_value(ListBase *editnurb, const int bezt_offsetof, const int bp_offset)
{
BezTriple *bezt;
BPoint *bp;
int a;
/* use for smoothing */
int last_sel;
int start_sel, end_sel; /* selection indices, inclusive */
float start_rad, end_rad, fac, range;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->bezt) {
#define BEZT_VALUE(bezt) (*((float *)((char *)(bezt) + bezt_offsetof)))
for (last_sel = 0; last_sel < nu->pntsu; last_sel++) {
/* loop over selection segments of a curve, smooth each */
/* Start BezTriple code,
* this is duplicated below for points, make sure these functions stay in sync */
start_sel = -1;
for (bezt = &nu->bezt[last_sel], a = last_sel; a < nu->pntsu; a++, bezt++) {
if (bezt->f2 & SELECT) {
start_sel = a;
break;
}
}
/* in case there are no other selected verts */
end_sel = start_sel;
for (bezt = &nu->bezt[start_sel + 1], a = start_sel + 1; a < nu->pntsu; a++, bezt++) {
if ((bezt->f2 & SELECT) == 0) {
break;
}
end_sel = a;
}
if (start_sel == -1) {
last_sel = nu->pntsu; /* next... */
}
else {
last_sel = end_sel; /* before we modify it */
/* now blend between start and end sel */
start_rad = end_rad = FLT_MAX;
if (start_sel == end_sel) {
/* simple, only 1 point selected */
if (start_sel > 0) {
start_rad = BEZT_VALUE(&nu->bezt[start_sel - 1]);
}
if (end_sel != -1 && end_sel < nu->pntsu) {
end_rad = BEZT_VALUE(&nu->bezt[start_sel + 1]);
}
if (start_rad != FLT_MAX && end_rad >= FLT_MAX) {
BEZT_VALUE(&nu->bezt[start_sel]) = (start_rad + end_rad) / 2.0f;
}
else if (start_rad != FLT_MAX) {
BEZT_VALUE(&nu->bezt[start_sel]) = start_rad;
}
else if (end_rad != FLT_MAX) {
BEZT_VALUE(&nu->bezt[start_sel]) = end_rad;
}
}
else {
/* if endpoints selected, then use them */
if (start_sel == 0) {
start_rad = BEZT_VALUE(&nu->bezt[start_sel]);
start_sel++; /* we don't want to edit the selected endpoint */
}
else {
start_rad = BEZT_VALUE(&nu->bezt[start_sel - 1]);
}
if (end_sel == nu->pntsu - 1) {
end_rad = BEZT_VALUE(&nu->bezt[end_sel]);
end_sel--; /* we don't want to edit the selected endpoint */
}
else {
end_rad = BEZT_VALUE(&nu->bezt[end_sel + 1]);
}
/* Now Blend between the points */
range = (float)(end_sel - start_sel) + 2.0f;
for (bezt = &nu->bezt[start_sel], a = start_sel; a <= end_sel; a++, bezt++) {
fac = (float)(1 + a - start_sel) / range;
BEZT_VALUE(bezt) = start_rad * (1.0f - fac) + end_rad * fac;
}
}
}
}
#undef BEZT_VALUE
}
else if (nu->bp) {
#define BP_VALUE(bp) (*((float *)((char *)(bp) + bp_offset)))
/* Same as above, keep these the same! */
for (last_sel = 0; last_sel < nu->pntsu; last_sel++) {
/* loop over selection segments of a curve, smooth each */
/* Start BezTriple code,
* this is duplicated below for points, make sure these functions stay in sync */
start_sel = -1;
for (bp = &nu->bp[last_sel], a = last_sel; a < nu->pntsu; a++, bp++) {
if (bp->f1 & SELECT) {
start_sel = a;
break;
}
}
/* in case there are no other selected verts */
end_sel = start_sel;
for (bp = &nu->bp[start_sel + 1], a = start_sel + 1; a < nu->pntsu; a++, bp++) {
if ((bp->f1 & SELECT) == 0) {
break;
}
end_sel = a;
}
if (start_sel == -1) {
last_sel = nu->pntsu; /* next... */
}
else {
last_sel = end_sel; /* before we modify it */
/* now blend between start and end sel */
start_rad = end_rad = FLT_MAX;
if (start_sel == end_sel) {
/* simple, only 1 point selected */
if (start_sel > 0) {
start_rad = BP_VALUE(&nu->bp[start_sel - 1]);
}
if (end_sel != -1 && end_sel < nu->pntsu) {
end_rad = BP_VALUE(&nu->bp[start_sel + 1]);
}
if (start_rad != FLT_MAX && end_rad != FLT_MAX) {
BP_VALUE(&nu->bp[start_sel]) = (start_rad + end_rad) / 2;
}
else if (start_rad != FLT_MAX) {
BP_VALUE(&nu->bp[start_sel]) = start_rad;
}
else if (end_rad != FLT_MAX) {
BP_VALUE(&nu->bp[start_sel]) = end_rad;
}
}
else {
/* if endpoints selected, then use them */
if (start_sel == 0) {
start_rad = BP_VALUE(&nu->bp[start_sel]);
start_sel++; /* we don't want to edit the selected endpoint */
}
else {
start_rad = BP_VALUE(&nu->bp[start_sel - 1]);
}
if (end_sel == nu->pntsu - 1) {
end_rad = BP_VALUE(&nu->bp[end_sel]);
end_sel--; /* we don't want to edit the selected endpoint */
}
else {
end_rad = BP_VALUE(&nu->bp[end_sel + 1]);
}
/* Now Blend between the points */
range = (float)(end_sel - start_sel) + 2.0f;
for (bp = &nu->bp[start_sel], a = start_sel; a <= end_sel; a++, bp++) {
fac = (float)(1 + a - start_sel) / range;
BP_VALUE(bp) = start_rad * (1.0f - fac) + end_rad * fac;
}
}
}
}
#undef BP_VALUE
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Weight Operator
* \{ */
static int curve_smooth_weight_exec(bContext *C, wmOperator *UNUSED(op))
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
curve_smooth_value(editnurb, offsetof(BezTriple, weight), offsetof(BPoint, weight));
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_smooth_weight(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Curve Weight";
ot->description = "Interpolate weight of selected points";
ot->idname = "CURVE_OT_smooth_weight";
/* api callbacks */
ot->exec = curve_smooth_weight_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Radius Operator
* \{ */
static int curve_smooth_radius_exec(bContext *C, wmOperator *UNUSED(op))
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
curve_smooth_value(editnurb, offsetof(BezTriple, radius), offsetof(BPoint, radius));
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_smooth_radius(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Curve Radius";
ot->description = "Interpolate radii of selected points";
ot->idname = "CURVE_OT_smooth_radius";
/* api callbacks */
ot->exec = curve_smooth_radius_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Tilt Operator
* \{ */
static int curve_smooth_tilt_exec(bContext *C, wmOperator *UNUSED(op))
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
curve_smooth_value(editnurb, offsetof(BezTriple, tilt), offsetof(BPoint, tilt));
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_smooth_tilt(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Curve Tilt";
ot->description = "Interpolate tilt of selected points";
ot->idname = "CURVE_OT_smooth_tilt";
/* api callbacks */
ot->exec = curve_smooth_tilt_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hide Operator
* \{ */
static int hide_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const bool invert = RNA_boolean_get(op->ptr, "unselected");
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!(invert || ED_curve_select_check(v3d, cu->editnurb))) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
int a, sel;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
bezt = nu->bezt;
a = nu->pntsu;
sel = 0;
while (a--) {
if (invert == 0 && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
bezt->hide = 1;
}
else if (invert && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
bezt->hide = 1;
}
if (bezt->hide) {
sel++;
}
bezt++;
}
if (sel == nu->pntsu) {
nu->hide = 1;
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
sel = 0;
while (a--) {
if (invert == 0 && (bp->f1 & SELECT)) {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
bp->hide = 1;
}
else if (invert && (bp->f1 & SELECT) == 0) {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
bp->hide = 1;
}
if (bp->hide) {
sel++;
}
bp++;
}
if (sel == nu->pntsu * nu->pntsv) {
nu->hide = 1;
}
}
}
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
BKE_curve_nurb_vert_active_validate(obedit->data);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_hide(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hide Selected";
ot->idname = "CURVE_OT_hide";
ot->description = "Hide (un)selected control points";
/* api callbacks */
ot->exec = hide_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Reveal Operator
* \{ */
static int reveal_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool select = RNA_boolean_get(op->ptr, "select");
bool changed_multi = false;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
int a;
bool changed = false;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
nu->hide = 0;
if (nu->type == CU_BEZIER) {
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
if (bezt->hide) {
select_beztriple(bezt, select, SELECT, HIDDEN);
bezt->hide = 0;
changed = true;
}
bezt++;
}
}
else {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
if (bp->hide) {
select_bpoint(bp, select, SELECT, HIDDEN);
bp->hide = 0;
changed = true;
}
bp++;
}
}
}
if (changed) {
DEG_id_tag_update(obedit->data,
ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
changed_multi = true;
}
}
MEM_freeN(objects);
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void CURVE_OT_reveal(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reveal Hidden";
ot->idname = "CURVE_OT_reveal";
ot->description = "Reveal hidden control points";
/* api callbacks */
ot->exec = reveal_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "select", true, "Select", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subdivide Operator
* \{ */
/**
* Divide the line segments associated with the currently selected
* curve nodes (Bezier or NURB). If there are no valid segment
* selections within the current selection, nothing happens.
*/
static void subdividenurb(Object *obedit, View3D *v3d, int number_cuts)
{
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
BezTriple *bezt, *beztnew, *beztn;
BPoint *bp, *prevbp, *bpnew, *bpn;
float vec[15];
int a, b, sel, amount, *usel, *vsel;
float factor;
// printf("*** subdivideNurb: entering subdivide\n");
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
amount = 0;
if (nu->type == CU_BEZIER) {
BezTriple *nextbezt;
/*
* Insert a point into a 2D Bezier curve.
* Endpoints are preserved. Otherwise, all selected and inserted points are
* newly created. Old points are discarded.
*/
/* count */
a = nu->pntsu;
bezt = nu->bezt;
while (a--) {
nextbezt = BKE_nurb_bezt_get_next(nu, bezt);
if (nextbezt == NULL) {
break;
}
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nextbezt)) {
amount += number_cuts;
}
bezt++;
}
if (amount) {
/* insert */
beztnew = (BezTriple *)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb");
beztn = beztnew;
a = nu->pntsu;
bezt = nu->bezt;
while (a--) {
memcpy(beztn, bezt, sizeof(BezTriple));
keyIndex_updateBezt(editnurb, bezt, beztn, 1);
beztn++;
nextbezt = BKE_nurb_bezt_get_next(nu, bezt);
if (nextbezt == NULL) {
break;
}
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nextbezt)) {
float prevvec[3][3];
memcpy(prevvec, bezt->vec, sizeof(float[9]));
for (int i = 0; i < number_cuts; i++) {
factor = 1.0f / (number_cuts + 1 - i);
memcpy(beztn, nextbezt, sizeof(BezTriple));
/* midpoint subdividing */
interp_v3_v3v3(vec, prevvec[1], prevvec[2], factor);
interp_v3_v3v3(vec + 3, prevvec[2], nextbezt->vec[0], factor);
interp_v3_v3v3(vec + 6, nextbezt->vec[0], nextbezt->vec[1], factor);
interp_v3_v3v3(vec + 9, vec, vec + 3, factor);
interp_v3_v3v3(vec + 12, vec + 3, vec + 6, factor);
/* change handle of prev beztn */
copy_v3_v3((beztn - 1)->vec[2], vec);
/* new point */
copy_v3_v3(beztn->vec[0], vec + 9);
interp_v3_v3v3(beztn->vec[1], vec + 9, vec + 12, factor);
copy_v3_v3(beztn->vec[2], vec + 12);
/* handle of next bezt */
if (a == 0 && i == number_cuts - 1 && (nu->flagu & CU_NURB_CYCLIC)) {
copy_v3_v3(beztnew->vec[0], vec + 6);
}
else {
copy_v3_v3(nextbezt->vec[0], vec + 6);
}
beztn->radius = (bezt->radius + nextbezt->radius) / 2;
beztn->weight = (bezt->weight + nextbezt->weight) / 2;
memcpy(prevvec, beztn->vec, sizeof(float[9]));
beztn++;
}
}
bezt++;
}
MEM_freeN(nu->bezt);
nu->bezt = beztnew;
nu->pntsu += amount;
BKE_nurb_handles_calc(nu);
}
} /* End of 'if (nu->type == CU_BEZIER)' */
else if (nu->pntsv == 1) {
BPoint *nextbp;
/*
* All flat lines (ie. co-planar), except flat Nurbs. Flat NURB curves
* are handled together with the regular NURB plane division, as it
* should be. I split it off just now, let's see if it is
* stable... nzc 30-5-'00
*/
/* count */
a = nu->pntsu;
bp = nu->bp;
while (a--) {
nextbp = BKE_nurb_bpoint_get_next(nu, bp);
if (nextbp == NULL) {
break;
}
if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) {
amount += number_cuts;
}
bp++;
}
if (amount) {
/* insert */
bpnew = (BPoint *)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2");
bpn = bpnew;
a = nu->pntsu;
bp = nu->bp;
while (a--) {
/* Copy "old" point. */
memcpy(bpn, bp, sizeof(BPoint));
keyIndex_updateBP(editnurb, bp, bpn, 1);
bpn++;
nextbp = BKE_nurb_bpoint_get_next(nu, bp);
if (nextbp == NULL) {
break;
}
if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) {
// printf("*** subdivideNurb: insert 'linear' point\n");
for (int i = 0; i < number_cuts; i++) {
factor = (float)(i + 1) / (number_cuts + 1);
memcpy(bpn, nextbp, sizeof(BPoint));
interp_v4_v4v4(bpn->vec, bp->vec, nextbp->vec, factor);
bpn->radius = interpf(bp->radius, nextbp->radius, factor);
bpn++;
}
}
bp++;
}
MEM_freeN(nu->bp);
nu->bp = bpnew;
nu->pntsu += amount;
if (nu->type & CU_NURBS) {
BKE_nurb_knot_calc_u(nu);
}
}
} /* End of 'else if (nu->pntsv == 1)' */
else if (nu->type == CU_NURBS) {
/* This is a very strange test ... */
/**
* Subdivide NURB surfaces - nzc 30-5-'00 -
*
* Subdivision of a NURB curve can be effected by adding a
* control point (insertion of a knot), or by raising the
* degree of the functions used to build the NURB. The
* expression
*
* `degree = knots - controlpoints + 1` (J Walter piece)
* `degree = knots - controlpoints` (Blender implementation)
* ( this is confusing.... what is true? Another concern
* is that the JW piece allows the curve to become
* explicitly 1st order derivative discontinuous, while
* this is not what we want here... )
*
* is an invariant for a single NURB curve. Raising the degree
* of the NURB is done elsewhere; the degree is assumed
* constant during this operation. Degree is a property shared
* by all control-points in a curve (even though it is stored
* per control point - this can be misleading).
* Adding a knot is done by searching for the place in the
* knot vector where a certain knot value must be inserted, or
* by picking an appropriate knot value between two existing
* ones. The number of control-points that is influenced by the
* insertion depends on the order of the curve. A certain
* minimum number of knots is needed to form high-order
* curves, as can be seen from the equation above. In Blender,
* currently NURBs may be up to 6th order, so we modify at
* most 6 points. One point is added. For an n-degree curve,
* n points are discarded, and n+1 points inserted
* (so effectively, n points are modified). (that holds for
* the JW piece, but it seems not for our NURBs)
* In practice, the knot spacing is copied, but the tail
* (the points following the insertion point) need to be
* offset to keep the knot series ascending. The knot series
* is always a series of monotonically ascending integers in
* Blender. When not enough control points are available to
* fit the order, duplicates of the endpoints are added as
* needed.
*/
/* selection-arrays */
usel = MEM_callocN(sizeof(int) * nu->pntsu, "subivideNurb3");
vsel = MEM_callocN(sizeof(int) * nu->pntsv, "subivideNurb3");
sel = 0;
/* Count the number of selected points. */
bp = nu->bp;
for (a = 0; a < nu->pntsv; a++) {
for (b = 0; b < nu->pntsu; b++) {
if (bp->f1 & SELECT) {
usel[b]++;
vsel[a]++;
sel++;
}
bp++;
}
}
if (sel == (nu->pntsu * nu->pntsv)) { /* subdivide entire nurb */
/* Global subdivision is a special case of partial
* subdivision. Strange it is considered separately... */
/* count of nodes (after subdivision) along U axis */
int countu = nu->pntsu + (nu->pntsu - 1) * number_cuts;
/* total count of nodes after subdivision */
int tot = ((number_cuts + 1) * nu->pntsu - number_cuts) *
((number_cuts + 1) * nu->pntsv - number_cuts);
bpn = bpnew = MEM_mallocN(tot * sizeof(BPoint), "subdivideNurb4");
bp = nu->bp;
/* first subdivide rows */
for (a = 0; a < nu->pntsv; a++) {
for (b = 0; b < nu->pntsu; b++) {
*bpn = *bp;
keyIndex_updateBP(editnurb, bp, bpn, 1);
bpn++;
bp++;
if (b < nu->pntsu - 1) {
prevbp = bp - 1;
for (int i = 0; i < number_cuts; i++) {
factor = (float)(i + 1) / (number_cuts + 1);
*bpn = *bp;
interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
bpn++;
}
}
}
bpn += number_cuts * countu;
}
/* now insert new */
bpn = bpnew + ((number_cuts + 1) * nu->pntsu - number_cuts);
bp = bpnew + (number_cuts + 1) * ((number_cuts + 1) * nu->pntsu - number_cuts);
prevbp = bpnew;
for (a = 1; a < nu->pntsv; a++) {
for (b = 0; b < (number_cuts + 1) * nu->pntsu - number_cuts; b++) {
BPoint *tmp = bpn;
for (int i = 0; i < number_cuts; i++) {
factor = (float)(i + 1) / (number_cuts + 1);
*tmp = *bp;
interp_v4_v4v4(tmp->vec, prevbp->vec, bp->vec, factor);
tmp += countu;
}
bp++;
prevbp++;
bpn++;
}
bp += number_cuts * countu;
bpn += number_cuts * countu;
prevbp += number_cuts * countu;
}
MEM_freeN(nu->bp);
nu->bp = bpnew;
nu->pntsu = (number_cuts + 1) * nu->pntsu - number_cuts;
nu->pntsv = (number_cuts + 1) * nu->pntsv - number_cuts;
BKE_nurb_knot_calc_u(nu);
BKE_nurb_knot_calc_v(nu);
} /* End of 'if (sel == nu->pntsu * nu->pntsv)' (subdivide entire NURB) */
else {
/* subdivide in v direction? */
sel = 0;
for (a = 0; a < nu->pntsv - 1; a++) {
if (vsel[a] == nu->pntsu && vsel[a + 1] == nu->pntsu) {
sel += number_cuts;
}
}
if (sel) { /* V ! */
bpn = bpnew = MEM_mallocN((sel + nu->pntsv) * nu->pntsu * sizeof(BPoint),
"subdivideNurb4");
bp = nu->bp;
for (a = 0; a < nu->pntsv; a++) {
for (b = 0; b < nu->pntsu; b++) {
*bpn = *bp;
keyIndex_updateBP(editnurb, bp, bpn, 1);
bpn++;
bp++;
}
if ((a < nu->pntsv - 1) && vsel[a] == nu->pntsu && vsel[a + 1] == nu->pntsu) {
for (int i = 0; i < number_cuts; i++) {
factor = (float)(i + 1) / (number_cuts + 1);
prevbp = bp - nu->pntsu;
for (b = 0; b < nu->pntsu; b++) {
/*
* This simple bisection must be replaces by a
* subtle resampling of a number of points. Our
* task is made slightly easier because each
* point in our curve is a separate data
* node. (is it?)
*/
*bpn = *prevbp;
interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
bpn++;
prevbp++;
bp++;
}
bp -= nu->pntsu;
}
}
}
MEM_freeN(nu->bp);
nu->bp = bpnew;
nu->pntsv += sel;
BKE_nurb_knot_calc_v(nu);
}
else {
/* or in u direction? */
sel = 0;
for (a = 0; a < nu->pntsu - 1; a++) {
if (usel[a] == nu->pntsv && usel[a + 1] == nu->pntsv) {
sel += number_cuts;
}
}
if (sel) { /* U ! */
/* Inserting U points is sort of 'default' Flat curves only get */
/* U points inserted in them. */
bpn = bpnew = MEM_mallocN((sel + nu->pntsu) * nu->pntsv * sizeof(BPoint),
"subdivideNurb4");
bp = nu->bp;
for (a = 0; a < nu->pntsv; a++) {
for (b = 0; b < nu->pntsu; b++) {
*bpn = *bp;
keyIndex_updateBP(editnurb, bp, bpn, 1);
bpn++;
bp++;
if ((b < nu->pntsu - 1) && usel[b] == nu->pntsv && usel[b + 1] == nu->pntsv) {
/*
* One thing that bugs me here is that the
* orders of things are not the same as in
* the JW piece. Also, this implies that we
* handle at most 3rd order curves? I miss
* some symmetry here...
*/
for (int i = 0; i < number_cuts; i++) {
factor = (float)(i + 1) / (number_cuts + 1);
prevbp = bp - 1;
*bpn = *prevbp;
interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor);
bpn++;
}
}
}
}
MEM_freeN(nu->bp);
nu->bp = bpnew;
nu->pntsu += sel;
BKE_nurb_knot_calc_u(nu); /* shift knots forward */
}
}
}
MEM_freeN(usel);
MEM_freeN(vsel);
} /* End of 'if (nu->type == CU_NURBS)' */
}
}
static int subdivide_exec(bContext *C, wmOperator *op)
{
const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
subdividenurb(obedit, v3d, number_cuts);
if (ED_curve_updateAnimPaths(bmain, cu)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, cu);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_subdivide(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Subdivide";
ot->description = "Subdivide selected segments";
ot->idname = "CURVE_OT_subdivide";
/* api callbacks */
ot->exec = subdivide_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
/* Avoid re-using last var because it can cause _very_ high poly meshes
* and annoy users (or worse crash). */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Set Spline Type Operator
* \{ */
static int set_spline_type_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
int ret_value = OPERATOR_CANCELLED;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C);
ListBase *editnurb = object_editcurve_get(obedit);
bool changed = false;
bool changed_size = false;
const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
const int type = RNA_enum_get(op->ptr, "type");
if (ELEM(type, CU_CARDINAL, CU_BSPLINE)) {
BKE_report(op->reports, RPT_ERROR, "Not yet implemented");
continue;
}
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (ED_curve_nurb_select_check(v3d, nu)) {
const int pntsu_prev = nu->pntsu;
const char *err_msg = NULL;
if (BKE_nurb_type_convert(nu, type, use_handles, &err_msg)) {
changed = true;
if (pntsu_prev != nu->pntsu) {
changed_size = true;
}
}
else {
BKE_report(op->reports, RPT_ERROR, err_msg);
}
}
}
if (changed) {
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
DEG_id_tag_update(obedit->data, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
if (changed_size) {
Curve *cu = obedit->data;
cu->actvert = CU_ACT_NONE;
}
ret_value = OPERATOR_FINISHED;
}
}
MEM_freeN(objects);
return ret_value;
}
void CURVE_OT_spline_type_set(wmOperatorType *ot)
{
static const EnumPropertyItem type_items[] = {
{CU_POLY, "POLY", 0, "Poly", ""},
{CU_BEZIER, "BEZIER", 0, "Bezier", ""},
// {CU_CARDINAL, "CARDINAL", 0, "Cardinal", ""},
// {CU_BSPLINE, "B_SPLINE", 0, "B-Spline", ""},
{CU_NURBS, "NURBS", 0, "NURBS", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Set Spline Type";
ot->description = "Set type of active spline";
ot->idname = "CURVE_OT_spline_type_set";
/* api callbacks */
ot->exec = set_spline_type_exec;
ot->invoke = WM_menu_invoke;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type");
RNA_def_boolean(ot->srna,
"use_handles",
0,
"Handles",
"Use handles when converting bezier curves into polygons");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Set Handle Type Operator
* \{ */
static int set_handle_type_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const int handle_type = RNA_enum_get(op->ptr, "type");
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
BKE_nurbList_handles_set(editnurb, handle_type);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_handle_type_set(wmOperatorType *ot)
{
/* keep in sync with graphkeys_handle_type_items */
static const EnumPropertyItem editcurve_handle_type_items[] = {
{HD_AUTO, "AUTOMATIC", 0, "Automatic", ""},
{HD_VECT, "VECTOR", 0, "Vector", ""},
{5, "ALIGNED", 0, "Aligned", ""},
{6, "FREE_ALIGN", 0, "Free", ""},
{3, "TOGGLE_FREE_ALIGN", 0, "Toggle Free/Align", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Set Handle Type";
ot->description = "Set type of handles for selected control points";
ot->idname = "CURVE_OT_handle_type_set";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = set_handle_type_exec;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Recalculate Handles Operator
* \{ */
static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const bool calc_length = RNA_boolean_get(op->ptr, "calc_length");
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
BKE_nurbList_handles_recalculate(editnurb, calc_length, SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_normals_make_consistent(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Recalculate Handles";
ot->description = "Recalculate the direction of selected handles";
ot->idname = "CURVE_OT_normals_make_consistent";
/* api callbacks */
ot->exec = curve_normals_make_consistent_exec;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "calc_length", false, "Length", "Recalculate handle length");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Make Segment Operator
*
* Also handles skinning & lofting.
* \{ */
static void switchdirection_knots(float *base, int tot)
{
float *fp1, *fp2, *tempf;
int a;
if (base == NULL || tot == 0) {
return;
}
/* reverse knots */
a = tot;
fp1 = base;
fp2 = fp1 + (a - 1);
a /= 2;
while (fp1 != fp2 && a > 0) {
SWAP(float, *fp1, *fp2);
a--;
fp1++;
fp2--;
}
/* and make in increasing order again */
a = tot - 1;
fp1 = base;
fp2 = tempf = MEM_mallocN(sizeof(float) * tot, "switchdirect");
while (a--) {
fp2[0] = fabsf(fp1[1] - fp1[0]);
fp1++;
fp2++;
}
fp2[0] = 0.0f;
a = tot - 1;
fp1 = base;
fp2 = tempf;
fp1[0] = 0.0;
fp1++;
while (a--) {
fp1[0] = fp1[-1] + fp2[0];
fp1++;
fp2++;
}
MEM_freeN(tempf);
}
static void rotate_direction_nurb(Nurb *nu)
{
BPoint *bp1, *bp2, *temp;
int u, v;
SWAP(int, nu->pntsu, nu->pntsv);
SWAP(short, nu->orderu, nu->orderv);
SWAP(short, nu->resolu, nu->resolv);
SWAP(short, nu->flagu, nu->flagv);
SWAP(float *, nu->knotsu, nu->knotsv);
switchdirection_knots(nu->knotsv, KNOTSV(nu));
temp = MEM_dupallocN(nu->bp);
bp1 = nu->bp;
for (v = 0; v < nu->pntsv; v++) {
for (u = 0; u < nu->pntsu; u++, bp1++) {
bp2 = temp + (nu->pntsu - u - 1) * (nu->pntsv) + v;
*bp1 = *bp2;
}
}
MEM_freeN(temp);
}
static bool is_u_selected(Nurb *nu, int u)
{
BPoint *bp;
int v;
/* what about resolu == 2? */
bp = &nu->bp[u];
for (v = 0; v < nu->pntsv - 1; v++, bp += nu->pntsu) {
if ((v != 0) && (bp->f1 & SELECT)) {
return true;
}
}
return false;
}
typedef struct NurbSort {
struct NurbSort *next, *prev;
Nurb *nu;
float vec[3];
} NurbSort;
static void make_selection_list_nurb(View3D *v3d, ListBase *editnurb, ListBase *nsortbase)
{
ListBase nbase = {NULL, NULL};
NurbSort *nus, *nustest, *headdo, *taildo;
BPoint *bp;
float dist, headdist, taildist;
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (ED_curve_nurb_select_check(v3d, nu)) {
nus = (NurbSort *)MEM_callocN(sizeof(NurbSort), "sort");
BLI_addhead(&nbase, nus);
nus->nu = nu;
bp = nu->bp;
a = nu->pntsu;
while (a--) {
add_v3_v3(nus->vec, bp->vec);
bp++;
}
mul_v3_fl(nus->vec, 1.0f / (float)nu->pntsu);
}
}
/* just add the first one */
nus = nbase.first;
BLI_remlink(&nbase, nus);
BLI_addtail(nsortbase, nus);
/* now add, either at head or tail, the closest one */
while (nbase.first) {
headdist = taildist = 1.0e30;
headdo = taildo = NULL;
nustest = nbase.first;
while (nustest) {
dist = len_v3v3(nustest->vec, ((NurbSort *)nsortbase->first)->vec);
if (dist < headdist) {
headdist = dist;
headdo = nustest;
}
dist = len_v3v3(nustest->vec, ((NurbSort *)nsortbase->last)->vec);
if (dist < taildist) {
taildist = dist;
taildo = nustest;
}
nustest = nustest->next;
}
if (headdist < taildist) {
BLI_remlink(&nbase, headdo);
BLI_addhead(nsortbase, headdo);
}
else {
BLI_remlink(&nbase, taildo);
BLI_addtail(nsortbase, taildo);
}
}
}
enum {
CURVE_MERGE_OK = 0,
CURVE_MERGE_ERR_FEW_SELECTION,
CURVE_MERGE_ERR_RESOLUTION_ALL,
CURVE_MERGE_ERR_RESOLUTION_SOME,
};
static bool merge_2_nurb(Curve *cu, ListBase *editnurb, Nurb *nu1, Nurb *nu2)
{
BPoint *bp, *bp1, *bp2, *temp;
float len1, len2;
int origu, u, v;
/* first nurbs will be changed to make u = resolu-1 selected */
/* 2nd nurbs will be changed to make u = 0 selected */
/* first nurbs: u = resolu-1 selected */
if (is_u_selected(nu1, nu1->pntsu - 1)) {
/* pass */
}
else {
/* For 2D curves blender uses (orderv = 0). It doesn't make any sense mathematically. */
/* but after rotating (orderu = 0) will be confusing. */
if (nu1->orderv == 0) {
nu1->orderv = 1;
}
rotate_direction_nurb(nu1);
if (is_u_selected(nu1, nu1->pntsu - 1)) {
/* pass */
}
else {
rotate_direction_nurb(nu1);
if (is_u_selected(nu1, nu1->pntsu - 1)) {
/* pass */
}
else {
rotate_direction_nurb(nu1);
if (is_u_selected(nu1, nu1->pntsu - 1)) {
/* pass */
}
else {
/* rotate again, now its OK! */
if (nu1->pntsv != 1) {
rotate_direction_nurb(nu1);
}
return true;
}
}
}
}
/* 2nd nurbs: u = 0 selected */
if (is_u_selected(nu2, 0)) {
/* pass */
}
else {
if (nu2->orderv == 0) {
nu2->orderv = 1;
}
rotate_direction_nurb(nu2);
if (is_u_selected(nu2, 0)) {
/* pass */
}
else {
rotate_direction_nurb(nu2);
if (is_u_selected(nu2, 0)) {
/* pass */
}
else {
rotate_direction_nurb(nu2);
if (is_u_selected(nu2, 0)) {
/* pass */
}
else {
/* rotate again, now its OK! */
if (nu1->pntsu == 1) {
rotate_direction_nurb(nu1);
}
if (nu2->pntsv != 1) {
rotate_direction_nurb(nu2);
}
return true;
}
}
}
}
if (nu1->pntsv != nu2->pntsv) {
return false;
}
/* ok, now nu1 has the rightmost column and nu2 the leftmost column selected */
/* maybe we need a 'v' flip of nu2? */
bp1 = &nu1->bp[nu1->pntsu - 1];
bp2 = nu2->bp;
len1 = 0.0;
for (v = 0; v < nu1->pntsv; v++, bp1 += nu1->pntsu, bp2 += nu2->pntsu) {
len1 += len_v3v3(bp1->vec, bp2->vec);
}
bp1 = &nu1->bp[nu1->pntsu - 1];
bp2 = &nu2->bp[nu2->pntsu * (nu2->pntsv - 1)];
len2 = 0.0;
for (v = 0; v < nu1->pntsv; v++, bp1 += nu1->pntsu, bp2 -= nu2->pntsu) {
len2 += len_v3v3(bp1->vec, bp2->vec);
}
/* merge */
origu = nu1->pntsu;
nu1->pntsu += nu2->pntsu;
if (nu1->orderu < 3 && nu1->orderu < nu1->pntsu) {
nu1->orderu++;
}
if (nu1->orderv < 3 && nu1->orderv < nu1->pntsv) {
nu1->orderv++;
}
temp = nu1->bp;
nu1->bp = MEM_mallocN(nu1->pntsu * nu1->pntsv * sizeof(BPoint), "mergeBP");
bp = nu1->bp;
bp1 = temp;
for (v = 0; v < nu1->pntsv; v++) {
/* switch direction? */
if (len1 < len2) {
bp2 = &nu2->bp[v * nu2->pntsu];
}
else {
bp2 = &nu2->bp[(nu1->pntsv - v - 1) * nu2->pntsu];
}
for (u = 0; u < nu1->pntsu; u++, bp++) {
if (u < origu) {
keyIndex_updateBP(cu->editnurb, bp1, bp, 1);
*bp = *bp1;
bp1++;
select_bpoint(bp, SELECT, SELECT, HIDDEN);
}
else {
keyIndex_updateBP(cu->editnurb, bp2, bp, 1);
*bp = *bp2;
bp2++;
}
}
}
if (nu1->type == CU_NURBS) {
/* merge knots */
BKE_nurb_knot_calc_u(nu1);
/* make knots, for merged curved for example */
BKE_nurb_knot_calc_v(nu1);
}
MEM_freeN(temp);
BLI_remlink(editnurb, nu2);
BKE_nurb_free(nu2);
return true;
}
static int merge_nurb(View3D *v3d, Object *obedit)
{
Curve *cu = obedit->data;
ListBase *editnurb = object_editcurve_get(obedit);
NurbSort *nus1, *nus2;
bool ok = true;
ListBase nsortbase = {NULL, NULL};
make_selection_list_nurb(v3d, editnurb, &nsortbase);
if (nsortbase.first == nsortbase.last) {
BLI_freelistN(&nsortbase);
return CURVE_MERGE_ERR_FEW_SELECTION;
}
nus1 = nsortbase.first;
nus2 = nus1->next;
/* resolution match, to avoid uv rotations */
if (nus1->nu->pntsv == 1) {
if (ELEM(nus1->nu->pntsu, nus2->nu->pntsu, nus2->nu->pntsv)) {
/* pass */
}
else {
ok = false;
}
}
else if (nus2->nu->pntsv == 1) {
if (ELEM(nus2->nu->pntsu, nus1->nu->pntsu, nus1->nu->pntsv)) {
/* pass */
}
else {
ok = false;
}
}
else if (nus1->nu->pntsu == nus2->nu->pntsu || nus1->nu->pntsv == nus2->nu->pntsv) {
/* pass */
}
else if (nus1->nu->pntsu == nus2->nu->pntsv || nus1->nu->pntsv == nus2->nu->pntsu) {
/* pass */
}
else {
ok = false;
}
if (ok == false) {
BLI_freelistN(&nsortbase);
return CURVE_MERGE_ERR_RESOLUTION_ALL;
}
while (nus2) {
/* There is a change a few curves merged properly, but not all.
* In this case we still update the curve, yet report the error. */
ok &= merge_2_nurb(cu, editnurb, nus1->nu, nus2->nu);
nus2 = nus2->next;
}
BLI_freelistN(&nsortbase);
BKE_curve_nurb_active_set(obedit->data, NULL);
return ok ? CURVE_MERGE_OK : CURVE_MERGE_ERR_RESOLUTION_SOME;
}
static int make_segment_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
struct {
int changed;
int unselected;
int error_selected_few;
int error_resolution;
int error_generic;
} status = {0};
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
status.unselected++;
continue;
}
ListBase *nubase = object_editcurve_get(obedit);
Nurb *nu, *nu1 = NULL, *nu2 = NULL;
BPoint *bp;
bool ok = false;
/* first decide if this is a surface merge! */
if (obedit->type == OB_SURF) {
nu = nubase->first;
}
else {
nu = NULL;
}
while (nu) {
const int nu_select_num = ED_curve_nurb_select_count(v3d, nu);
if (nu_select_num) {
if (nu->pntsu > 1 && nu->pntsv > 1) {
break;
}
if (nu_select_num > 1) {
break;
}
/* only 1 selected, not first or last, a little complex, but intuitive */
if (nu->pntsv == 1) {
if ((nu->bp->f1 & SELECT) || (nu->bp[nu->pntsu - 1].f1 & SELECT)) {
/* pass */
}
else {
break;
}
}
}
nu = nu->next;
}
if (nu) {
int merge_result = merge_nurb(v3d, obedit);
switch (merge_result) {
case CURVE_MERGE_OK:
status.changed++;
goto curve_merge_tag_object;
case CURVE_MERGE_ERR_RESOLUTION_SOME:
status.error_resolution++;
goto curve_merge_tag_object;
case CURVE_MERGE_ERR_FEW_SELECTION:
status.error_selected_few++;
break;
case CURVE_MERGE_ERR_RESOLUTION_ALL:
status.error_resolution++;
break;
}
continue;
}
/* find both nurbs and points, nu1 will be put behind nu2 */
for (nu = nubase->first; nu; nu = nu->next) {
if (nu->pntsu == 1) {
nu->flagu &= ~CU_NURB_CYCLIC;
}
if ((nu->flagu & CU_NURB_CYCLIC) == 0) { /* not cyclic */
if (nu->type == CU_BEZIER) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &(nu->bezt[nu->pntsu - 1]))) {
/* Last point is selected, preferred for nu2 */
if (nu2 == NULL) {
nu2 = nu;
}
else if (nu1 == NULL) {
nu1 = nu;
/* Just in case both of first/last CV are selected check
* whether we really need to switch the direction.
*/
if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu1->bezt)) {
BKE_nurb_direction_switch(nu1);
keyData_switchDirectionNurb(cu, nu1);
}
}
}
else if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu->bezt)) {
/* First point is selected, preferred for nu1 */
if (nu1 == NULL) {
nu1 = nu;
}
else if (nu2 == NULL) {
nu2 = nu;
/* Just in case both of first/last CV are selected check
* whether we really need to switch the direction.
*/
if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &(nu->bezt[nu2->pntsu - 1]))) {
BKE_nurb_direction_switch(nu2);
keyData_switchDirectionNurb(cu, nu2);
}
}
}
}
else if (nu->pntsv == 1) {
/* Same logic as above: if first point is selected spline is
* preferred for nu1, if last point is selected spline is
* preferred for u2u.
*/
bp = nu->bp;
if (bp[nu->pntsu - 1].f1 & SELECT) {
if (nu2 == NULL) {
nu2 = nu;
}
else if (nu1 == NULL) {
nu1 = nu;
if ((bp->f1 & SELECT) == 0) {
BKE_nurb_direction_switch(nu);
keyData_switchDirectionNurb(cu, nu);
}
}
}
else if (bp->f1 & SELECT) {
if (nu1 == NULL) {
nu1 = nu;
}
else if (nu2 == NULL) {
nu2 = nu;
if ((bp[nu->pntsu - 1].f1 & SELECT) == 0) {
BKE_nurb_direction_switch(nu);
keyData_switchDirectionNurb(cu, nu);
}
}
}
}
}
if (nu1 && nu2) {
/* Got second spline, no need to loop over rest of the splines. */
break;
}
}
if ((nu1 && nu2) && (nu1 != nu2)) {
if (nu1->type == nu2->type) {
if (nu1->type == CU_BEZIER) {
BezTriple *bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + nu2->pntsu) * sizeof(BezTriple),
"addsegmentN");
ED_curve_beztcpy(cu->editnurb, bezt, nu2->bezt, nu2->pntsu);
ED_curve_beztcpy(cu->editnurb, bezt + nu2->pntsu, nu1->bezt, nu1->pntsu);
MEM_freeN(nu1->bezt);
nu1->bezt = bezt;
nu1->pntsu += nu2->pntsu;
BLI_remlink(nubase, nu2);
keyIndex_delNurb(cu->editnurb, nu2);
BKE_nurb_free(nu2);
nu2 = NULL;
BKE_nurb_handles_calc(nu1);
}
else {
bp = (BPoint *)MEM_mallocN((nu1->pntsu + nu2->pntsu) * sizeof(BPoint), "addsegmentN2");
ED_curve_bpcpy(cu->editnurb, bp, nu2->bp, nu2->pntsu);
ED_curve_bpcpy(cu->editnurb, bp + nu2->pntsu, nu1->bp, nu1->pntsu);
MEM_freeN(nu1->bp);
nu1->bp = bp;
/* a = nu1->pntsu + nu1->orderu; */ /* UNUSED */
nu1->pntsu += nu2->pntsu;
BLI_remlink(nubase, nu2);
/* now join the knots */
if (nu1->type == CU_NURBS) {
if (nu1->knotsu != NULL) {
MEM_freeN(nu1->knotsu);
nu1->knotsu = NULL;
}
BKE_nurb_knot_calc_u(nu1);
}
keyIndex_delNurb(cu->editnurb, nu2);
BKE_nurb_free(nu2);
nu2 = NULL;
}
BKE_curve_nurb_active_set(cu, nu1); /* for selected */
ok = true;
}
}
else if ((nu1 && !nu2) || (!nu1 && nu2)) {
if (nu2) {
SWAP(Nurb *, nu1, nu2);
}
if (!(nu1->flagu & CU_NURB_CYCLIC) && nu1->pntsu > 1) {
if (nu1->type == CU_BEZIER && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, nu1->bezt) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu1->bezt[nu1->pntsu - 1])) {
nu1->flagu |= CU_NURB_CYCLIC;
BKE_nurb_handles_calc(nu1);
ok = true;
}
else if (ELEM(nu1->type, CU_NURBS, CU_POLY) && nu1->bp->f1 & SELECT &&
(nu1->bp[nu1->pntsu - 1].f1 & SELECT)) {
nu1->flagu |= CU_NURB_CYCLIC;
BKE_nurb_knot_calc_u(nu1);
ok = true;
}
}
}
if (!ok) {
status.error_generic++;
continue;
}
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
status.changed++;
curve_merge_tag_object:
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
if (status.unselected == objects_len) {
BKE_report(op->reports, RPT_ERROR, "No points were selected");
return OPERATOR_CANCELLED;
}
const int tot_errors = status.error_selected_few + status.error_resolution +
status.error_generic;
if (tot_errors > 0) {
/* Some curves changed, but some curves failed: don't explain why it failed. */
if (status.changed) {
BKE_reportf(op->reports,
RPT_INFO,
tot_errors == 1 ? "%d curve could not make segments" :
"%d curves could not make segments",
tot_errors);
return OPERATOR_FINISHED;
}
/* All curves failed: If there is more than one error give a generic error report. */
if (((status.error_selected_few ? 1 : 0) + (status.error_resolution ? 1 : 0) +
(status.error_generic ? 1 : 0)) > 1) {
BKE_report(op->reports, RPT_ERROR, "Could not make new segments");
}
/* All curves failed due to the same error. */
if (status.error_selected_few) {
BKE_report(op->reports, RPT_ERROR, "Too few selections to merge");
}
else if (status.error_resolution) {
BKE_report(op->reports, RPT_ERROR, "Resolution does not match");
}
else {
BLI_assert(status.error_generic);
BKE_report(op->reports, RPT_ERROR, "Cannot make segment");
}
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void CURVE_OT_make_segment(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Make Segment";
ot->idname = "CURVE_OT_make_segment";
ot->description = "Join two curves by their selected ends";
/* api callbacks */
ot->exec = make_segment_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pick Select from 3D View
* \{ */
bool ED_curve_editnurb_select_pick(
bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
Nurb *nu;
BezTriple *bezt = NULL;
BPoint *bp = NULL;
Base *basact = NULL;
short hand;
view3d_operator_needs_opengl(C);
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_v2_v2_int(vc.mval, mval);
if (ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact)) {
Object *obedit = basact->object;
Curve *cu = obedit->data;
ListBase *editnurb = object_editcurve_get(obedit);
const void *vert = BKE_curve_vert_active_get(cu);
if (!extend && !deselect && !toggle) {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
ED_curve_deselect_all(((Curve *)ob_iter->data)->editnurb);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
MEM_freeN(objects);
}
if (extend) {
if (bezt) {
if (hand == 1) {
select_beztriple(bezt, SELECT, SELECT, HIDDEN);
}
else {
if (hand == 0) {
bezt->f1 |= SELECT;
}
else {
bezt->f3 |= SELECT;
}
}
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
else {
select_bpoint(bp, SELECT, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
}
else if (deselect) {
if (bezt) {
if (hand == 1) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
}
}
else if (hand == 0) {
bezt->f1 &= ~SELECT;
}
else {
bezt->f3 &= ~SELECT;
}
}
else {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
if (bp == vert) {
cu->actvert = CU_ACT_NONE;
}
}
}
else if (toggle) {
if (bezt) {
if (hand == 1) {
if (bezt->f2 & SELECT) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
}
}
else {
select_beztriple(bezt, SELECT, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
}
else if (hand == 0) {
bezt->f1 ^= SELECT;
}
else {
bezt->f3 ^= SELECT;
}
}
else {
if (bp->f1 & SELECT) {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
if (bp == vert) {
cu->actvert = CU_ACT_NONE;
}
}
else {
select_bpoint(bp, SELECT, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
}
}
else {
BKE_nurbList_flag_set(editnurb, SELECT, false);
if (bezt) {
if (hand == 1) {
select_beztriple(bezt, SELECT, SELECT, HIDDEN);
}
else {
if (hand == 0) {
bezt->f1 |= SELECT;
}
else {
bezt->f3 |= SELECT;
}
}
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
else {
select_bpoint(bp, SELECT, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
}
if (nu != BKE_curve_nurb_active_get(cu)) {
cu->actvert = CU_ACT_NONE;
BKE_curve_nurb_active_set(cu, nu);
}
if (vc.view_layer->basact != basact) {
ED_object_base_activate(C, basact);
}
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
return true;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Spin Operator
* \{ */
/* 'cent' is in object space and 'dvec' in worldspace.
*/
bool ed_editnurb_spin(
float viewmat[4][4], View3D *v3d, Object *obedit, const float axis[3], const float cent[3])
{
Curve *cu = (Curve *)obedit->data;
ListBase *editnurb = object_editcurve_get(obedit);
float cmat[3][3], tmat[3][3], imat[3][3];
float bmat[3][3], rotmat[3][3], scalemat1[3][3], scalemat2[3][3];
float persmat[3][3], persinv[3][3];
bool ok, changed = false;
int a;
copy_m3_m4(persmat, viewmat);
invert_m3_m3(persinv, persmat);
/* imat and center and size */
copy_m3_m4(bmat, obedit->obmat);
invert_m3_m3(imat, bmat);
axis_angle_to_mat3(cmat, axis, M_PI / 4.0);
mul_m3_m3m3(tmat, cmat, bmat);
mul_m3_m3m3(rotmat, imat, tmat);
unit_m3(scalemat1);
scalemat1[0][0] = M_SQRT2;
scalemat1[1][1] = M_SQRT2;
mul_m3_m3m3(tmat, persmat, bmat);
mul_m3_m3m3(cmat, scalemat1, tmat);
mul_m3_m3m3(tmat, persinv, cmat);
mul_m3_m3m3(scalemat1, imat, tmat);
unit_m3(scalemat2);
scalemat2[0][0] /= (float)M_SQRT2;
scalemat2[1][1] /= (float)M_SQRT2;
mul_m3_m3m3(tmat, persmat, bmat);
mul_m3_m3m3(cmat, scalemat2, tmat);
mul_m3_m3m3(tmat, persinv, cmat);
mul_m3_m3m3(scalemat2, imat, tmat);
ok = true;
for (a = 0; a < 7; a++) {
ok = ed_editnurb_extrude_flag(cu->editnurb, SELECT);
if (ok == false) {
return changed;
}
changed = true;
rotateflagNurb(editnurb, SELECT, cent, rotmat);
if ((a & 1) == 0) {
rotateflagNurb(editnurb, SELECT, cent, scalemat1);
weightflagNurb(editnurb, SELECT, 0.25 * M_SQRT2);
}
else {
rotateflagNurb(editnurb, SELECT, cent, scalemat2);
weightflagNurb(editnurb, SELECT, 4.0 / M_SQRT2);
}
}
if (ok) {
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (ED_curve_nurb_select_check(v3d, nu)) {
nu->orderv = 4;
nu->flagv |= CU_NURB_CYCLIC;
BKE_nurb_knot_calc_v(nu);
}
}
}
return changed;
}
static int spin_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = ED_view3d_context_rv3d(C);
float cent[3], axis[3], viewmat[4][4];
int ok = -1;
RNA_float_get_array(op->ptr, "center", cent);
RNA_float_get_array(op->ptr, "axis", axis);
if (rv3d) {
copy_m4_m4(viewmat, rv3d->viewmat);
}
else {
unit_m4(viewmat);
}
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
invert_m4_m4(obedit->imat, obedit->obmat);
mul_m4_v3(obedit->imat, cent);
if (!ed_editnurb_spin(viewmat, v3d, obedit, axis, cent)) {
ok = MAX2(ok, 0);
continue;
}
ok = 1;
if (ED_curve_updateAnimPaths(bmain, cu)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
if (ok == 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot spin");
}
return OPERATOR_FINISHED;
}
static int spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Scene *scene = CTX_data_scene(C);
RegionView3D *rv3d = ED_view3d_context_rv3d(C);
float axis[3] = {0.0f, 0.0f, 1.0f};
if (rv3d) {
copy_v3_v3(axis, rv3d->viewinv[2]);
}
RNA_float_set_array(op->ptr, "center", scene->cursor.location);
RNA_float_set_array(op->ptr, "axis", axis);
return spin_exec(C, op);
}
void CURVE_OT_spin(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Spin";
ot->idname = "CURVE_OT_spin";
ot->description = "Extrude selected boundary row around pivot point and current view axis";
/* api callbacks */
ot->exec = spin_exec;
ot->invoke = spin_invoke;
ot->poll = ED_operator_editsurf;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_float_vector_xyz(ot->srna,
"center",
3,
NULL,
-OBJECT_ADD_SIZE_MAXF,
OBJECT_ADD_SIZE_MAXF,
"Center",
"Center in global view space",
-1000.0f,
1000.0f);
RNA_def_float_vector(
ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Extrude Vertex Operator
* \{ */
static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb, View3D *v3d)
{
bool changed = false;
Nurb *cu_actnu;
union {
BezTriple *bezt;
BPoint *bp;
void *p;
} cu_actvert;
if (BLI_listbase_is_empty(&editnurb->nurbs)) {
return changed;
}
BKE_curve_nurb_vert_active_get(cu, &cu_actnu, &cu_actvert.p);
int act_offset = 0;
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
BLI_assert(nu->pntsu > 0);
int i;
int pnt_len = nu->pntsu;
int new_points = 0;
int offset = 0;
bool is_prev_selected = false;
bool duplic_first = false;
bool duplic_last = false;
if (nu->type == CU_BEZIER) {
BezTriple *bezt, *bezt_prev = NULL;
BezTriple bezt_stack;
bool is_cyclic = false;
if (pnt_len == 1) {
/* Single point extrusion.
* Keep `is_prev_selected` false to force extrude. */
bezt_prev = &nu->bezt[0];
}
else if (nu->flagu & CU_NURB_CYCLIC) {
is_cyclic = true;
bezt_prev = &nu->bezt[pnt_len - 1];
is_prev_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt_prev);
}
else {
duplic_first = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[0]) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[1]);
duplic_last = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[pnt_len - 2]) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, &nu->bezt[pnt_len - 1]);
if (duplic_first) {
bezt_stack = nu->bezt[0];
BEZT_DESEL_ALL(&bezt_stack);
bezt_prev = &bezt_stack;
}
if (duplic_last) {
new_points++;
}
}
i = pnt_len;
for (bezt = &nu->bezt[0]; i--; bezt++) {
bool is_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
if (bezt_prev && is_prev_selected != is_selected) {
new_points++;
}
if (bezt == cu_actvert.bezt) {
act_offset = new_points;
}
bezt_prev = bezt;
is_prev_selected = is_selected;
}
if (new_points) {
if (pnt_len == 1) {
/* Single point extrusion.
* Set `is_prev_selected` as false to force extrude. */
BLI_assert(bezt_prev == &nu->bezt[0]);
is_prev_selected = false;
}
else if (is_cyclic) {
BLI_assert(bezt_prev == &nu->bezt[pnt_len - 1]);
BLI_assert(is_prev_selected == BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt_prev));
}
else if (duplic_first) {
bezt_prev = &bezt_stack;
is_prev_selected = false;
}
else {
bezt_prev = NULL;
}
BezTriple *bezt_src, *bezt_dst, *bezt_src_iter, *bezt_dst_iter;
const int new_len = pnt_len + new_points;
bezt_src = nu->bezt;
bezt_dst = MEM_mallocN(new_len * sizeof(BezTriple), __func__);
bezt_src_iter = &bezt_src[0];
bezt_dst_iter = &bezt_dst[0];
i = 0;
for (bezt = &nu->bezt[0]; i < pnt_len; i++, bezt++) {
bool is_selected = BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
/* While this gets de-selected, selecting here ensures newly created verts are selected.
* without this, the vertices are copied but only the handles are transformed.
* which seems buggy from a user perspective. */
if (is_selected) {
bezt->f2 |= SELECT;
}
if (bezt_prev && is_prev_selected != is_selected) {
int count = i - offset + 1;
if (is_prev_selected) {
ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, count - 1);
ED_curve_beztcpy(editnurb, &bezt_dst_iter[count - 1], bezt_prev, 1);
}
else {
ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, count);
}
ED_curve_beztcpy(editnurb, &bezt_dst_iter[count], bezt, 1);
BEZT_DESEL_ALL(&bezt_dst_iter[count - 1]);
bezt_dst_iter += count + 1;
bezt_src_iter += count;
offset = i + 1;
}
bezt_prev = bezt;
is_prev_selected = is_selected;
}
int remain = pnt_len - offset;
if (remain) {
ED_curve_beztcpy(editnurb, bezt_dst_iter, bezt_src_iter, remain);
}
if (duplic_last) {
ED_curve_beztcpy(editnurb, &bezt_dst[new_len - 1], &bezt_src[pnt_len - 1], 1);
BEZT_DESEL_ALL(&bezt_dst[new_len - 1]);
}
MEM_freeN(nu->bezt);
nu->bezt = bezt_dst;
nu->pntsu += new_points;
changed = true;
}
}
else {
BPoint *bp, *bp_prev = NULL;
BPoint bp_stack;
if (pnt_len == 1) {
/* Single point extrusion.
* Reference a `prev_bp` to force extrude. */
bp_prev = &nu->bp[0];
}
else {
duplic_first = (nu->bp[0].f1 & SELECT) && (nu->bp[1].f1 & SELECT);
duplic_last = (nu->bp[pnt_len - 2].f1 & SELECT) && (nu->bp[pnt_len - 1].f1 & SELECT);
if (duplic_first) {
bp_stack = nu->bp[0];
bp_stack.f1 &= ~SELECT;
bp_prev = &bp_stack;
}
if (duplic_last) {
new_points++;
}
}
i = pnt_len;
for (bp = &nu->bp[0]; i--; bp++) {
bool is_selected = (bp->f1 & SELECT) != 0;
if (bp_prev && is_prev_selected != is_selected) {
new_points++;
}
if (bp == cu_actvert.bp) {
act_offset = new_points;
}
bp_prev = bp;
is_prev_selected = is_selected;
}
if (new_points) {
BPoint *bp_src, *bp_dst, *bp_src_iter, *bp_dst_iter;
const int new_len = pnt_len + new_points;
is_prev_selected = false;
if (pnt_len == 1) {
/* Single point extrusion.
* Keep `is_prev_selected` false to force extrude. */
BLI_assert(bp_prev == &nu->bp[0]);
}
else if (duplic_first) {
bp_prev = &bp_stack;
is_prev_selected = false;
}
else {
bp_prev = NULL;
}
bp_src = nu->bp;
bp_dst = MEM_mallocN(new_len * sizeof(BPoint), __func__);
bp_src_iter = &bp_src[0];
bp_dst_iter = &bp_dst[0];
i = 0;
for (bp = &nu->bp[0]; i < pnt_len; i++, bp++) {
bool is_selected = (bp->f1 & SELECT) != 0;
if (bp_prev && is_prev_selected != is_selected) {
int count = i - offset + 1;
if (is_prev_selected) {
ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, count - 1);
ED_curve_bpcpy(editnurb, &bp_dst_iter[count - 1], bp_prev, 1);
}
else {
ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, count);
}
ED_curve_bpcpy(editnurb, &bp_dst_iter[count], bp, 1);
bp_dst_iter[count - 1].f1 &= ~SELECT;
bp_dst_iter += count + 1;
bp_src_iter += count;
offset = i + 1;
}
bp_prev = bp;
is_prev_selected = is_selected;
}
int remain = pnt_len - offset;
if (remain) {
ED_curve_bpcpy(editnurb, bp_dst_iter, bp_src_iter, remain);
}
if (duplic_last) {
ED_curve_bpcpy(editnurb, &bp_dst[new_len - 1], &bp_src[pnt_len - 1], 1);
bp_dst[new_len - 1].f1 &= ~SELECT;
}
MEM_freeN(nu->bp);
nu->bp = bp_dst;
nu->pntsu += new_points;
BKE_nurb_knot_calc_u(nu);
changed = true;
}
}
}
cu->actvert += act_offset;
return changed;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Vertex Operator
* \{ */
static int ed_editcurve_addvert(Curve *cu,
EditNurb *editnurb,
View3D *v3d,
const float location_init[3])
{
float center[3];
float temp[3];
uint verts_len;
bool changed = false;
zero_v3(center);
verts_len = 0;
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
int i;
if (nu->type == CU_BEZIER) {
BezTriple *bezt;
for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
add_v3_v3(center, bezt->vec[1]);
verts_len += 1;
}
}
}
else {
BPoint *bp;
for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) {
if (bp->f1 & SELECT) {
add_v3_v3(center, bp->vec);
verts_len += 1;
}
}
}
}
if (verts_len && ed_editcurve_extrude(cu, editnurb, v3d)) {
float ofs[3];
int i;
mul_v3_fl(center, 1.0f / (float)verts_len);
sub_v3_v3v3(ofs, location_init, center);
if (CU_IS_2D(cu)) {
ofs[2] = 0.0f;
}
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if (nu->type == CU_BEZIER) {
BezTriple *bezt;
for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
add_v3_v3(bezt->vec[0], ofs);
add_v3_v3(bezt->vec[1], ofs);
add_v3_v3(bezt->vec[2], ofs);
if (((nu->flagu & CU_NURB_CYCLIC) == 0) && (i == 0 || i == nu->pntsu - 1)) {
BKE_nurb_handle_calc_simple_auto(nu, bezt);
}
}
}
BKE_nurb_handles_calc(nu);
}
else {
BPoint *bp;
for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) {
if (bp->f1 & SELECT) {
add_v3_v3(bp->vec, ofs);
}
}
}
}
changed = true;
}
else {
float location[3];
copy_v3_v3(location, location_init);
if (CU_IS_2D(cu)) {
location[2] = 0.0f;
}
/* nothing selected: create a new curve */
Nurb *nu = BKE_curve_nurb_active_get(cu);
Nurb *nurb_new;
if (!nu) {
/* Bezier as default. */
nurb_new = MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2");
nurb_new->type = CU_BEZIER;
nurb_new->resolu = cu->resolu;
nurb_new->orderu = 4;
nurb_new->flag |= CU_SMOOTH;
BKE_nurb_bezierPoints_add(nurb_new, 1);
}
else {
/* Copy the active nurb settings. */
nurb_new = BKE_nurb_copy(nu, 1, 1);
if (nu->bezt) {
memcpy(nurb_new->bezt, nu->bezt, sizeof(BezTriple));
}
else {
memcpy(nurb_new->bp, nu->bp, sizeof(BPoint));
}
}
if (nurb_new->type == CU_BEZIER) {
BezTriple *bezt_new = nurb_new->bezt;
BEZT_SEL_ALL(bezt_new);
bezt_new->h1 = HD_AUTO;
bezt_new->h2 = HD_AUTO;
temp[0] = 1.0f;
temp[1] = 0.0f;
temp[2] = 0.0f;
copy_v3_v3(bezt_new->vec[1], location);
sub_v3_v3v3(bezt_new->vec[0], location, temp);
add_v3_v3v3(bezt_new->vec[2], location, temp);
}
else {
BPoint *bp_new = nurb_new->bp;
bp_new->f1 |= SELECT;
copy_v3_v3(bp_new->vec, location);
BKE_nurb_knot_calc_u(nurb_new);
}
BLI_addtail(&editnurb->nurbs, nurb_new);
changed = true;
}
return changed;
}
static int add_vertex_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *obedit = CTX_data_edit_object(C);
View3D *v3d = CTX_wm_view3d(C);
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
float location[3];
float imat[4][4];
RNA_float_get_array(op->ptr, "location", location);
invert_m4_m4(imat, obedit->obmat);
mul_m4_v3(imat, location);
if (ed_editcurve_addvert(cu, editnurb, v3d, location)) {
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
DEG_id_tag_update(obedit->data, 0);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
if (vc.rv3d && !RNA_struct_property_is_set(op->ptr, "location")) {
Curve *cu;
float location[3];
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
(vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
Nurb *nu;
BezTriple *bezt;
BPoint *bp;
cu = vc.obedit->data;
ED_curve_nurb_vert_selected_find(cu, vc.v3d, &nu, &bezt, &bp);
if (bezt) {
mul_v3_m4v3(location, vc.obedit->obmat, bezt->vec[1]);
}
else if (bp) {
mul_v3_m4v3(location, vc.obedit->obmat, bp->vec);
}
else {
copy_v3_v3(location, vc.scene->cursor.location);
}
ED_view3d_win_to_3d_int(vc.v3d, vc.region, location, event->mval, location);
if (use_proj) {
const float mval[2] = {UNPACK2(event->mval)};
struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
vc.scene, 0, vc.region, vc.v3d);
ED_transform_snap_object_project_view3d(
snap_context,
vc.depsgraph,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
.edit_mode_type = SNAP_GEOM_FINAL,
},
mval,
NULL,
NULL,
location,
NULL);
ED_transform_snap_object_context_destroy(snap_context);
}
if (CU_IS_2D(cu)) {
const float eps = 1e-6f;
/* get the view vector to 'location' */
float view_dir[3];
ED_view3d_global_to_vector(vc.rv3d, location, view_dir);
/* get the plane */
float plane[4];
/* only normalize to avoid precision errors */
normalize_v3_v3(plane, vc.obedit->obmat[2]);
plane[3] = -dot_v3v3(plane, vc.obedit->obmat[3]);
if (fabsf(dot_v3v3(view_dir, plane)) < eps) {
/* can't project on an aligned plane. */
}
else {
float lambda;
if (isect_ray_plane_v3(location, view_dir, plane, &lambda, false)) {
/* check if we're behind the viewport */
float location_test[3];
madd_v3_v3v3fl(location_test, location, view_dir, lambda);
if ((vc.rv3d->is_persp == false) ||
(mul_project_m4_v3_zfac(vc.rv3d->persmat, location_test) > 0.0f)) {
copy_v3_v3(location, location_test);
}
}
}
}
RNA_float_set_array(op->ptr, "location", location);
}
return add_vertex_exec(C, op);
}
void CURVE_OT_vertex_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Vertex";
ot->idname = "CURVE_OT_vertex_add";
ot->description = "Add a new control point (linked to only selected end-curve one, if any)";
/* api callbacks */
ot->exec = add_vertex_exec;
ot->invoke = add_vertex_invoke;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_vector_xyz(ot->srna,
"location",
3,
NULL,
-OBJECT_ADD_SIZE_MAXF,
OBJECT_ADD_SIZE_MAXF,
"Location",
"Location to add new vertex at",
-1.0e4f,
1.0e4f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Extrude Operator
* \{ */
static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
bool changed = false;
bool as_curve = false;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
/* First test: curve? */
if (obedit->type != OB_CURVE) {
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) {
as_curve = true;
break;
}
}
}
if (obedit->type == OB_CURVE || as_curve) {
changed = ed_editcurve_extrude(cu, editnurb, v3d);
}
else {
changed = ed_editnurb_extrude_flag(editnurb, SELECT);
}
if (changed) {
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_extrude(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Extrude";
ot->description = "Extrude selected control point(s)";
ot->idname = "CURVE_OT_extrude";
/* api callbacks */
ot->exec = curve_extrude_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* to give to transform */
RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Make Cyclic Operator
* \{ */
static bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
{
BezTriple *bezt;
BPoint *bp;
int a;
bool changed = false;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->pntsu > 1 || nu->pntsv > 1) {
if (nu->type == CU_POLY) {
a = nu->pntsu;
bp = nu->bp;
while (a--) {
if (bp->f1 & SELECT) {
nu->flagu ^= CU_NURB_CYCLIC;
changed = true;
break;
}
bp++;
}
}
else if (nu->type == CU_BEZIER) {
a = nu->pntsu;
bezt = nu->bezt;
while (a--) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
nu->flagu ^= CU_NURB_CYCLIC;
changed = true;
break;
}
bezt++;
}
BKE_nurb_handles_calc(nu);
}
else if (nu->pntsv == 1 && nu->type == CU_NURBS) {
if (nu->knotsu) { /* if check_valid_nurb_u fails the knotsu can be NULL */
a = nu->pntsu;
bp = nu->bp;
while (a--) {
if (bp->f1 & SELECT) {
nu->flagu ^= CU_NURB_CYCLIC;
/* 1==u type is ignored for cyclic curves */
BKE_nurb_knot_calc_u(nu);
changed = true;
break;
}
bp++;
}
}
}
else if (nu->type == CU_NURBS) {
a = nu->pntsu * nu->pntsv;
bp = nu->bp;
while (a--) {
if (bp->f1 & SELECT) {
if (direction == 0 && nu->pntsu > 1) {
nu->flagu ^= CU_NURB_CYCLIC;
/* 1==u type is ignored for cyclic curves */
BKE_nurb_knot_calc_u(nu);
changed = true;
}
if (direction == 1 && nu->pntsv > 1) {
nu->flagv ^= CU_NURB_CYCLIC;
/* 2==v type is ignored for cyclic curves */
BKE_nurb_knot_calc_v(nu);
changed = true;
}
break;
}
bp++;
}
}
}
}
return changed;
}
static int toggle_cyclic_exec(bContext *C, wmOperator *op)
{
const int direction = RNA_enum_get(op->ptr, "direction");
View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool changed_multi = false;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
if (curve_toggle_cyclic(v3d, editnurb, direction)) {
changed_multi = true;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
}
MEM_freeN(objects);
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
static int toggle_cyclic_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *obedit = CTX_data_edit_object(C);
ListBase *editnurb = object_editcurve_get(obedit);
uiPopupMenu *pup;
uiLayout *layout;
if (obedit->type == OB_SURF) {
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->pntsu > 1 || nu->pntsv > 1) {
if (nu->type == CU_NURBS) {
pup = UI_popup_menu_begin(C, IFACE_("Direction"), ICON_NONE);
layout = UI_popup_menu_layout(pup);
uiItemsEnumO(layout, op->type->idname, "direction");
UI_popup_menu_end(C, pup);
return OPERATOR_INTERFACE;
}
}
}
}
return toggle_cyclic_exec(C, op);
}
void CURVE_OT_cyclic_toggle(wmOperatorType *ot)
{
static const EnumPropertyItem direction_items[] = {
{0, "CYCLIC_U", 0, "Cyclic U", ""},
{1, "CYCLIC_V", 0, "Cyclic V", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Toggle Cyclic";
ot->description = "Make active spline closed/opened loop";
ot->idname = "CURVE_OT_cyclic_toggle";
/* api callbacks */
ot->exec = toggle_cyclic_exec;
ot->invoke = toggle_cyclic_invoke;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_enum(ot->srna,
"direction",
direction_items,
0,
"Direction",
"Direction to make surface cyclic in");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Duplicate Operator
* \{ */
static int duplicate_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
int ok = -1;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase newnurb = {NULL, NULL};
adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, false);
if (BLI_listbase_is_empty(&newnurb)) {
ok = MAX2(ok, 0);
continue;
}
ok = 1;
BLI_movelisttolist(object_editcurve_get(obedit), &newnurb);
DEG_id_tag_update(&cu->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, &cu->id);
}
MEM_freeN(objects);
if (ok == 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection");
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void CURVE_OT_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Curve";
ot->description = "Duplicate selected control points";
ot->idname = "CURVE_OT_duplicate";
/* api callbacks */
ot->exec = duplicate_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Delete Operator
* \{ */
static bool curve_delete_vertices(Object *obedit, View3D *v3d)
{
if (obedit->type == OB_SURF) {
ed_surf_delete_selected(obedit);
}
else {
ed_curve_delete_selected(obedit, v3d);
}
return true;
}
static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split)
{
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
ListBase *nubase = &editnurb->nurbs, newnurb = {NULL, NULL};
Nurb *nu1;
BezTriple *bezt, *bezt1, *bezt2;
BPoint *bp, *bp1, *bp2;
int a, b, starta, enda, cut, cyclicut;
LISTBASE_FOREACH (Nurb *, nu, nubase) {
nu1 = NULL;
starta = enda = cut = -1;
cyclicut = 0;
if (nu->type == CU_BEZIER) {
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
if (!BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
enda = a;
if (starta == -1) {
starta = a;
}
if (a < nu->pntsu - 1) {
continue;
}
}
else if (a < nu->pntsu - 1 && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt + 1)) {
/* if just single selected point then continue */
continue;
}
if (starta >= 0) {
/* got selected segment, now check where and copy */
if (starta <= 1 && a == nu->pntsu - 1) {
/* copying all points in spline */
if (starta == 1 && enda != a) {
nu->flagu &= ~CU_NURB_CYCLIC;
}
starta = 0;
enda = a;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, cut, 1);
}
else if (starta == 0) {
/* if start of curve copy next end point */
enda++;
cut = enda - starta + 1;
bezt1 = &nu->bezt[nu->pntsu - 1];
bezt2 = &nu->bezt[nu->pntsu - 2];
if ((nu->flagu & CU_NURB_CYCLIC) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, cut + 1, 1);
ED_curve_beztcpy(editnurb, &nu1->bezt[1], nu->bezt, cut);
starta = nu->pntsu - 1;
cut = 1;
}
else {
if (nu->flagu & CU_NURB_CYCLIC) {
cyclicut = cut;
}
else {
nu1 = BKE_nurb_copy(nu, cut, 1);
}
}
}
else if (enda == nu->pntsu - 1) {
/* if end of curve copy previous start point */
starta--;
cut = enda - starta + 1;
bezt1 = nu->bezt;
bezt2 = &nu->bezt[1];
if ((nu->flagu & CU_NURB_CYCLIC) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) &&
BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, cut + 1, 1);
ED_curve_beztcpy(editnurb, &nu1->bezt[cut], nu->bezt, 1);
}
else if (cyclicut != 0) {
/* if cyclicut exists it is a cyclic spline, start and end should be connected */
nu1 = BKE_nurb_copy(nu, cut + cyclicut, 1);
ED_curve_beztcpy(editnurb, &nu1->bezt[cut], nu->bezt, cyclicut);
cyclicut = 0;
}
else {
nu1 = BKE_nurb_copy(nu, cut, 1);
}
}
else {
/* mid spline selection, copy adjacent start and end */
starta--;
enda++;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, cut, 1);
}
if (nu1 != NULL) {
ED_curve_beztcpy(editnurb, nu1->bezt, &nu->bezt[starta], cut);
BLI_addtail(&newnurb, nu1);
if (starta != 0 || enda != nu->pntsu - 1) {
nu1->flagu &= ~CU_NURB_CYCLIC;
}
nu1 = NULL;
}
starta = enda = -1;
}
}
if (!split && cut != -1 && nu->pntsu > 2 && !(nu->flagu & CU_NURB_CYCLIC)) {
/* start and points copied if connecting segment was deleted and not cyclic spline */
bezt1 = nu->bezt;
bezt2 = &nu->bezt[1];
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
nu1 = BKE_nurb_copy(nu, 1, 1);
ED_curve_beztcpy(editnurb, nu1->bezt, bezt1, 1);
BLI_addtail(&newnurb, nu1);
}
bezt1 = &nu->bezt[nu->pntsu - 1];
bezt2 = &nu->bezt[nu->pntsu - 2];
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt1) && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt2)) {
nu1 = BKE_nurb_copy(nu, 1, 1);
ED_curve_beztcpy(editnurb, nu1->bezt, bezt1, 1);
BLI_addtail(&newnurb, nu1);
}
}
}
else if (nu->pntsv >= 1) {
int u, v;
if (isNurbselV(nu, &u, SELECT)) {
for (a = 0, bp = nu->bp; a < nu->pntsu; a++, bp++) {
if (!(bp->f1 & SELECT)) {
enda = a;
if (starta == -1) {
starta = a;
}
if (a < nu->pntsu - 1) {
continue;
}
}
else if (a < nu->pntsu - 1 && !((bp + 1)->f1 & SELECT)) {
/* if just single selected point then continue */
continue;
}
if (starta >= 0) {
/* got selected segment, now check where and copy */
if (starta <= 1 && a == nu->pntsu - 1) {
/* copying all points in spline */
if (starta == 1 && enda != a) {
nu->flagu &= ~CU_NURB_CYCLIC;
}
starta = 0;
enda = a;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
}
else if (starta == 0) {
/* if start of curve copy next end point */
enda++;
cut = enda - starta + 1;
bp1 = &nu->bp[nu->pntsu - 1];
bp2 = &nu->bp[nu->pntsu - 2];
if ((nu->flagu & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, cut + 1, nu->pntsv);
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(
editnurb, &nu1->bp[b * nu1->pntsu + 1], &nu->bp[b * nu->pntsu], cut);
}
starta = nu->pntsu - 1;
cut = 1;
}
else {
if (nu->flagu & CU_NURB_CYCLIC) {
cyclicut = cut;
}
else {
nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
}
}
}
else if (enda == nu->pntsu - 1) {
/* if end of curve copy previous start point */
starta--;
cut = enda - starta + 1;
bp1 = nu->bp;
bp2 = &nu->bp[1];
if ((nu->flagu & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, cut + 1, nu->pntsv);
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(
editnurb, &nu1->bp[b * nu1->pntsu + cut], &nu->bp[b * nu->pntsu], 1);
}
}
else if (cyclicut != 0) {
/* if cyclicut exists it is a cyclic spline, start and end should be connected */
nu1 = BKE_nurb_copy(nu, cut + cyclicut, nu->pntsv);
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(
editnurb, &nu1->bp[b * nu1->pntsu + cut], &nu->bp[b * nu->pntsu], cyclicut);
}
}
else {
nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
}
}
else {
/* mid spline selection, copy adjacent start and end */
starta--;
enda++;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, cut, nu->pntsv);
}
if (nu1 != NULL) {
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(
editnurb, &nu1->bp[b * nu1->pntsu], &nu->bp[b * nu->pntsu + starta], cut);
}
BLI_addtail(&newnurb, nu1);
if (starta != 0 || enda != nu->pntsu - 1) {
nu1->flagu &= ~CU_NURB_CYCLIC;
}
nu1 = NULL;
}
starta = enda = -1;
}
}
if (!split && cut != -1 && nu->pntsu > 2 && !(nu->flagu & CU_NURB_CYCLIC)) {
/* start and points copied if connecting segment was deleted and not cyclic spline */
bp1 = nu->bp;
bp2 = &nu->bp[1];
if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
nu1 = BKE_nurb_copy(nu, 1, nu->pntsv);
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(editnurb, &nu1->bp[b], &nu->bp[b * nu->pntsu], 1);
}
BLI_addtail(&newnurb, nu1);
}
bp1 = &nu->bp[nu->pntsu - 1];
bp2 = &nu->bp[nu->pntsu - 2];
if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
nu1 = BKE_nurb_copy(nu, 1, nu->pntsv);
for (b = 0; b < nu->pntsv; b++) {
ED_curve_bpcpy(editnurb, &nu1->bp[b], &nu->bp[b * nu->pntsu + nu->pntsu - 1], 1);
}
BLI_addtail(&newnurb, nu1);
}
}
}
else if (isNurbselU(nu, &v, SELECT)) {
for (a = 0, bp = nu->bp; a < nu->pntsv; a++, bp += nu->pntsu) {
if (!(bp->f1 & SELECT)) {
enda = a;
if (starta == -1) {
starta = a;
}
if (a < nu->pntsv - 1) {
continue;
}
}
else if (a < nu->pntsv - 1 && !((bp + nu->pntsu)->f1 & SELECT)) {
/* if just single selected point then continue */
continue;
}
if (starta >= 0) {
/* got selected segment, now check where and copy */
if (starta <= 1 && a == nu->pntsv - 1) {
/* copying all points in spline */
if (starta == 1 && enda != a) {
nu->flagv &= ~CU_NURB_CYCLIC;
}
starta = 0;
enda = a;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
}
else if (starta == 0) {
/* if start of curve copy next end point */
enda++;
cut = enda - starta + 1;
bp1 = &nu->bp[nu->pntsv * nu->pntsu - nu->pntsu];
bp2 = &nu->bp[nu->pntsv * nu->pntsu - (nu->pntsu * 2)];
if ((nu->flagv & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + 1);
ED_curve_bpcpy(editnurb, &nu1->bp[nu->pntsu], nu->bp, cut * nu->pntsu);
starta = nu->pntsv - 1;
cut = 1;
}
else {
if (nu->flagv & CU_NURB_CYCLIC) {
cyclicut = cut;
}
else {
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
}
}
}
else if (enda == nu->pntsv - 1) {
/* if end of curve copy previous start point */
starta--;
cut = enda - starta + 1;
bp1 = nu->bp;
bp2 = &nu->bp[nu->pntsu];
if ((nu->flagv & CU_NURB_CYCLIC) && (bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
/* check if need to join start of spline to end */
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + 1);
ED_curve_bpcpy(editnurb, &nu1->bp[cut * nu->pntsu], nu->bp, nu->pntsu);
}
else if (cyclicut != 0) {
/* if cyclicut exists it is a cyclic spline, start and end should be connected */
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut + cyclicut);
ED_curve_bpcpy(editnurb, &nu1->bp[cut * nu->pntsu], nu->bp, nu->pntsu * cyclicut);
cyclicut = 0;
}
else {
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
}
}
else {
/* mid spline selection, copy adjacent start and end */
starta--;
enda++;
cut = enda - starta + 1;
nu1 = BKE_nurb_copy(nu, nu->pntsu, cut);
}
if (nu1 != NULL) {
ED_curve_bpcpy(editnurb, nu1->bp, &nu->bp[starta * nu->pntsu], cut * nu->pntsu);
BLI_addtail(&newnurb, nu1);
if (starta != 0 || enda != nu->pntsv - 1) {
nu1->flagv &= ~CU_NURB_CYCLIC;
}
nu1 = NULL;
}
starta = enda = -1;
}
}
if (!split && cut != -1 && nu->pntsv > 2 && !(nu->flagv & CU_NURB_CYCLIC)) {
/* start and points copied if connecting segment was deleted and not cyclic spline */
bp1 = nu->bp;
bp2 = &nu->bp[nu->pntsu];
if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
nu1 = BKE_nurb_copy(nu, nu->pntsu, 1);
ED_curve_bpcpy(editnurb, nu1->bp, nu->bp, nu->pntsu);
BLI_addtail(&newnurb, nu1);
}
bp1 = &nu->bp[nu->pntsu * nu->pntsv - nu->pntsu];
bp2 = &nu->bp[nu->pntsu * nu->pntsv - (nu->pntsu * 2)];
if ((bp1->f1 & SELECT) && (bp2->f1 & SELECT)) {
nu1 = BKE_nurb_copy(nu, nu->pntsu, 1);
ED_curve_bpcpy(
editnurb, nu1->bp, &nu->bp[nu->pntsu * nu->pntsv - nu->pntsu], nu->pntsu);
BLI_addtail(&newnurb, nu1);
}
}
}
else {
/* selection not valid, just copy nurb to new list */
nu1 = BKE_nurb_copy(nu, nu->pntsu, nu->pntsv);
ED_curve_bpcpy(editnurb, nu1->bp, nu->bp, nu->pntsu * nu->pntsv);
BLI_addtail(&newnurb, nu1);
}
}
}
LISTBASE_FOREACH (Nurb *, nu, &newnurb) {
if (nu->type == CU_BEZIER) {
if (split) {
/* deselect for split operator */
for (b = 0, bezt1 = nu->bezt; b < nu->pntsu; b++, bezt1++) {
select_beztriple(bezt1, DESELECT, SELECT, true);
}
}
BKE_nurb_handles_calc(nu);
}
else {
if (split) {
/* deselect for split operator */
for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
select_bpoint(bp1, DESELECT, SELECT, HIDDEN);
}
}
BKE_nurb_order_clamp_u(nu);
BKE_nurb_knot_calc_u(nu);
if (nu->pntsv > 1) {
BKE_nurb_order_clamp_v(nu);
BKE_nurb_knot_calc_v(nu);
}
}
}
keyIndex_delNurbList(editnurb, nubase);
BKE_nurbList_free(nubase);
BLI_movelisttolist(nubase, &newnurb);
return true;
}
static int curve_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C);
eCurveElem_Types type = RNA_enum_get(op->ptr, "type");
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
bool changed_multi = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
bool changed = false;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
if (type == CURVE_VERTEX) {
changed = curve_delete_vertices(obedit, v3d);
}
else if (type == CURVE_SEGMENT) {
changed = curve_delete_segments(obedit, v3d, false);
}
else {
BLI_assert(0);
}
if (changed) {
changed_multi = true;
cu->actnu = cu->actvert = CU_ACT_NONE;
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
}
MEM_freeN(objects);
if (changed_multi) {
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static const EnumPropertyItem curve_delete_type_items[] = {
{CURVE_VERTEX, "VERT", 0, "Vertices", ""},
{CURVE_SEGMENT, "SEGMENT", 0, "Segments", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem *rna_curve_delete_type_itemf(bContext *C,
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *item = NULL;
int totitem = 0;
if (!C) { /* needed for docs and i18n tools */
return curve_delete_type_items;
}
RNA_enum_items_add_value(&item, &totitem, curve_delete_type_items, CURVE_VERTEX);
RNA_enum_items_add_value(&item, &totitem, curve_delete_type_items, CURVE_SEGMENT);
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
void CURVE_OT_delete(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Delete";
ot->description = "Delete selected control points or segments";
ot->idname = "CURVE_OT_delete";
/* api callbacks */
ot->exec = curve_delete_exec;
ot->invoke = WM_menu_invoke;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(
ot->srna, "type", curve_delete_type_items, 0, "Type", "Which elements to delete");
RNA_def_enum_funcs(prop, rna_curve_delete_type_itemf);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
ot->prop = prop;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dissolve Vertices
* \{ */
static bool test_bezt_is_sel_any(const void *bezt_v, void *user_data)
{
View3D *v3d = user_data;
const BezTriple *bezt = bezt_v;
return BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
}
static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if ((nu->type == CU_BEZIER) && (nu->pntsu > 2)) {
uint span_step[2] = {nu->pntsu, nu->pntsu};
uint span_len;
while (BLI_array_iter_span(nu->bezt,
nu->pntsu,
(nu->flagu & CU_NURB_CYCLIC) != 0,
false,
test_bezt_is_sel_any,
v3d,
span_step,
&span_len)) {
BezTriple *bezt_prev = &nu->bezt[mod_i(span_step[0] - 1, nu->pntsu)];
BezTriple *bezt_next = &nu->bezt[mod_i(span_step[1] + 1, nu->pntsu)];
int i_span_edge_len = span_len + 1;
const uint dims = 3;
const uint points_len = ((cu->resolu - 1) * i_span_edge_len) + 1;
float *points = MEM_mallocN(points_len * dims * sizeof(float), __func__);
float *points_stride = points;
const int points_stride_len = (cu->resolu - 1);
for (int segment = 0; segment < i_span_edge_len; segment++) {
BezTriple *bezt_a = &nu->bezt[mod_i((span_step[0] + segment) - 1, nu->pntsu)];
BezTriple *bezt_b = &nu->bezt[mod_i((span_step[0] + segment), nu->pntsu)];
for (int axis = 0; axis < dims; axis++) {
BKE_curve_forward_diff_bezier(bezt_a->vec[1][axis],
bezt_a->vec[2][axis],
bezt_b->vec[0][axis],
bezt_b->vec[1][axis],
points_stride + axis,
points_stride_len,
dims * sizeof(float));
}
points_stride += dims * points_stride_len;
}
BLI_assert(points_stride + dims == points + (points_len * dims));
float tan_l[3], tan_r[3], error_sq_dummy;
uint error_index_dummy;
sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]);
normalize_v3(tan_l);
sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]);
normalize_v3(tan_r);
curve_fit_cubic_to_points_single_fl(points,
points_len,
NULL,
dims,
FLT_EPSILON,
tan_l,
tan_r,
bezt_prev->vec[2],
bezt_next->vec[0],
&error_sq_dummy,
&error_index_dummy);
if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) {
bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) {
bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
MEM_freeN(points);
}
}
}
ed_curve_delete_selected(obedit, v3d);
cu->actnu = cu->actvert = CU_ACT_NONE;
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_dissolve_verts(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Dissolve Vertices";
ot->description = "Delete selected control points, correcting surrounding handles";
ot->idname = "CURVE_OT_dissolve_verts";
/* api callbacks */
ot->exec = curve_dissolve_exec;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Decimate Operator
* \{ */
static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test)
{
BezTriple *bezt = nu->bezt;
int i;
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
if (bezt->f2 & flag_test) {
return true;
}
}
return false;
}
static int curve_decimate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
const float error_sq_max = FLT_MAX;
float ratio = RNA_float_get(op->ptr, "ratio");
bool all_supported_multi = true;
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
bool all_supported = true;
bool changed = false;
{
ListBase *editnurb = object_editcurve_get(obedit);
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
if ((nu->pntsu > 2) && nurb_bezt_flag_any(nu, SELECT)) {
const int error_target_len = max_ii(2, nu->pntsu * ratio);
if (error_target_len != nu->pntsu) {
BKE_curve_decimate_nurb(nu, cu->resolu, error_sq_max, error_target_len);
changed = true;
}
}
}
else {
all_supported = false;
}
}
}
if (all_supported == false) {
all_supported_multi = false;
}
if (changed) {
cu->actnu = cu->actvert = CU_ACT_NONE;
if (ED_curve_updateAnimPaths(bmain, obedit->data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
}
if (all_supported_multi == false) {
BKE_report(op->reports, RPT_WARNING, "Only bezier curves are supported");
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_decimate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Decimate Curve";
ot->description = "Simplify selected curves";
ot->idname = "CURVE_OT_decimate";
/* api callbacks */
ot->exec = curve_decimate_exec;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_factor(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shade Smooth/Flat Operator
* \{ */
static int shade_smooth_exec(bContext *C, wmOperator *op)
{
View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int clear = (STREQ(op->idname, "CURVE_OT_shade_flat"));
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
int ret_value = OPERATOR_CANCELLED;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
if (obedit->type != OB_CURVE) {
continue;
}
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (ED_curve_nurb_select_check(v3d, nu)) {
if (!clear) {
nu->flag |= CU_SMOOTH;
}
else {
nu->flag &= ~CU_SMOOTH;
}
}
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
ret_value = OPERATOR_FINISHED;
}
MEM_freeN(objects);
return ret_value;
}
void CURVE_OT_shade_smooth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Shade Smooth";
ot->idname = "CURVE_OT_shade_smooth";
ot->description = "Set shading to smooth";
/* api callbacks */
ot->exec = shade_smooth_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void CURVE_OT_shade_flat(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Shade Flat";
ot->idname = "CURVE_OT_shade_flat";
ot->description = "Set shading to flat";
/* api callbacks */
ot->exec = shade_smooth_exec;
ot->poll = ED_operator_editsurfcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Join Operator
* \{ */
/**
* This is used externally, by #OBJECT_OT_join.
* TODO: shape keys - as with meshes.
*/
int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob_active = CTX_data_active_object(C);
Curve *cu;
BezTriple *bezt;
BPoint *bp;
ListBase tempbase;
float imat[4][4], cmat[4][4];
int a;
bool ok = false;
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
if (ob_iter == ob_active) {
ok = true;
break;
}
}
CTX_DATA_END;
/* that way the active object is always selected */
if (ok == false) {
BKE_report(op->reports, RPT_WARNING, "Active object is not a selected curve");
return OPERATOR_CANCELLED;
}
BLI_listbase_clear(&tempbase);
/* Inverse transform for all selected curves in this object,
* See #object_join_exec for detailed comment on why the safe version is used. */
invert_m4_m4_safe_ortho(imat, ob_active->obmat);
Curve *cu_active = ob_active->data;
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
if (ob_iter->type == ob_active->type) {
if (ob_iter != ob_active) {
cu = ob_iter->data;
if (cu->nurb.first) {
/* watch it: switch order here really goes wrong */
mul_m4_m4m4(cmat, imat, ob_iter->obmat);
/* Compensate for different bevel depth. */
bool do_radius = false;
float compensate_radius = 0.0f;
if (cu->ext2 != 0.0f && cu_active->ext2 != 0.0f) {
float compensate_scale = mat4_to_scale(cmat);
compensate_radius = cu->ext2 / cu_active->ext2 * compensate_scale;
do_radius = true;
}
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
Nurb *newnu = BKE_nurb_duplicate(nu);
if (ob_active->totcol) { /* TODO, merge material lists */
CLAMP(newnu->mat_nr, 0, ob_active->totcol - 1);
}
else {
newnu->mat_nr = 0;
}
BLI_addtail(&tempbase, newnu);
if ((bezt = newnu->bezt)) {
a = newnu->pntsu;
while (a--) {
/* Compensate for different bevel depth. */
if (do_radius) {
bezt->radius *= compensate_radius;
}
mul_m4_v3(cmat, bezt->vec[0]);
mul_m4_v3(cmat, bezt->vec[1]);
mul_m4_v3(cmat, bezt->vec[2]);
bezt++;
}
BKE_nurb_handles_calc(newnu);
}
if ((bp = newnu->bp)) {
a = newnu->pntsu * nu->pntsv;
while (a--) {
mul_m4_v3(cmat, bp->vec);
bp++;
}
}
}
}
ED_object_base_free_and_unlink(bmain, scene, ob_iter);
}
}
}
CTX_DATA_END;
cu = ob_active->data;
BLI_movelisttolist(&cu->nurb, &tempbase);
if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) {
/* Account for mixed 2D/3D curves when joining */
BKE_curve_dimension_update(cu);
}
DEG_relations_tag_update(bmain); /* because we removed object(s), call before editmode! */
DEG_id_tag_update(&ob_active->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Clear Tilt Operator
* \{ */
static int clear_tilt_exec(bContext *C, wmOperator *UNUSED(op))
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
ListBase *editnurb = object_editcurve_get(obedit);
BezTriple *bezt;
BPoint *bp;
int a;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->bezt) {
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
bezt->tilt = 0.0;
}
bezt++;
}
}
else if (nu->bp) {
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
if (bp->f1 & SELECT) {
bp->tilt = 0.0f;
}
bp++;
}
}
}
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void CURVE_OT_tilt_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Tilt";
ot->idname = "CURVE_OT_tilt_clear";
ot->description = "Clear the tilt of selected control points";
/* api callbacks */
ot->exec = clear_tilt_exec;
ot->poll = ED_operator_editcurve;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
{
memcpy(dst, src, count * sizeof(BezTriple));
keyIndex_updateBezt(editnurb, src, dst, count);
}
void ED_curve_bpcpy(EditNurb *editnurb, BPoint *dst, BPoint *src, int count)
{
memcpy(dst, src, count * sizeof(BPoint));
keyIndex_updateBP(editnurb, src, dst, count);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Match Texture Space Operator
* \{ */
static bool match_texture_space_poll(bContext *C)
{
Object *object = CTX_data_active_object(C);
return object && ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT);
}
static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op))
{
/* Need to ensure the dependency graph is fully evaluated, so the display list is at a correct
* state. */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
(void)depsgraph;
Object *object = CTX_data_active_object(C);
Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
Curve *curve = (Curve *)object->data;
float min[3], max[3], size[3], loc[3];
int a;
BLI_assert(object_eval->runtime.curve_cache != NULL);
INIT_MINMAX(min, max);
BKE_displist_minmax(&object_eval->runtime.curve_cache->disp, min, max);
mid_v3_v3v3(loc, min, max);
size[0] = (max[0] - min[0]) / 2.0f;
size[1] = (max[1] - min[1]) / 2.0f;
size[2] = (max[2] - min[2]) / 2.0f;
for (a = 0; a < 3; a++) {
if (size[a] == 0.0f) {
size[a] = 1.0f;
}
else if (size[a] > 0.0f && size[a] < 0.00001f) {
size[a] = 0.00001f;
}
else if (size[a] < 0.0f && size[a] > -0.00001f) {
size[a] = -0.00001f;
}
}
copy_v3_v3(curve->loc, loc);
copy_v3_v3(curve->size, size);
curve->texflag &= ~CU_AUTOSPACE;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curve);
DEG_id_tag_update(&curve->id, ID_RECALC_GEOMETRY);
return OPERATOR_FINISHED;
}
void CURVE_OT_match_texture_space(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Match Texture Space";
ot->idname = "CURVE_OT_match_texture_space";
ot->description = "Match texture space to object's bounding box";
/* api callbacks */
ot->exec = match_texture_space_exec;
ot->poll = match_texture_space_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */