/* * ***** BEGIN GPL LICENSE BLOCK ***** * * 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. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/editors/curve/editcurve.c * \ingroup edcurve */ #include #include #ifndef WIN32 #include #else #include #endif #include #include "DNA_key_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_anim_types.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_dynstr.h" #include "BLI_rand.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_animsys.h" #include "BKE_action.h" #include "WM_api.h" #include "WM_types.h" #include "ED_keyframes_edit.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_types.h" #include "ED_util.h" #include "ED_view3d.h" #include "ED_curve.h" #include "curve_intern.h" #include "UI_interface.h" #include "UI_resources.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" /* Undo stuff */ typedef struct { ListBase nubase; void *lastsel; GHash *undoIndex; ListBase fcurves, drivers; int actnu; } UndoCurve; /* Definitions needed for shape keys */ typedef struct { void *orig_cv; int key_index, nu_index, pt_index; int switched; Nurb *orig_nu; } CVKeyIndex; void selectend_nurb(Object *obedit, short selfirst, short doswap, short selstatus); static void select_adjacent_cp(ListBase *editnurb, short next, short cont, short selstatus); /* still need to eradicate a few :( */ #define callocstructN(x, y, name) (x*)MEM_callocN((y)* sizeof(x), name) static float nurbcircle[8][2]= { {0.0, -1.0}, {-1.0, -1.0}, {-1.0, 0.0}, {-1.0, 1.0}, {0.0, 1.0}, { 1.0, 1.0}, { 1.0, 0.0}, { 1.0, -1.0} }; 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; } /* this replaces the active flag used in uv/face mode */ static void set_actNurb(Object *obedit, Nurb *nu) { Curve *cu= obedit->data; if (nu==NULL) cu->actnu = -1; else { ListBase *nurbs= BKE_curve_editNurbs_get(cu); cu->actnu = BLI_findindex(nurbs, nu); } } static Nurb *get_actNurb(Object *obedit) { Curve *cu= obedit->data; ListBase *nurbs= BKE_curve_editNurbs_get(cu); return BLI_findlink(nurbs, cu->actnu); } /* ******************* SELECTION FUNCTIONS ********************* */ #define HIDDEN 1 #define VISIBLE 0 #define FIRST 1 #define LAST 0 /* returns 1 in case (de)selection was successful */ static short select_beztriple(BezTriple *bezt, short selstatus, short flag, short hidden) { if (bezt) { if ((bezt->hide==0) || (hidden==1)) { if (selstatus==1) { /* selects */ bezt->f1 |= flag; bezt->f2 |= flag; bezt->f3 |= flag; return 1; } else { /* deselects */ bezt->f1 &= ~flag; bezt->f2 &= ~flag; bezt->f3 &= ~flag; return 1; } } } return 0; } /* returns 1 in case (de)selection was successful */ static short select_bpoint(BPoint *bp, short selstatus, short flag, short hidden) { if (bp) { if ((bp->hide==0) || (hidden==1)) { if (selstatus==1) { bp->f1 |= flag; return 1; } else { bp->f1 &= ~flag; return 1; } } } return 0; } static short swap_selection_beztriple(BezTriple *bezt) { if (bezt->f2 & SELECT) return select_beztriple(bezt, DESELECT, 1, VISIBLE); else return select_beztriple(bezt, SELECT, 1, VISIBLE); } static short swap_selection_bpoint(BPoint *bp) { if (bp->f1 & SELECT) return select_bpoint(bp, DESELECT, 1, VISIBLE); else return select_bpoint(bp, SELECT, 1, VISIBLE); } int isNurbsel(Nurb *nu) { BezTriple *bezt; BPoint *bp; int a; if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if ( (bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT) ) return 1; bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if ( (bp->f1 & SELECT) ) return 1; bp++; } } return 0; } static int isNurbsel_count(Curve *cu, Nurb *nu) { BezTriple *bezt; BPoint *bp; int a, sel=0; if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) sel++; bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if ( (bp->f1 & SELECT) ) sel++; bp++; } } return sel; } /* ******************* PRINTS ********************* */ void printknots(Object *obedit) { ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; int a, num; for (nu= editnurb->first; nu; nu= nu->next) { if (isNurbsel(nu) && nu->type == CU_NURBS) { if (nu->knotsu) { num= KNOTSU(nu); for (a=0;aknotsu[a]); } if (nu->knotsv) { num= KNOTSV(nu); for (a=0;aknotsv[a]); } } } } /* ********************* Shape keys *************** */ static CVKeyIndex *init_cvKeyIndex(void *cv, int key_index, int nu_index, int pt_index, Nurb *orig_nu) { CVKeyIndex *cvIndex = MEM_callocN(sizeof(CVKeyIndex), "init_cvKeyIndex"); cvIndex->orig_cv= cv; cvIndex->key_index= key_index; cvIndex->nu_index= nu_index; cvIndex->pt_index= pt_index; cvIndex->switched= 0; cvIndex->orig_nu= orig_nu; 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; if (editnurb->keyindex) return; gh= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "editNurb keyIndex"); while (orignu) { if (orignu->bezt) { a= orignu->pntsu; bezt= nu->bezt; origbezt= orignu->bezt; pt_index= 0; while (a--) { keyIndex= init_cvKeyIndex(origbezt, key_index, nu_index, pt_index, orignu); BLI_ghash_insert(gh, bezt, keyIndex); key_index+= 12; bezt++; origbezt++; pt_index++; } } else { a= orignu->pntsu * orignu->pntsv; bp= nu->bp; origbp= orignu->bp; pt_index= 0; while (a--) { keyIndex= init_cvKeyIndex(origbp, key_index, nu_index, pt_index, orignu); BLI_ghash_insert(gh, bp, keyIndex); key_index+= 4; bp++; origbp++; pt_index++; } } nu= nu->next; orignu= orignu->next; nu_index++; } editnurb->keyindex= gh; } static CVKeyIndex *getCVKeyIndex(EditNurb *editnurb, void *cv) { return BLI_ghash_lookup(editnurb->keyindex, cv); } static BezTriple *getKeyIndexOrig_bezt(EditNurb *editnurb, 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_delCV(EditNurb *editnurb, void *cv) { if (!editnurb->keyindex) { return; } BLI_ghash_remove(editnurb->keyindex, cv, NULL, (GHashValFreeFP)MEM_freeN); } static void keyIndex_delBezt(EditNurb *editnurb, BezTriple *bezt) { keyIndex_delCV(editnurb, bezt); } static void keyIndex_delBP(EditNurb *editnurb, BPoint *bp) { keyIndex_delCV(editnurb, bp); } static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu) { int a; if (!editnurb->keyindex) { return; } if (nu->bezt) { BezTriple *bezt= nu->bezt; a= nu->pntsu; while (a--) { BLI_ghash_remove(editnurb->keyindex, bezt, NULL, (GHashValFreeFP)MEM_freeN); ++bezt; } } else { BPoint *bp= nu->bp; a= nu->pntsu * nu->pntsv; while (a--) { BLI_ghash_remove(editnurb->keyindex, bp, NULL, (GHashValFreeFP)MEM_freeN); ++bp; } } } static void keyIndex_delNurbList(EditNurb *editnurb, ListBase *nubase) { Nurb *nu= nubase->first; while (nu) { keyIndex_delNurb(editnurb, nu); nu= nu->next; } } 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= getCVKeyIndex(editnurb, cv); BLI_ghash_remove(editnurb->keyindex, cv, NULL, NULL); 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)); } static void keyIndex_updateNurb(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= getCVKeyIndex(editnurb, a); CVKeyIndex *index2= getCVKeyIndex(editnurb, b); BLI_ghash_remove(editnurb->keyindex, a, NULL, NULL); BLI_ghash_remove(editnurb->keyindex, b, NULL, NULL); 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; bpntsv; 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) { KeyBlock *currkey; EditNurb *editnurb= cu->editnurb; ListBase *nubase= &editnurb->nurbs; Nurb *nu; float *fp; int a; currkey = cu->key->block.first; while (currkey) { fp= currkey->data; nu= nubase->first; while (nu) { 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 += 12; } bezt++; } } else fp += a * 12; } 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 += 4; } bp++; } } else fp += a * 4; } nu= nu->next; } currkey= currkey->next; } } 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); } static GHash *dupli_keyIndexHash(GHash *keyindex) { GHash *gh; GHashIterator *hashIter; gh= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "dupli_keyIndex gh"); for (hashIter = BLI_ghashIterator_new(keyindex); !BLI_ghashIterator_isDone(hashIter); BLI_ghashIterator_step(hashIter)) { void *cv = BLI_ghashIterator_getKey(hashIter); CVKeyIndex *index = BLI_ghashIterator_getValue(hashIter); CVKeyIndex *newIndex = MEM_callocN(sizeof(CVKeyIndex), "dupli_keyIndexHash index"); memcpy(newIndex, index, sizeof(CVKeyIndex)); BLI_ghash_insert(gh, cv, newIndex); } BLI_ghashIterator_free(hashIter); 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->alfa= key[9]; } static void bezt_to_key(BezTriple *bezt, float *key) { memcpy(key, bezt->vec, sizeof(float) * 9); key[9] = bezt->alfa; } static void calc_keyHandles(ListBase *nurb, float *key) { Nurb *nu; int a; float *fp= key; BezTriple *bezt; nu= nurb->first; while (nu) { 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+(12 * (a-1)); } else { prevp= NULL; prevfp= NULL; } nextp= bezt + 1; nextfp= fp + 12; 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); 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 += 12; } ++bezt; fp += 12; } } else { a= nu->pntsu * nu->pntsv; fp += a * 4; } nu= nu->next; } } static void calc_shapeKeys(Object *obedit) { Curve *cu= (Curve*)obedit->data; /* are there keys? */ if (cu->key) { int a, i; EditNurb *editnurb= cu->editnurb; KeyBlock *currkey; KeyBlock *actkey= BLI_findlink(&cu->key->block, editnurb->shapenr-1); BezTriple *bezt, *oldbezt; BPoint *bp, *oldbp; Nurb *nu; int totvert= BKE_nurbList_verts_count(&editnurb->nurbs); float (*ofs)[3] = NULL; float *oldkey, *newkey, *ofp; /* editing the base key should update others */ if (cu->key->type==KEY_RELATIVE) { int act_is_basis = 0; /* find if this key is a basis for any others */ for (currkey = cu->key->block.first; currkey; currkey= currkey->next) { if (editnurb->shapenr-1 == currkey->relative) { act_is_basis = 1; break; } } if (act_is_basis) { /* active key is a base */ int totvec= 0; /* Calculate needed memory to store offset */ nu= editnurb->nurbs.first; while (nu) { if (nu->bezt) { /* Three vects to store handles and one for alfa */ totvec+= nu->pntsu * 4; } else { totvec+= 2 * nu->pntsu * nu->pntsv; } nu= nu->next; } ofs= MEM_callocN(sizeof(float) * 3 * totvec, "currkey->data"); nu= editnurb->nurbs.first; i= 0; while (nu) { 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->alfa - oldbezt->alfa; } 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->alfa - oldbp->alfa; } i += 2; ++bp; } } nu= nu->next; } } } currkey = cu->key->block.first; while (currkey) { int 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; nu= editnurb->nurbs.first; i = 0; while (nu) { if (currkey == actkey) { int restore= actkey != cu->key->refkey; if (nu->bezt) { bezt= nu->bezt; a= nu->pntsu; while (a--) { int j; oldbezt= getKeyIndexOrig_bezt(editnurb, bezt); for (j= 0; j < 3; ++j, ++i) { copy_v3_v3(fp, bezt->vec[j]); if (restore && oldbezt) { copy_v3_v3(bezt->vec[j], oldbezt->vec[j]); } fp+= 3; } fp[0]= bezt->alfa; if (restore && oldbezt) { bezt->alfa= oldbezt->alfa; } fp+= 3; ++i;/* alphas */ ++bezt; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { oldbp= getKeyIndexOrig_bp(editnurb, bp); copy_v3_v3(fp, bp->vec); fp[3]= bp->alfa; if (restore && oldbp) { copy_v3_v3(bp->vec, oldbp->vec); bp->alfa= oldbp->alfa; } fp+= 4; ++bp; i+=2; } } } else { int index; 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, curofp); if (apply_offset) { add_v3_v3(fp, ofs[i]); } fp+= 3; curofp+= 3; } fp[0]= curofp[0]; if (apply_offset) { /* apply alfa offsets */ add_v3_v3(fp, ofs[i]); ++i; } fp+= 3; /* alphas */ } else { int j; for (j= 0; j < 3; ++j, ++i) { copy_v3_v3(fp, bezt->vec[j]); fp+= 3; } fp[0]= bezt->alfa; fp+= 3; /* alphas */ } ++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]; if (apply_offset) { add_v3_v3(fp, ofs[i]); fp[3]+=ofs[i+1][0]; } } else { copy_v3_v3(fp, bp->vec); fp[3]= bp->alfa; } fp+= 4; ++bp; i+=2; } } } } nu= nu->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; currkey= currkey->next; } if (ofs) MEM_freeN(ofs); } } /* ********************* Amimation data *************** */ static int 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 *ad, char *orig_rna_path, char *rna_path, ListBase *orig_curves, ListBase *curves) { FCurve *fcu, *nfcu, *nextfcu; int len= strlen(orig_rna_path); fcu= orig_curves->first; while (fcu) { nextfcu= fcu->next; if (!strncmp(fcu->rna_path, orig_rna_path, len)) { char *spath, *suffix= fcu->rna_path + len; nfcu= copy_fcurve(fcu); spath= nfcu->rna_path; nfcu->rna_path= BLI_sprintfN("%s%s", rna_path, suffix); BLI_addtail(curves, nfcu); if (fcu->grp) { action_groups_remove_channel(ad->action, fcu); action_groups_add_channel(ad->action, fcu->grp, nfcu); } else if (ad->action && &ad->action->curves == orig_curves) BLI_remlink(&ad->action->curves, fcu); else BLI_remlink(&ad->drivers, fcu); free_fcurve(fcu); MEM_freeN(spath); } fcu= nextfcu; } } static void fcurve_remove(AnimData *ad, ListBase *orig_curves, FCurve *fcu) { if (orig_curves==&ad->drivers) BLI_remlink(&ad->drivers, fcu); else action_groups_remove_channel(ad->action, fcu); free_fcurve(fcu); } static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves) { int nu_index= 0, a, pt_index; EditNurb *editnurb= cu->editnurb; Nurb *nu= editnurb->nurbs.first; CVKeyIndex *keyIndex; char rna_path[64], orig_rna_path[64]; AnimData *ad= BKE_animdata_from_id(&cu->id); ListBase curves= {NULL, NULL}; FCurve *fcu, *next; while (nu) { 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(ad, 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(ad, orig_handle_path, handle_path, orig_curves, &curves); } fcurve_path_rename(ad, 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(ad, orig_rna_path, rna_path, orig_curves, &curves); keyIndex->nu_index= nu_index; keyIndex->pt_index= pt_index; } bp++; pt_index++; } } nu= nu->next; nu_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 */ for (fcu= orig_curves->first; fcu; fcu= next) { next= fcu->next; if (!strncmp(fcu->rna_path, "splines", 7)) { char *ch= strchr(fcu->rna_path, '.'); if (ch && (!strncmp(ch, ".bezier_points", 14) || !strncmp(ch, ".points", 7))) fcurve_remove(ad, orig_curves, fcu); } } nu_index= 0; nu= editnurb->nurbs.first; while (nu) { 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(ad, orig_rna_path, rna_path, orig_curves, &curves); } nu_index++; nu= nu->next; } /* the remainders in orig_curves can be copied back (like follow path) */ /* (if it's not path to spline) */ for (fcu= orig_curves->first; fcu; fcu= next) { next= fcu->next; if (!strncmp(fcu->rna_path, "splines", 7)) fcurve_remove(ad, orig_curves, fcu); else BLI_addtail(&curves, fcu); } *orig_curves= curves; } /* return 0 if animation data wasn't changed, 1 otherwise */ int ED_curve_updateAnimPaths(Curve *cu) { AnimData *ad= BKE_animdata_from_id(&cu->id); if (!curve_is_animated(cu)) return 0; if (ad->action) curve_rename_fcurves(cu, &ad->action->curves); curve_rename_fcurves(cu, &ad->drivers); return 1; } /* ********************* LOAD and MAKE *************** */ /* load editNurb in object */ void load_editNurb(Object *obedit) { ListBase *editnurb= object_editcurve_get(obedit); if (obedit==NULL) return; set_actNurb(obedit, NULL); if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { Curve *cu= obedit->data; Nurb *nu, *newnu; ListBase newnurb= {NULL, NULL}, oldnurb= cu->nurb; for (nu= editnurb->first; nu; nu= nu->next) { newnu= BKE_nurb_duplicate(nu); BLI_addtail(&newnurb, newnu); if (nu->type == CU_NURBS) { BKE_nurb_order_clamp_u(nu); } } cu->nurb= newnurb; calc_shapeKeys(obedit); ED_curve_updateAnimPaths(obedit->data); BKE_nurbList_free(&oldnurb); } set_actNurb(obedit, NULL); } /* make copy in cu->editnurb */ void make_editNurb(Object *obedit) { Curve *cu= (Curve*)obedit->data; EditNurb *editnurb= cu->editnurb; Nurb *nu, *newnu, *nu_act= NULL; KeyBlock *actkey; set_actNurb(obedit, NULL); if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { actkey= ob_get_keyblock(obedit); if (actkey) { // XXX strcpy(G.editModeTitleExtra, "(Key) "); undo_editmode_clear(); key_to_curve(actkey, cu, &cu->nurb); } if (editnurb) { BKE_nurbList_free(&editnurb->nurbs); BKE_curve_editNurb_keyIndex_free(editnurb); editnurb->keyindex= NULL; } else { editnurb= MEM_callocN(sizeof(EditNurb), "editnurb"); cu->editnurb= editnurb; } nu= cu->nurb.first; cu->lastsel= NULL; /* for select row */ while (nu) { newnu= BKE_nurb_duplicate(nu); BKE_nurb_test2D(newnu); // after join, or any other creation of curve BLI_addtail(&editnurb->nurbs, newnu); if (nu_act == NULL && isNurbsel(nu)) { nu_act= newnu; set_actNurb(obedit, newnu); } nu= nu->next; } if (actkey) editnurb->shapenr= obedit->shapenr; /* animation could be added in editmode even if there was no animdata i * object mode hence we always need CVs index be created */ init_editNurb_keyIndex(editnurb, &cu->nurb); } } void free_editNurb(Object *obedit) { Curve *cu= obedit->data; BKE_curve_editNurb_free(cu); } void CU_deselect_all(Object *obedit) { ListBase *editnurb= object_editcurve_get(obedit); if (editnurb) { selectend_nurb(obedit, FIRST, 0, DESELECT); /* set first control points as unselected */ select_adjacent_cp(editnurb, 1, 1, DESELECT); /* cascade selection */ } } void CU_select_all(Object *obedit) { ListBase *editnurb= object_editcurve_get(obedit); if (editnurb) { selectend_nurb(obedit, FIRST, 0, SELECT); /* set first control points as unselected */ select_adjacent_cp(editnurb, 1, 1, SELECT); /* cascade selection */ } } void CU_select_swap(Object *obedit) { ListBase *editnurb= object_editcurve_get(obedit); if (editnurb) { Curve *cu= obedit->data; Nurb *nu; BPoint *bp; BezTriple *bezt; int a; cu->lastsel= NULL; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if (bezt->hide==0) { bezt->f2 ^= SELECT; /* always do the center point */ if ((cu->drawflag & CU_HIDE_HANDLES)==0) { bezt->f1 ^= SELECT; bezt->f3 ^= SELECT; } } bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { swap_selection_bpoint(bp); bp++; } } } } } /******************** separate operator ***********************/ static int separate_exec(bContext *C, wmOperator *op) { Main *bmain= CTX_data_main(C); Scene *scene= CTX_data_scene(C); Nurb *nu, *nu1; Object *oldob, *newob; Base *oldbase, *newbase; Curve *oldcu, *newcu; EditNurb *oldedit, *newedit; oldbase= CTX_data_active_base(C); oldob= oldbase->object; oldcu= oldob->data; oldedit= oldcu->editnurb; if (oldcu->key) { BKE_report(op->reports, RPT_ERROR, "Can't separate a curve with vertex keys"); return OPERATOR_CANCELLED; } WM_cursor_wait(1); /* 1. duplicate the object and data */ newbase= ED_object_add_duplicate(bmain, scene, oldbase, 0); /* 0 = fully linked */ ED_base_object_select(newbase, BA_DESELECT); newob= newbase->object; newcu= newob->data= BKE_curve_copy(oldcu); newcu->editnurb= NULL; oldcu->id.us--; /* because new curve is a copy: reduce user count */ /* 2. put new object in editmode and clear it */ make_editNurb(newob); newedit= newcu->editnurb; BKE_nurbList_free(&newedit->nurbs); BKE_curve_editNurb_keyIndex_free(newedit); /* 3. move over parts from old object */ for (nu= oldedit->nurbs.first; nu; nu=nu1) { nu1= nu->next; if (isNurbsel(nu)) { BLI_remlink(&oldedit->nurbs, nu); BLI_addtail(&newedit->nurbs, nu); } } /* 4. put old object out of editmode */ load_editNurb(newob); free_editNurb(newob); DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */ DAG_id_tag_update(&newob->id, OB_RECALC_DATA); /* this is the separated one */ WM_event_add_notifier(C, NC_GEOM|ND_DATA, oldob->data); WM_cursor_wait(0); return OPERATOR_FINISHED; } void CURVE_OT_separate(wmOperatorType *ot) { /* identifiers */ ot->name = "Separate"; ot->idname = "CURVE_OT_separate"; /* api callbacks */ ot->exec = separate_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /* ******************* FLAGS ********************* */ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) { /* 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; *u= *v= -1; bp= nu->bp; for (b=0; bpntsv; b++) { sel= 0; for (a=0; apntsu; a++, bp++) { if (bp->f1 & flag) sel++; } if (sel==nu->pntsu) { if (*u== -1) *u= b; else return 0; } else if (sel>1) return 0; /* because sel==1 is still ok */ } for (a=0; apntsu; a++) { sel= 0; bp= nu->bp+a; for (b=0; bpntsv; b++, bp+=nu->pntsu) { if (bp->f1 & flag) sel++; } if (sel==nu->pntsv) { if (*v== -1) *v= a; else return 0; } else if (sel>1) return 0; } if (*u==-1 && *v>-1) return 1; if (*v==-1 && *u>-1) return 1; return 0; } static void setflagsNurb(ListBase *editnurb, short flag) { Nurb *nu; BezTriple *bezt; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { a= nu->pntsu; bezt= nu->bezt; while (a--) { bezt->f1= bezt->f2= bezt->f3= flag; bezt++; } } else { a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { bp->f1= flag; bp++; } } } } static void rotateflagNurb(ListBase *editnurb, short flag, float *cent, float rotmat[][3]) { /* all verts with (flag & 'flag') rotate */ Nurb *nu; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { 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++; } } } } static void translateflagNurb(ListBase *editnurb, short flag, const float vec[3]) { /* all verts with ('flag' & flag) translate */ Nurb *nu; BezTriple *bezt; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { 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++; } } BKE_nurb_test2D(nu); } } static void weightflagNurb(ListBase *editnurb, short flag, float w) { Nurb *nu; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { 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 int deleteflagNurb(bContext *C, wmOperator *UNUSED(op), int flag) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu, *next; BPoint *bp, *bpn, *newbp; int a, b, newu, newv, sel; if (obedit->type==OB_SURF); else return OPERATOR_CANCELLED; cu->lastsel= NULL; nu= editnurb->first; while (nu) { next= nu->next; /* is entire nurb selected */ bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a) { a--; if (bp->f1 & flag); else break; bp++; } if (a==0) { BLI_remlink(editnurb, nu); keyIndex_delNurb(cu->editnurb, nu); BKE_nurb_free(nu); nu=NULL; } else { /* is nurb in U direction selected */ newv= nu->pntsv; bp= nu->bp; for (b=0; bpntsv; b++) { sel= 0; for (a=0; apntsu; a++, bp++) { if (bp->f1 & flag) sel++; } if (sel==nu->pntsu) { newv--; } else if (sel>=1) { /* don't delete */ break; } } if (newv!=nu->pntsv && b==nu->pntsv) { /* delete */ bp= nu->bp; bpn = newbp = (BPoint*) MEM_mallocN(newv * nu->pntsu * sizeof(BPoint), "deleteNurb"); for (b=0; bpntsv; b++) { if ((bp->f1 & flag)==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 { /* is the nurb in V direction selected */ newu= nu->pntsu; for (a=0; apntsu; a++) { bp= nu->bp+a; sel= 0; for (b=0; bpntsv; b++, bp+=nu->pntsu) { if (bp->f1 & flag) sel++; } if (sel==nu->pntsv) { newu--; } else if (sel>=1) { /* don't delete */ break; } } if (newu!=nu->pntsu && a==nu->pntsu) { /* delete */ bp= nu->bp; bpn = newbp = (BPoint*) MEM_mallocN(newu * nu->pntsv * sizeof(BPoint), "deleteNurb"); for (b=0; bpntsv; b++) { for (a=0; apntsu; a++, bp++) { if ((bp->f1 & flag)==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); } } } nu= next; } if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); return OPERATOR_FINISHED; } /* only for OB_SURF */ static short extrudeflagNurb(EditNurb *editnurb, int flag) { Nurb *nu; BPoint *bp, *bpn, *newbp; int ok= 0, a, u, v, len; nu= editnurb->nurbs.first; while (nu) { if (nu->pntsv==1) { bp= nu->bp; a= nu->pntsu; while (a) { if (bp->f1 & flag); else break; bp++; a--; } if (a==0) { ok= 1; 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, &u, &v, flag) ) { /* deselect all */ bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { select_bpoint(bp, DESELECT, flag, HIDDEN); bp++; } if (u==0 || u== nu->pntsv-1) { /* row in u-direction selected */ ok= 1; 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 (v==0 || v== nu->pntsu-1) { /* column in v-direction selected */ ok= 1; bpn = newbp = (BPoint*) MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint), "extrudeNurb1"); bp= nu->bp; for (a=0; apntsv; 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); } } } nu= nu->next; } return ok; } static void adduplicateflagNurb(Object *obedit, short flag) { ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu, *newnu; BezTriple *bezt, *bezt1; BPoint *bp, *bp1; Curve *cu= (Curve*)obedit->data; int a, b, starta, enda, newu, newv; char *usel; cu->lastsel= NULL; nu= editnurb->last; while (nu) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; for (a=0; apntsu; a++) { enda= -1; starta= a; while ( (bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag) ) { select_beztriple(bezt, DESELECT, flag, HIDDEN); enda=a; if (a>=nu->pntsu-1) break; a++; bezt++; } if (enda>=starta) { newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN"); memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(editnurb, newnu); set_actNurb(obedit, newnu); newnu->pntsu= enda-starta+1; newnu->bezt= (BezTriple*)MEM_mallocN((enda - starta + 1) * sizeof(BezTriple), "adduplicateN"); memcpy(newnu->bezt, nu->bezt+starta, newnu->pntsu*sizeof(BezTriple)); b= newnu->pntsu; bezt1= newnu->bezt; while (b--) { select_beztriple(bezt1, SELECT, flag, HIDDEN); bezt1++; } if (nu->flagu & CU_NURB_CYCLIC) { if (starta!=0 || enda!=nu->pntsu-1) { newnu->flagu &= ~CU_NURB_CYCLIC; } } } bezt++; } } else if (nu->pntsv==1) { /* because UV Nurb has a different method for dupli */ bp= nu->bp; for (a=0; apntsu; a++) { enda= -1; starta= a; while (bp->f1 & flag) { select_bpoint(bp, DESELECT, flag, HIDDEN); enda= a; if (a>=nu->pntsu-1) break; a++; bp++; } if (enda>=starta) { newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN3"); memcpy(newnu, nu, sizeof(Nurb)); set_actNurb(obedit, newnu); BLI_addtail(editnurb, newnu); newnu->pntsu= enda-starta+1; newnu->bp = (BPoint*)MEM_mallocN((enda-starta+1) * sizeof(BPoint), "adduplicateN4"); memcpy(newnu->bp, nu->bp+starta, newnu->pntsu*sizeof(BPoint)); b= newnu->pntsu; bp1= newnu->bp; while (b--) { select_bpoint(bp1, SELECT, flag, HIDDEN); bp1++; } if (nu->flagu & CU_NURB_CYCLIC) { if (starta!=0 || enda!=nu->pntsu-1) { newnu->flagu &= ~CU_NURB_CYCLIC; } } /* knots */ newnu->knotsu= NULL; BKE_nurb_knot_calc_u(newnu); } bp++; } } else { /* a rectangular area in nurb has to be selected */ if (isNurbsel(nu)) { usel= MEM_callocN(nu->pntsu, "adduplicateN4"); bp= nu->bp; for (a=0; apntsv; a++) { for (b=0; bpntsu; b++, bp++) { if (bp->f1 & flag) usel[b]++; } } newu= 0; newv= 0; for (a=0; apntsu; a++) { if (usel[a]) { if (newv==0 || usel[a]==newv) { newv= usel[a]; newu++; } else { newv= 0; break; } } } if (newu==0 || newv==0) { if (G.debug & G_DEBUG) printf("Can't duplicate Nurb\n"); } else { if (newu==1) SWAP(short, newu, newv); newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN5"); memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(editnurb, newnu); set_actNurb(obedit, newnu); newnu->pntsu= newu; newnu->pntsv= newv; newnu->bp = (BPoint*)MEM_mallocN(newu * newv * sizeof(BPoint), "adduplicateN6"); BKE_nurb_order_clamp_u(newnu); BKE_nurb_order_clamp_v(newnu); newnu->knotsu= newnu->knotsv= NULL; bp= newnu->bp; bp1= nu->bp; for (a=0; apntsv; a++) { for (b=0; bpntsu; b++, bp1++) { if (bp1->f1 & flag) { memcpy(bp, bp1, sizeof(BPoint)); select_bpoint(bp1, DESELECT, flag, HIDDEN); bp++; } } } if (BKE_nurb_check_valid_u(newnu)) { if (nu->pntsu==newnu->pntsu && nu->knotsu) { newnu->knotsu= MEM_dupallocN( nu->knotsu ); } else { BKE_nurb_knot_calc_u(newnu); } } if (BKE_nurb_check_valid_v(newnu)) { if (nu->pntsv==newnu->pntsv && nu->knotsv) { newnu->knotsv= MEM_dupallocN( nu->knotsv ); } else { BKE_nurb_knot_calc_v(newnu); } } } MEM_freeN(usel); } } nu= nu->prev; } /* actnu changed */ } /**************** switch direction operator ***************/ static int switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); Curve *cu= (Curve*)obedit->data; EditNurb *editnurb= cu->editnurb; Nurb *nu; for (nu= editnurb->nurbs.first; nu; nu= nu->next) if (isNurbsel(nu)) { BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); } if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); 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; } /****************** set weight operator *******************/ static int set_goal_weight_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BezTriple *bezt; BPoint *bp; float weight= RNA_float_get(op->ptr, "weight"); int a; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->bezt) { for (bezt=nu->bezt, a=0; apntsu; a++, bezt++) { if (bezt->f2 & SELECT) bezt->weight= weight; } } else if (nu->bp) { for (bp=nu->bp, a=0; apntsu*nu->pntsv; a++, bp++) { if (bp->f1 & SELECT) bp->weight= weight; } } } DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); 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); } /******************* set radius operator ******************/ static int set_radius_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BezTriple *bezt; BPoint *bp; float radius= RNA_float_get(op->ptr, "radius"); int a; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->bezt) { for (bezt=nu->bezt, a=0; apntsu; a++, bezt++) { if (bezt->f2 & SELECT) bezt->radius= radius; } } else if (nu->bp) { for (bp=nu->bp, a=0; apntsu*nu->pntsv; a++, bp++) { if (bp->f1 & SELECT) bp->radius= radius; } } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); 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, FLT_MAX, "Radius", "", 0.0001f, 10.0f); } /********************* smooth operator ********************/ static int smooth_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BezTriple *bezt, *beztOrig; BPoint *bp, *bpOrig; float val, newval, offset; int a, i, change = 0; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->bezt) { change = 0; beztOrig = MEM_dupallocN( nu->bezt ); for (bezt=nu->bezt+1, a=1; apntsu-1; a++, bezt++) { if (bezt->f2 & SELECT) { for (i=0; i<3; i++) { val = bezt->vec[1][i]; newval = ((beztOrig+(a-1))->vec[1][i] * 0.5f) + ((beztOrig+(a+1))->vec[1][i] * 0.5f); offset = (val*((1.0f/6.0f)*5.0f)) + (newval*(1.0f/6.0f)) - val; /* offset handles */ bezt->vec[1][i] += offset; bezt->vec[0][i] += offset; bezt->vec[2][i] += offset; } change = 1; } } MEM_freeN(beztOrig); if (change) BKE_nurb_handles_calc(nu); } else if (nu->bp) { bpOrig = MEM_dupallocN( nu->bp ); /* Same as above, keep these the same! */ for (bp=nu->bp+1, a=1; apntsu-1; a++, bp++) { if (bp->f1 & SELECT) { for (i=0; i<3; i++) { val = bp->vec[i]; newval = ((bpOrig+(a-1))->vec[i] * 0.5f) + ((bpOrig+(a+1))->vec[i] * 0.5f); offset = (val*((1.0f/6.0f)*5.0f)) + (newval*(1.0f/6.0f)) - val; bp->vec[i] += offset; } } } MEM_freeN(bpOrig); } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); 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; } /**************** smooth curve radius operator *************/ /* TODO, make smoothing distance based */ static int smooth_radius_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; 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; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->bezt) { 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; apntsu; 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; apntsu; 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 = -1.0; if (start_sel == end_sel) { /* simple, only 1 point selected */ if (start_sel>0) start_rad = (nu->bezt+start_sel-1)->radius; if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bezt+start_sel+1)->radius; if (start_rad >= 0.0f && end_rad >= 0.0f) (nu->bezt+start_sel)->radius = (start_rad + end_rad)/2; else if (start_rad >= 0.0f) (nu->bezt+start_sel)->radius = start_rad; else if (end_rad >= 0.0f) (nu->bezt+start_sel)->radius = end_rad; } else { /* if endpoints selected, then use them */ if (start_sel==0) { start_rad = (nu->bezt+start_sel)->radius; start_sel++; /* we don't want to edit the selected endpoint */ } else { start_rad = (nu->bezt+start_sel-1)->radius; } if (end_sel==nu->pntsu-1) { end_rad = (nu->bezt+end_sel)->radius; end_sel--; /* we don't want to edit the selected endpoint */ } else { end_rad = (nu->bezt+end_sel+1)->radius; } /* 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->radius = start_rad*(1.0f-fac) + end_rad*fac; } } } } } else if (nu->bp) { /* 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; apntsu; 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; apntsu; 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 = -1.0; if (start_sel == end_sel) { /* simple, only 1 point selected */ if (start_sel>0) start_rad = (nu->bp+start_sel-1)->radius; if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bp+start_sel+1)->radius; if (start_rad >= 0.0f && end_rad >= 0.0f) (nu->bp+start_sel)->radius = (start_rad + end_rad)/2; else if (start_rad >= 0.0f) (nu->bp+start_sel)->radius = start_rad; else if (end_rad >= 0.0f) (nu->bp+start_sel)->radius = end_rad; } else { /* if endpoints selected, then use them */ if (start_sel==0) { start_rad = (nu->bp+start_sel)->radius; start_sel++; /* we don't want to edit the selected endpoint */ } else { start_rad = (nu->bp+start_sel-1)->radius; } if (end_sel==nu->pntsu-1) { end_rad = (nu->bp+end_sel)->radius; end_sel--; /* we don't want to edit the selected endpoint */ } else { end_rad = (nu->bp+end_sel+1)->radius; } /* 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->radius = start_rad*(1.0f-fac) + end_rad*fac; } } } } } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } void CURVE_OT_smooth_radius(wmOperatorType *ot) { /* identifiers */ ot->name = "Smooth Curve Radius"; ot->description = "Flatten radiuses of selected points"; ot->idname = "CURVE_OT_smooth_radius"; /* api clastbacks */ ot->exec = smooth_radius_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /***************** selection utility *************************/ /* next == 1 -> select next */ /* next == -1 -> select previous */ /* cont == 1 -> select continuously */ /* selstatus, inverts behavior */ static void select_adjacent_cp(ListBase *editnurb, short next, short cont, short selstatus) { Nurb *nu; BezTriple *bezt; BPoint *bp; int a; short lastsel= 0; if (next==0) return; for (nu= editnurb->first; nu; nu= nu->next) { lastsel=0; if (nu->type == CU_BEZIER) { a= nu->pntsu; bezt= nu->bezt; if (next < 0) bezt= (nu->bezt + (a-1)); while (a--) { if (a-abs(next) < 0) break; if ((lastsel==0) && (bezt->hide==0) && ((bezt->f2 & SELECT) || (selstatus==0))) { bezt+=next; if (!(bezt->f2 & SELECT) || (selstatus==0)) { short sel= select_beztriple(bezt, selstatus, 1, VISIBLE); if ((sel==1) && (cont==0)) lastsel= 1; } } else { bezt+=next; lastsel= 0; } /* move around in zigzag way so that we go through each */ bezt-=(next-next/abs(next)); } } else { a= nu->pntsu*nu->pntsv; bp= nu->bp; if (next < 0) bp= (nu->bp + (a-1)); while (a--) { if (a-abs(next) < 0) break; if ((lastsel==0) && (bp->hide==0) && ((bp->f1 & SELECT) || (selstatus==0))) { bp+=next; if (!(bp->f1 & SELECT) || (selstatus==0)) { short sel= select_bpoint(bp, selstatus, 1, VISIBLE); if ((sel==1) && (cont==0)) lastsel= 1; } } else { bp+=next; lastsel= 0; } /* move around in zigzag way so that we go through each */ bp-=(next-next/abs(next)); } } } } /**************** select start/end operators **************/ /* (de)selects first or last of visible part of each Nurb depending on selFirst */ /* selFirst: defines the end of which to select */ /* doswap: defines if selection state of each first/last control point is swapped */ /* selstatus: selection status in case doswap is false */ void selectend_nurb(Object *obedit, short selfirst, short doswap, short selstatus) { ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BPoint *bp; BezTriple *bezt; Curve *cu; int a; if (obedit==NULL) return; cu= (Curve*)obedit->data; cu->lastsel= NULL; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { a= nu->pntsu; /* which point? */ if (selfirst==0) { /* select last */ bezt= (nu->bezt + (a-1)); } else { /* select first */ bezt= nu->bezt; } while (a--) { short sel; if (doswap) sel= swap_selection_beztriple(bezt); else sel= select_beztriple(bezt, selstatus, 1, VISIBLE); if (sel==1) break; } } else { a= nu->pntsu*nu->pntsv; /* which point? */ if (selfirst==0) { /* select last */ bp= (nu->bp + (a-1)); } else { /* select first */ bp= nu->bp; } while (a--) { if (bp->hide == 0) { short sel; if (doswap) sel= swap_selection_bpoint(bp); else sel= select_bpoint(bp, selstatus, 1, VISIBLE); if (sel==1) break; } } } } } static int de_select_first_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); selectend_nurb(obedit, FIRST, 1, DESELECT); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_de_select_first(wmOperatorType *ot) { /* identifiers */ ot->name = "(De)select First"; ot->idname = "CURVE_OT_de_select_first"; /* api cfirstbacks */ ot->exec = de_select_first_exec; ot->poll = ED_operator_editcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } static int de_select_last_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); selectend_nurb(obedit, LAST, 1, DESELECT); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_de_select_last(wmOperatorType *ot) { /* identifiers */ ot->name = "(De)select Last"; ot->idname = "CURVE_OT_de_select_last"; /* api clastbacks */ ot->exec = de_select_last_exec; ot->poll = ED_operator_editcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /******************* de select all operator ***************/ static short nurb_has_selected_cps(ListBase *editnurb) { Nurb *nu; BezTriple *bezt; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { a= nu->pntsu; bezt= nu->bezt; while (a--) { if (bezt->hide==0) { if ((bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT)) { return 1; } } bezt++; } } else { a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { if ((bp->hide==0) && (bp->f1 & SELECT)) return 1; bp++; } } } return 0; } static int de_select_all_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); int action = RNA_enum_get(op->ptr, "action"); if (action == SEL_TOGGLE) { action = SEL_SELECT; if (nurb_has_selected_cps(editnurb)) action = SEL_DESELECT; } switch (action) { case SEL_SELECT: CU_select_all(obedit); break; case SEL_DESELECT: CU_deselect_all(obedit); break; case SEL_INVERT: CU_select_swap(obedit); break; } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_all(wmOperatorType *ot) { /* identifiers */ ot->name = "(De)select All"; ot->idname = "CURVE_OT_select_all"; /* api callbacks */ ot->exec = de_select_all_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ WM_operator_properties_select_all(ot); } /********************** hide operator *********************/ static int hide_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BPoint *bp; BezTriple *bezt; int a, sel, invert= RNA_boolean_get(op->ptr, "unselected"); for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; sel= 0; while (a--) { if (invert == 0 && BEZSELECTED_HIDDENHANDLES(cu, bezt)) { select_beztriple(bezt, DESELECT, 1, HIDDEN); bezt->hide= 1; } else if (invert && !BEZSELECTED_HIDDENHANDLES(cu, bezt)) { select_beztriple(bezt, DESELECT, 1, 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, 1, HIDDEN); bp->hide= 1; } else if (invert && (bp->f1 & SELECT)==0) { select_bpoint(bp, DESELECT, 1, HIDDEN); bp->hide= 1; } if (bp->hide) sel++; bp++; } if (sel==nu->pntsu*nu->pntsv) nu->hide= 1; } } DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_hide(wmOperatorType *ot) { /* identifiers */ ot->name = "Hide Selected"; ot->idname = "CURVE_OT_hide"; /* 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"); } /********************** reveal operator *********************/ static int reveal_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BPoint *bp; BezTriple *bezt; int a; for (nu= editnurb->first; nu; nu= nu->next) { nu->hide= 0; if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if (bezt->hide) { select_beztriple(bezt, SELECT, 1, HIDDEN); bezt->hide= 0; } bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if (bp->hide) { select_bpoint(bp, SELECT, 1, HIDDEN); bp->hide= 0; } bp++; } } } DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_reveal(wmOperatorType *ot) { /* identifiers */ ot->name = "Reveal Hidden"; ot->idname = "CURVE_OT_reveal"; /* api callbacks */ ot->exec = reveal_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /********************** 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, int number_cuts) { Curve *cu= obedit->data; EditNurb *editnurb= cu->editnurb; Nurb *nu; BezTriple *prevbezt, *bezt, *beztnew, *beztn; BPoint *bp, *prevbp, *bpnew, *bpn; float vec[15]; int a, b, sel, amount, *usel, *vsel, i; float factor; // printf("*** subdivideNurb: entering subdivide\n"); for (nu= editnurb->nurbs.first; nu; nu= nu->next) { amount= 0; if (nu->type == CU_BEZIER) { /* * 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 */ if (nu->flagu & CU_NURB_CYCLIC) { a= nu->pntsu; bezt= nu->bezt; prevbezt= bezt+(a-1); } else { a= nu->pntsu-1; prevbezt= nu->bezt; bezt= prevbezt+1; } while (a--) { if ( BEZSELECTED_HIDDENHANDLES(cu, prevbezt) && BEZSELECTED_HIDDENHANDLES(cu, bezt) ) amount+=number_cuts; prevbezt= bezt; bezt++; } if (amount) { /* insert */ beztnew = (BezTriple*)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb"); beztn= beztnew; if (nu->flagu & CU_NURB_CYCLIC) { a= nu->pntsu; bezt= nu->bezt; prevbezt= bezt+(a-1); } else { a= nu->pntsu-1; prevbezt= nu->bezt; bezt= prevbezt+1; } while (a--) { memcpy(beztn, prevbezt, sizeof(BezTriple)); keyIndex_updateBezt(editnurb, prevbezt, beztn, 1); beztn++; if ( BEZSELECTED_HIDDENHANDLES(cu, prevbezt) && BEZSELECTED_HIDDENHANDLES(cu, bezt) ) { float prevvec[3][3]; memcpy(prevvec, prevbezt->vec, sizeof(float) * 9); for (i = 0; i < number_cuts; i++) { factor = 1.0f / (number_cuts + 1 - i); memcpy(beztn, bezt, sizeof(BezTriple)); /* midpoint subdividing */ interp_v3_v3v3(vec, prevvec[1], prevvec[2], factor); interp_v3_v3v3(vec+3, prevvec[2], bezt->vec[0], factor); interp_v3_v3v3(vec+6, bezt->vec[0], bezt->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(bezt->vec[0], vec+6);} beztn->radius = (prevbezt->radius + bezt->radius)/2; beztn->weight = (prevbezt->weight + bezt->weight)/2; memcpy(prevvec, beztn->vec, sizeof(float) * 9); beztn++; } } prevbezt= bezt; bezt++; } /* last point */ if ((nu->flagu & CU_NURB_CYCLIC)==0) { memcpy(beztn, prevbezt, sizeof(BezTriple)); keyIndex_updateBezt(editnurb, prevbezt, beztn, 1); } 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) { /* * 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 */ if (nu->flagu & CU_NURB_CYCLIC) { a= nu->pntsu; bp= nu->bp; prevbp= bp+(a-1); } else { a= nu->pntsu-1; prevbp= nu->bp; bp= prevbp+1; } while (a--) { if ( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) amount+=number_cuts; prevbp= bp; bp++; } if (amount) { /* insert */ bpnew = (BPoint*)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2"); bpn= bpnew; if (nu->flagu & CU_NURB_CYCLIC) { a= nu->pntsu; bp= nu->bp; prevbp= bp+(a-1); } else { a= nu->pntsu-1; prevbp= nu->bp; bp= prevbp+1; } while (a--) { memcpy(bpn, prevbp, sizeof(BPoint)); keyIndex_updateBP(editnurb, prevbp, bpn, 1); bpn++; if ( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) { // printf("*** subdivideNurb: insert 'linear' point\n"); for (i = 0; i < number_cuts; i++) { factor = (float)(i + 1) / (number_cuts + 1); memcpy(bpn, bp, sizeof(BPoint)); interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor); bpn++; } } prevbp= bp; bp++; } if ((nu->flagu & CU_NURB_CYCLIC)==0) { /* last point */ memcpy(bpn, prevbp, sizeof(BPoint)); keyIndex_updateBP(editnurb, prevbp, bpn, 1); } 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 controlpoints 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 controlpoints 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; apntsv; a++) { for (b=0; bpntsu; 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; apntsv; a++) { for (b=0; bpntsu; b++) { *bpn= *bp; keyIndex_updateBP(editnurb, bp, bpn, 1); bpn++; bp++; if (bpntsu-1) { prevbp= bp-1; for (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; apntsv; a++) { for (b=0; b<(number_cuts+1)*nu->pntsu-number_cuts; b++) { BPoint *tmp= bpn; for (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; apntsv-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; apntsv; a++) { for (b=0; bpntsu; b++) { *bpn= *bp; keyIndex_updateBP(editnurb, bp, bpn, 1); bpn++; bp++; } if ( (apntsv-1) && vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu ) { for (i = 0; i < number_cuts; i++) { factor = (float)(i + 1) / (number_cuts + 1); prevbp= bp- nu->pntsu; for (b=0; bpntsu; 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; apntsu-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; apntsv; a++) { for (b=0; bpntsu; b++) { *bpn= *bp; keyIndex_updateBP(editnurb, bp, bpn, 1); bpn++; bp++; if ( (bpntsu-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 (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) { Object *obedit= CTX_data_edit_object(C); int number_cuts= RNA_int_get(op->ptr, "number_cuts"); subdividenurb(obedit, number_cuts); if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); 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, INT_MAX, "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); } /******************** find nearest ************************/ static void findnearestNurbvert__doClosest(void *userData, Nurb *nu, BPoint *bp, BezTriple *bezt, int beztindex, int x, int y) { struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } *data = userData; short flag; short temp; if (bp) { flag = bp->f1; } else { if (beztindex==0) { flag = bezt->f1; } else if (beztindex==1) { flag = bezt->f2; } else { flag = bezt->f3; } } temp = abs(data->mval[0]-x) + abs(data->mval[1]-y); if ((flag&1)==data->select) temp += 5; if (bezt && beztindex==1) temp += 3; /* middle points get a small disadvantage */ if (tempdist) { data->dist = temp; data->bp = bp; data->bezt = bezt; data->nurb = nu; data->hpoint = bezt?beztindex:0; } } static short findnearestNurbvert(ViewContext *vc, short sel, const int mval[2], Nurb **nurb, BezTriple **bezt, BPoint **bp) { /* sel==1: selected gets a disadvantage */ /* in nurb and bezt or bp the nearest is written */ /* return 0 1 2: handlepunt */ struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } data = {NULL}; data.dist = 100; data.hpoint = 0; data.select = sel; data.mval[0] = mval[0]; data.mval[1] = mval[1]; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); nurbs_foreachScreenVert(vc, findnearestNurbvert__doClosest, &data); *nurb = data.nurb; *bezt = data.bezt; *bp = data.bp; return data.hpoint; } static void findselectedNurbvert(ListBase *editnurb, Nurb **nu, BezTriple **bezt, BPoint **bp) { /* in nu and (bezt or bp) selected are written if there's 1 sel. */ /* if more points selected in 1 spline: return only nu, bezt and bp are 0 */ Nurb *nu1; BezTriple *bezt1; BPoint *bp1; int a; *nu= NULL; *bezt= NULL; *bp= NULL; for (nu1= editnurb->first; nu1; nu1= nu1->next) { if (nu1->type == CU_BEZIER) { bezt1= nu1->bezt; a= nu1->pntsu; while (a--) { if ( (bezt1->f1 & SELECT) || (bezt1->f2 & SELECT) || (bezt1->f3 & SELECT) ) { if (*nu != NULL && *nu != nu1) { *nu= NULL; *bp= NULL; *bezt= NULL; return; } else if (*bezt || *bp) { *bp= NULL; *bezt= NULL; } else { *bezt= bezt1; *nu= nu1; } } bezt1++; } } else { bp1= nu1->bp; a= nu1->pntsu*nu1->pntsv; while (a--) { if ( bp1->f1 & 1 ) { if (*nu != NULL && *nu != nu1) { *bp= NULL; *bezt= NULL; *nu= NULL; return; } else if (*bezt || *bp) { *bp= NULL; *bezt= NULL; } else { *bp= bp1; *nu= nu1; } } bp1++; } } } } /***************** set spline type operator *******************/ static int convertspline(short type, Nurb *nu) { BezTriple *bezt; BPoint *bp; int a, c, nr; if (nu->type == CU_POLY) { if (type==CU_BEZIER) { /* to Bezier with vecthandles */ nr= nu->pntsu; bezt = (BezTriple*)MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2"); nu->bezt= bezt; a= nr; bp= nu->bp; while (a--) { copy_v3_v3(bezt->vec[1], bp->vec); bezt->f1=bezt->f2=bezt->f3= bp->f1; bezt->h1= bezt->h2= HD_VECT; bezt->weight= bp->weight; bezt->radius= bp->radius; bp++; bezt++; } MEM_freeN(nu->bp); nu->bp= NULL; nu->pntsu= nr; nu->type = CU_BEZIER; BKE_nurb_handles_calc(nu); } else if (type==CU_NURBS) { nu->type = CU_NURBS; nu->orderu= 4; nu->flagu &= CU_NURB_CYCLIC; /* disable all flags except for cyclic */ BKE_nurb_knot_calc_u(nu); a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { bp->vec[3]= 1.0; bp++; } } } else if (nu->type == CU_BEZIER) { /* Bezier */ if (type==CU_POLY || type==CU_NURBS) { nr= 3*nu->pntsu; nu->bp = MEM_callocN(nr * sizeof(BPoint), "setsplinetype"); a= nu->pntsu; bezt= nu->bezt; bp= nu->bp; while (a--) { if (type==CU_POLY && bezt->h1==HD_VECT && bezt->h2==HD_VECT) { /* vector handle becomes 1 poly vertice */ copy_v3_v3(bp->vec, bezt->vec[1]); bp->vec[3]= 1.0; bp->f1= bezt->f2; nr-= 2; bp->radius= bezt->radius; bp->weight= bezt->weight; bp++; } else { for (c=0;c<3;c++) { copy_v3_v3(bp->vec, bezt->vec[c]); bp->vec[3]= 1.0; if (c==0) bp->f1= bezt->f1; else if (c==1) bp->f1= bezt->f2; else bp->f1= bezt->f3; bp->radius= bezt->radius; bp->weight= bezt->weight; bp++; } } bezt++; } MEM_freeN(nu->bezt); nu->bezt= NULL; nu->pntsu= nr; nu->pntsv= 1; nu->orderu= 4; nu->orderv= 1; nu->type = type; #if 0 /* UNUSED */ if (nu->flagu & CU_NURB_CYCLIC) c= nu->orderu-1; else c= 0; #endif if (type== CU_NURBS) { nu->flagu &= CU_NURB_CYCLIC; /* disable all flags except for cyclic */ nu->flagu |= CU_NURB_BEZIER; BKE_nurb_knot_calc_u(nu); } } } else if (nu->type == CU_NURBS) { if (type==CU_POLY) { nu->type = CU_POLY; if (nu->knotsu) MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */ nu->knotsu= NULL; if (nu->knotsv) MEM_freeN(nu->knotsv); nu->knotsv= NULL; } else if (type==CU_BEZIER) { /* to Bezier */ nr= nu->pntsu/3; if (nr<2) return 1; /* conversion impossible */ else { bezt = MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2"); nu->bezt= bezt; a= nr; bp= nu->bp; while (a--) { copy_v3_v3(bezt->vec[0], bp->vec); bezt->f1= bp->f1; bp++; copy_v3_v3(bezt->vec[1], bp->vec); bezt->f2= bp->f1; bp++; copy_v3_v3(bezt->vec[2], bp->vec); bezt->f3= bp->f1; bezt->radius= bp->radius; bezt->weight= bp->weight; bp++; bezt++; } MEM_freeN(nu->bp); nu->bp= NULL; MEM_freeN(nu->knotsu); nu->knotsu= NULL; nu->pntsu= nr; nu->type = CU_BEZIER; } } } return 0; } void ED_nurb_set_spline_type(Nurb *nu, int type) { convertspline(type, nu); } static int set_spline_type_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; int changed=0, type= RNA_enum_get(op->ptr, "type"); if (type==CU_CARDINAL || type==CU_BSPLINE) { BKE_report(op->reports, RPT_ERROR, "Not implemented yet"); return OPERATOR_CANCELLED; } for (nu= editnurb->first; nu; nu= nu->next) { if (isNurbsel(nu)) { if (convertspline(type, nu)) BKE_report(op->reports, RPT_ERROR, "No conversion possible"); else changed= 1; } } if (changed) { if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } else { return OPERATOR_CANCELLED; } } void CURVE_OT_spline_type_set(wmOperatorType *ot) { static 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"); } /***************** set handle type operator *******************/ static int set_handle_type_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); BKE_nurbList_handles_set(editnurb, RNA_enum_get(op->ptr, "type")); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } void CURVE_OT_handle_type_set(wmOperatorType *ot) { /* keep in sync with graphkeys_handle_type_items */ static 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"); } /***************** make segment operator **********************/ /* ******************** 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; fp1= base; fp2=tempf= MEM_mallocN(sizeof(float)*a, "switchdirect"); while (a--) { fp2[0]= fabs(fp1[1]-fp1[0]); fp1++; fp2++; } 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(short, 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; vpntsv; v++) { for (u=0; upntsu; u++, bp1++) { bp2= temp + (nu->pntsu-u-1)*(nu->pntsv) + v; *bp1= *bp2; } } MEM_freeN(temp); } static int is_u_selected(Nurb *nu, int u) { BPoint *bp; int v; /* what about resolu == 2? */ bp= nu->bp+u; for (v=0; vpntsv-1; v++, bp+=nu->pntsu) { if (v) if (bp->f1 & SELECT) return 1; } return 0; } typedef struct NurbSort { struct NurbSort *next, *prev; Nurb *nu; float vec[3]; } NurbSort; static ListBase nsortbase= {NULL, NULL}; /* static NurbSort *nusmain; */ /* this var seems to go unused... at least in this file */ static void make_selection_list_nurb(ListBase *editnurb) { ListBase nbase= {NULL, NULL}; NurbSort *nus, *nustest, *headdo, *taildo; Nurb *nu; BPoint *bp; float dist, headdist, taildist; int a; for (nu= editnurb->first; nu; nu= nu->next) { if ( isNurbsel(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 (distvec, ((NurbSort *)nsortbase.last)->vec); if (distnext; } if (headdistpntsu-1) ); 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) ); else { rotate_direction_nurb(nu1); if ( is_u_selected(nu1, nu1->pntsu-1) ); else { rotate_direction_nurb(nu1); if ( is_u_selected(nu1, nu1->pntsu-1) ); else { /* rotate again, now its OK! */ if (nu1->pntsv!=1) rotate_direction_nurb(nu1); return; } } } } /* 2nd nurbs: u = 0 selected */ if ( is_u_selected(nu2, 0) ); else { if (nu2->orderv == 0) nu2->orderv= 1; rotate_direction_nurb(nu2); if ( is_u_selected(nu2, 0) ); else { rotate_direction_nurb(nu2); if ( is_u_selected(nu2, 0) ); else { rotate_direction_nurb(nu2); if ( is_u_selected(nu2, 0) ); else { /* rotate again, now its OK! */ if (nu1->pntsu==1) rotate_direction_nurb(nu1); if (nu2->pntsv!=1) rotate_direction_nurb(nu2); return; } } } } if ( nu1->pntsv != nu2->pntsv ) { BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match"); return; } /* 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; vpntsv; 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; vpntsv; 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->orderupntsu) nu1->orderu++; if (nu1->orderv<3 && nu1->ordervpntsv) nu1->orderv++; temp= nu1->bp; nu1->bp= MEM_mallocN(nu1->pntsu*nu1->pntsv*sizeof(BPoint), "mergeBP"); bp= nu1->bp; bp1= temp; for (v=0; vpntsv; v++) { /* switch direction? */ if (len1bp + v*nu2->pntsu; else bp2= nu2->bp + (nu1->pntsv-v-1)*nu2->pntsu; for (u=0; upntsu; u++, bp++) { if (utype == 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); } static int merge_nurb(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); NurbSort *nus1, *nus2; int ok= 1; make_selection_list_nurb(editnurb); if (nsortbase.first == nsortbase.last) { BLI_freelistN(&nsortbase); BKE_report(op->reports, RPT_ERROR, "Too few selections to merge"); return OPERATOR_CANCELLED; } nus1= nsortbase.first; nus2= nus1->next; /* resolution match, to avoid uv rotations */ if (nus1->nu->pntsv==1) { if (nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsu==nus2->nu->pntsv); else ok= 0; } else if (nus2->nu->pntsv==1) { if (nus2->nu->pntsu==nus1->nu->pntsu || nus2->nu->pntsu==nus1->nu->pntsv); else ok= 0; } else if ( nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsv==nus2->nu->pntsv); else if ( nus1->nu->pntsu==nus2->nu->pntsv || nus1->nu->pntsv==nus2->nu->pntsu); else { ok= 0; } if (ok==0) { BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match"); BLI_freelistN(&nsortbase); return OPERATOR_CANCELLED; } while (nus2) { merge_2_nurb(op, editnurb, nus1->nu, nus2->nu); nus2= nus2->next; } BLI_freelistN(&nsortbase); set_actNurb(obedit, NULL); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } static int make_segment_exec(bContext *C, wmOperator *op) { /* joins 2 curves */ Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *nubase= object_editcurve_get(obedit); Nurb *nu, *nu1=NULL, *nu2=NULL; BPoint *bp; float *fp, offset; int a, ok= 0; /* first decide if this is a surface merge! */ if (obedit->type==OB_SURF) nu= nubase->first; else nu= NULL; while (nu) { if ( isNurbsel(nu) ) { if (nu->pntsu>1 && nu->pntsv>1) break; if (isNurbsel_count(cu, nu)>1) break; if (isNurbsel_count(cu, nu)==1) { /* 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)); else break; } } } nu= nu->next; } if (nu) return merge_nurb(C, op); /* 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 (nu1==NULL) { if ( BEZSELECTED_HIDDENHANDLES(cu, nu->bezt) ) nu1= nu; else { if ( BEZSELECTED_HIDDENHANDLES(cu, &(nu->bezt[nu->pntsu-1])) ) { nu1= nu; BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); } } } else if (nu2==NULL) { if ( BEZSELECTED_HIDDENHANDLES(cu, nu->bezt) ) { nu2= nu; BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); } else { if ( BEZSELECTED_HIDDENHANDLES(cu, &(nu->bezt[nu->pntsu-1])) ) { nu2= nu; } } } else break; } else if (nu->pntsv==1) { bp= nu->bp; if (nu1==NULL) { if ( bp->f1 & SELECT) nu1= nu; else { bp= bp+(nu->pntsu-1); if ( bp->f1 & SELECT ) { nu1= nu; BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); } } } else if (nu2==NULL) { if ( bp->f1 & SELECT ) { nu2= nu; BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); } else { bp= bp+(nu->pntsu-1); if ( bp->f1 & SELECT ) { nu2= nu; } } } else 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); 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; nu1->pntsu+= nu2->pntsu; BLI_remlink(nubase, nu2); /* now join the knots */ if (nu1->type == CU_NURBS) { if (nu1->knotsu==NULL) { BKE_nurb_knot_calc_u(nu1); } else { fp= MEM_mallocN(sizeof(float)*KNOTSU(nu1), "addsegment3"); memcpy(fp, nu1->knotsu, sizeof(float)*a); MEM_freeN(nu1->knotsu); nu1->knotsu= fp; offset= nu1->knotsu[a-1] + 1.0f; fp= nu1->knotsu+a; for (a=0; apntsu; a++, fp++) { if (nu2->knotsu) *fp= offset+nu2->knotsu[a+1]; else *fp = offset; } } } BKE_nurb_free(nu2); nu2= NULL; } set_actNurb(obedit, nu1); /* for selected */ ok= 1; } } else if (nu1 && !nu2) { if (!(nu1->flagu & CU_NURB_CYCLIC) && nu1->pntsu>1) { if (nu1->type == CU_BEZIER && BEZSELECTED_HIDDENHANDLES(cu, nu1->bezt) && BEZSELECTED_HIDDENHANDLES(cu, nu1->bezt+(nu1->pntsu-1))) { nu1->flagu|= CU_NURB_CYCLIC; BKE_nurb_handles_calc(nu1); ok= 1; } else if (nu1->type == CU_NURBS && nu1->bp->f1&SELECT && (nu1->bp+(nu1->pntsu-1))->f1&SELECT) { nu1->flagu|= CU_NURB_CYCLIC; BKE_nurb_knot_calc_u(nu1); ok= 1; } } } if (!ok) { BKE_report(op->reports, RPT_ERROR, "Can't make segment"); return OPERATOR_CANCELLED; } if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } void CURVE_OT_make_segment(wmOperatorType *ot) { /* identifiers */ ot->name = "Make Segment"; ot->idname = "CURVE_OT_make_segment"; /* api callbacks */ ot->exec = make_segment_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /***************** pick select from 3d view **********************/ int mouse_nurb(bContext *C, const int mval[2], int extend) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); ViewContext vc; Nurb *nu; BezTriple *bezt=NULL; BPoint *bp=NULL; int location[2]; short hand; view3d_operator_needs_opengl(C); view3d_set_viewcontext(C, &vc); location[0]= mval[0]; location[1]= mval[1]; hand= findnearestNurbvert(&vc, 1, location, &nu, &bezt, &bp); if (bezt || bp) { if (extend==0) { setflagsNurb(editnurb, 0); if (bezt) { if (hand==1) { select_beztriple(bezt, SELECT, 1, HIDDEN); cu->lastsel= bezt; } else { if (hand==0) bezt->f1|= SELECT; else bezt->f3|= SELECT; cu->lastsel= NULL; } } else { cu->lastsel= bp; select_bpoint(bp, SELECT, 1, HIDDEN); } } else { if (bezt) { if (hand==1) { if (bezt->f2 & SELECT) { select_beztriple(bezt, DESELECT, 1, HIDDEN); if (bezt == cu->lastsel) cu->lastsel = NULL; } else { select_beztriple(bezt, SELECT, 1, HIDDEN); cu->lastsel= bezt; } } else if (hand==0) { bezt->f1 ^= SELECT; } else { bezt->f3 ^= SELECT; } } else { if (bp->f1 & SELECT) { select_bpoint(bp, DESELECT, 1, HIDDEN); if (cu->lastsel == bp) cu->lastsel = NULL; } else { select_bpoint(bp, SELECT, 1, HIDDEN); cu->lastsel= bp; } } } if (nu!=get_actNurb(obedit)) set_actNurb(obedit, nu); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return 1; } return 0; } /******************** spin operator ***********************/ /* 'cent' is in object space and 'dvec' in worldspace. */ static int spin_nurb(float viewmat[][4], Object *obedit, float *axis, float *cent) { Curve *cu= (Curve*)obedit->data; ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; float si, phi, n[3], q[4], 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]; short a, ok, changed= 0; 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); normalize_v3_v3(n, axis); phi= M_PI/8.0; q[0]= cos(phi); si= sin(phi); q[1]= n[0]*si; q[2]= n[1]*si; q[3]= n[2]*si; quat_to_mat3(cmat, q); 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= 1; for (a=0;a<7;a++) { ok= extrudeflagNurb(cu->editnurb, 1); if (ok==0) return changed; changed= 1; rotateflagNurb(editnurb, SELECT, cent, rotmat); if ( (a & SELECT)==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) { for (nu= editnurb->first; nu; nu= nu->next) { if (isNurbsel(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) { Object *obedit= CTX_data_edit_object(C); RegionView3D *rv3d= ED_view3d_context_rv3d(C); float cent[3], axis[3], viewmat[4][4]; RNA_float_get_array(op->ptr, "center", cent); RNA_float_get_array(op->ptr, "axis", axis); invert_m4_m4(obedit->imat, obedit->obmat); mul_m4_v3(obedit->imat, cent); if (rv3d) copy_m4_m4(viewmat, rv3d->viewmat); else unit_m4(viewmat); if (!spin_nurb(viewmat, obedit, axis, cent)) { BKE_report(op->reports, RPT_ERROR, "Can't spin"); return OPERATOR_CANCELLED; } if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } static int spin_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(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", give_cursor(scene, v3d)); 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"; /* 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, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX); RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -FLT_MAX, FLT_MAX); } /***************** add vertex operator **********************/ static int addvert_Nurb(bContext *C, short mode, float location[3]) { Object *obedit= CTX_data_edit_object(C); Curve *cu= (Curve*)obedit->data; EditNurb *editnurb= cu->editnurb; Nurb *nu, *newnu= NULL; BezTriple *bezt, *newbezt = NULL; BPoint *bp, *newbp = NULL; float imat[4][4], temp[3]; int ok= 0; BezTriple *bezt_recalc[3] = {NULL}; invert_m4_m4(imat, obedit->obmat); findselectedNurbvert(&editnurb->nurbs, &nu, &bezt, &bp); if ((nu == NULL) || (nu->type==CU_BEZIER && bezt==NULL) || (nu->type!=CU_BEZIER && bp==NULL)) { if (mode!='e') { if (cu->actnu >= 0) nu= BLI_findlink(&editnurb->nurbs, cu->actnu); if (!nu || nu->type==CU_BEZIER) { newbezt= (BezTriple*)MEM_callocN(sizeof(BezTriple), "addvert_Nurb"); newbezt->radius= 1; newbezt->alfa= 0; BEZ_SEL(newbezt); newbezt->h2= newbezt->h1= HD_AUTO; newnu= (Nurb*)MEM_callocN(sizeof(Nurb), "addvert_Nurb newnu"); if (!nu) { /* no selected segment -- create new one which is BEZIER type * type couldn't be determined from Curve bt could be changed * in the future, so shouldn't make much headache */ newnu->type= CU_BEZIER; newnu->resolu= cu->resolu; newnu->flag |= CU_SMOOTH; } else memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(&editnurb->nurbs, newnu); set_actNurb(obedit, newnu); newnu->bezt= newbezt; newnu->pntsu= 1; temp[0] = 1; temp[1] = 0; temp[2] = 0; copy_v3_v3(newbezt->vec[1], location); sub_v3_v3v3(newbezt->vec[0], newbezt->vec[1], temp); add_v3_v3v3(newbezt->vec[2], newbezt->vec[1], temp); mul_m4_v3(imat, newbezt->vec[0]); mul_m4_v3(imat, newbezt->vec[1]); mul_m4_v3(imat, newbezt->vec[2]); ok= 1; nu= newnu; } else if (nu->pntsv == 1) { newbp= (BPoint*)MEM_callocN(sizeof(BPoint), "addvert_Nurb5"); newbp->radius= 1; newbp->alfa= 0; newbp->f1|= SELECT; cu->lastsel= newbp; newnu= (Nurb*)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(&editnurb->nurbs, newnu); set_actNurb(obedit, newnu); newnu->bp= newbp; newnu->orderu= 2; newnu->pntsu= 1; mul_v3_m4v3(newbp->vec, imat, location); newbp->vec[3]= 1.0; newnu->knotsu= newnu->knotsv= NULL; BKE_nurb_knot_calc_u(newnu); ok= 1; nu= newnu; } } if (!ok) return OPERATOR_CANCELLED; } if (!ok && nu->type == CU_BEZIER) { /* which bezpoint? */ if (bezt== (nu->bezt+nu->pntsu-1)) { /* last */ BEZ_DESEL(bezt); newbezt = (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); ED_curve_beztcpy(editnurb, newbezt, nu->bezt, nu->pntsu); *(newbezt+nu->pntsu)= *bezt; copy_v3_v3(temp, bezt->vec[1]); MEM_freeN(nu->bezt); nu->bezt= newbezt; newbezt+= nu->pntsu; BEZ_SEL(newbezt); cu->lastsel= newbezt; newbezt->h1 = newbezt->h2; bezt= nu->bezt+nu->pntsu-1; ok= 1; if (nu->pntsu > 1) { bezt_recalc[1] = newbezt; bezt_recalc[0] = newbezt - 1; } } else if (bezt== nu->bezt) { /* first */ BEZ_DESEL(bezt); newbezt = (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); ED_curve_beztcpy(editnurb, newbezt+1, bezt, nu->pntsu); *newbezt= *bezt; BEZ_SEL(newbezt); cu->lastsel= newbezt; newbezt->h2= newbezt->h1; copy_v3_v3(temp, bezt->vec[1]); MEM_freeN(nu->bezt); nu->bezt= newbezt; bezt= newbezt+1; ok= 1; if (nu->pntsu > 1) { bezt_recalc[1] = newbezt; bezt_recalc[2] = newbezt + 1; } } else if (mode!='e') { BEZ_DESEL(bezt); newbezt= (BezTriple*)MEM_callocN(sizeof(BezTriple), "addvert_Nurb"); *newbezt= *bezt; BEZ_SEL(newbezt); newbezt->h2= newbezt->h1; copy_v3_v3(temp, bezt->vec[1]); newnu= (Nurb*)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(&editnurb->nurbs, newnu); set_actNurb(obedit, newnu); newnu->bezt= newbezt; newnu->pntsu= 1; cu->lastsel= newbezt; bezt= newbezt; ok= 1; } else bezt= NULL; if (bezt) { if (!newnu) nu->pntsu++; if (mode=='e') { copy_v3_v3(newbezt->vec[0], bezt->vec[0]); copy_v3_v3(newbezt->vec[1], bezt->vec[1]); copy_v3_v3(newbezt->vec[2], bezt->vec[2]); } else { mul_v3_m4v3(newbezt->vec[1], imat, location); sub_v3_v3v3(temp, newbezt->vec[1], temp); if (bezt_recalc[1]) { const char h1 = bezt_recalc[1]->h1, h2 = bezt_recalc[1]->h2; bezt_recalc[1]->h1 = bezt_recalc[1]->h2 = HD_AUTO; BKE_nurb_handle_calc(bezt_recalc[1], bezt_recalc[0], bezt_recalc[2], 0); bezt_recalc[1]->h1 = h1; bezt_recalc[1]->h2 = h2; } else { add_v3_v3v3(newbezt->vec[0], bezt->vec[0], temp); add_v3_v3v3(newbezt->vec[2], bezt->vec[2], temp); } if (newnu) BKE_nurb_handles_calc(newnu); else BKE_nurb_handles_calc(nu); } } } else if (!ok && nu->pntsv==1) { /* which b-point? */ if (bp== (nu->bp+nu->pntsu-1)) { /* last */ bp->f1= 0; newbp = (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb4"); ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); *(newbp+nu->pntsu)= *bp; MEM_freeN(nu->bp); nu->bp= newbp; newbp+= nu->pntsu; newbp->f1|= SELECT; cu->lastsel= newbp; bp= newbp - 1; ok= 1; } else if (bp== nu->bp) { /* first */ bp->f1= 0; newbp = (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb3"); ED_curve_bpcpy(editnurb, newbp+1, bp, nu->pntsu); *newbp= *bp; newbp->f1|= SELECT; cu->lastsel= newbp; MEM_freeN(nu->bp); nu->bp= newbp; bp= newbp + 1; ok= 1; } else if (mode!='e') { bp->f1= 0; newbp= (BPoint*)MEM_callocN(sizeof(BPoint), "addvert_Nurb5"); *newbp= *bp; newbp->f1|= SELECT; cu->lastsel= newbp; newnu= (Nurb*)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); memcpy(newnu, nu, sizeof(Nurb)); BLI_addtail(&editnurb->nurbs, newnu); set_actNurb(obedit, newnu); newnu->bp= newbp; newnu->orderu= 2; newnu->pntsu= 1; newnu->knotsu= newnu->knotsv= NULL; bp= newbp; ok= 1; } else bp= NULL; if (bp) { if (mode=='e') { copy_v3_v3(newbp->vec, bp->vec); } else { mul_v3_m4v3(newbp->vec, imat, location); newbp->vec[3]= 1.0; if (!newnu && nu->orderu<4 && nu->orderu<=nu->pntsu) nu->orderu++; } if (!newnu) { nu->pntsu++; BKE_nurb_knot_calc_u(nu); } else BKE_nurb_knot_calc_u(newnu); } } if (ok) { BKE_nurb_test2D(nu); if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } return OPERATOR_CANCELLED; } static int add_vertex_exec(bContext *C, wmOperator *op) { float location[3]; RNA_float_get_array(op->ptr, "location", location); return addvert_Nurb(C, 0, location); } static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event) { RegionView3D *rv3d= CTX_wm_region_view3d(C); if (rv3d && !RNA_struct_property_is_set(op->ptr, "location")) { Curve *cu; ViewContext vc; float location[3]; Nurb *nu; BezTriple *bezt; BPoint *bp; view3d_set_viewcontext(C, &vc); cu= vc.obedit->data; findselectedNurbvert(&cu->editnurb->nurbs, &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, give_cursor(vc.scene, vc.v3d)); } view3d_get_view_aligned_coordinate(&vc, location, event->mval, TRUE); 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"; /* 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, -FLT_MAX, FLT_MAX, "Location", "Location to add new vertex at", -1e4, 1e4); } /***************** extrude operator **********************/ static int extrude_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; EditNurb *editnurb= cu->editnurb; Nurb *nu; /* first test: curve? */ for (nu= editnurb->nurbs.first; nu; nu= nu->next) if (nu->pntsv==1 && isNurbsel_count(cu, nu)==1) break; if (obedit->type==OB_CURVE || nu) { addvert_Nurb(C, 'e', NULL); } else { if (extrudeflagNurb(editnurb, 1)) { /* '1'= flag */ if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); } } return OPERATOR_FINISHED; } void CURVE_OT_extrude(wmOperatorType *ot) { /* identifiers */ ot->name = "Extrude"; ot->description = "Extrude selected control point(s) and move"; ot->idname = "CURVE_OT_extrude"; /* api callbacks */ ot->exec = extrude_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* to give to transform */ RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /***************** make cyclic operator **********************/ static int toggle_cyclic_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BezTriple *bezt; BPoint *bp; int a, direction= RNA_enum_get(op->ptr, "direction"); for (nu= editnurb->first; nu; nu= nu->next) { 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; break; } bp++; } } else if (nu->type == CU_BEZIER) { a= nu->pntsu; bezt= nu->bezt; while (a--) { if ( BEZSELECTED_HIDDENHANDLES(cu, bezt) ) { nu->flagu ^= CU_NURB_CYCLIC; 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; BKE_nurb_knot_calc_u(nu); /* 1==u type is ignored for cyclic curves */ 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; BKE_nurb_knot_calc_u(nu); /* 1==u type is ignored for cyclic curves */ } if (direction==1 && nu->pntsv>1) { nu->flagv ^= CU_NURB_CYCLIC; BKE_nurb_knot_calc_v(nu); /* 2==v type is ignored for cyclic curves */ } break; } bp++; } } } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } static int toggle_cyclic_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); uiPopupMenu *pup; uiLayout *layout; Nurb *nu; if (obedit->type == OB_SURF) { for (nu= editnurb->first; nu; nu= nu->next) { if (nu->pntsu>1 || nu->pntsv>1) { if (nu->type==CU_NURBS) { pup= uiPupMenuBegin(C, "Direction", ICON_NONE); layout= uiPupMenuLayout(pup); uiItemsEnumO(layout, op->type->idname, "direction"); uiPupMenuEnd(C, pup); return OPERATOR_CANCELLED; } } } } return toggle_cyclic_exec(C, op); } void CURVE_OT_cyclic_toggle(wmOperatorType *ot) { static 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"); } /***************** select linked operator ******************/ static int select_linked_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); Curve *cu= (Curve*)obedit->data; EditNurb *editnurb= cu->editnurb; ListBase *nurbs= &editnurb->nurbs; Nurb *nu; BezTriple *bezt; BPoint *bp; int a; for (nu= nurbs->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if ( (bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT) ) { a= nu->pntsu; bezt= nu->bezt; while (a--) { select_beztriple(bezt, SELECT, 1, VISIBLE); bezt++; } break; } bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if ( bp->f1 & 1 ) { a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { select_bpoint(bp, SELECT, 1, VISIBLE); bp++; } break; } bp++; } } } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } static int select_linked_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { return select_linked_exec(C, op); } void CURVE_OT_select_linked(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Linked All"; ot->idname = "CURVE_OT_select_linked"; /* api callbacks */ ot->exec = select_linked_exec; ot->invoke = select_linked_invoke; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ } /***************** select linked pick operator ******************/ static int select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event) { Object *obedit= CTX_data_edit_object(C); ViewContext vc; Nurb *nu; BezTriple *bezt; BPoint *bp; int a, deselect; deselect= RNA_boolean_get(op->ptr, "deselect"); view3d_operator_needs_opengl(C); view3d_set_viewcontext(C, &vc); findnearestNurbvert(&vc, 1, event->mval, &nu, &bezt, &bp); if (bezt) { a= nu->pntsu; bezt= nu->bezt; while (a--) { if (deselect) select_beztriple(bezt, DESELECT, 1, VISIBLE); else select_beztriple(bezt, SELECT, 1, VISIBLE); bezt++; } } else if (bp) { a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { if (deselect) select_bpoint(bp, DESELECT, 1, VISIBLE); else select_bpoint(bp, SELECT, 1, VISIBLE); bp++; } } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Linked"; ot->idname = "CURVE_OT_select_linked_pick"; /* api callbacks */ ot->invoke = select_linked_pick_invoke; ot->poll = ED_operator_editsurfcurve_region_view3d; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked control points rather than selecting them"); } /***************** select row operator **********************/ static int select_row_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); static BPoint *last= NULL; static int direction=0; Nurb *nu; BPoint *bp; int u = 0, v = 0, a, b, ok=0; if (editnurb->first == NULL) return OPERATOR_CANCELLED; if (cu->lastsel==NULL) return OPERATOR_CANCELLED; /* find the correct nurb and toggle with u of v */ for (nu= editnurb->first; nu; nu= nu->next) { bp= nu->bp; for (v=0; vpntsv; v++) { for (u=0; upntsu; u++, bp++) { if (bp==cu->lastsel) { if (bp->f1 & SELECT) { ok= 1; break; } } } if (ok) break; } if (ok) { if (last==cu->lastsel) { direction= 1-direction; setflagsNurb(editnurb, 0); } last= cu->lastsel; bp= nu->bp; for (a=0; apntsv; a++) { for (b=0; bpntsu; b++, bp++) { if (direction) { if (a==v) select_bpoint(bp, SELECT, 1, VISIBLE); } else { if (b==u) select_bpoint(bp, SELECT, 1, VISIBLE); } } } break; } } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_row(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Control Point Row"; ot->idname = "CURVE_OT_select_row"; /* api callbacks */ ot->exec = select_row_exec; ot->poll = ED_operator_editsurf; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /***************** select next operator **********************/ static int select_next_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); select_adjacent_cp(editnurb, 1, 0, SELECT); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_next(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Next"; ot->idname = "CURVE_OT_select_next"; /* api callbacks */ ot->exec = select_next_exec; ot->poll = ED_operator_editcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /***************** select previous operator **********************/ static int select_previous_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); select_adjacent_cp(editnurb, -1, 0, SELECT); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_previous(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Previous"; ot->idname = "CURVE_OT_select_previous"; /* api callbacks */ ot->exec = select_previous_exec; ot->poll = ED_operator_editcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /***************** select more operator **********************/ static int select_more_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BPoint *bp, *tempbp; int a; short sel= 0; short *selbpoints; /* note that NURBS surface is a special case because we mimic */ /* the behavior of "select more" of mesh tools. */ /* The algorithm is designed to work in planar cases so it */ /* may not be optimal always (example: end of NURBS sphere) */ if (obedit->type==OB_SURF) { for (nu= editnurb->first; nu; nu= nu->next) { a= nu->pntsu*nu->pntsv; bp= nu->bp; selbpoints= MEM_callocN(sizeof(short)*a-nu->pntsu, "selectlist"); while (a > 0) { if ((selbpoints[a]!=1) && (bp->hide==0) && (bp->f1 & SELECT)) { /* upper control point */ if (a%nu->pntsu != 0) { tempbp= bp-1; if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE); } /* left control point. select only if it is not selected already */ if (a-nu->pntsu > 0) { sel= 0; tempbp= bp+nu->pntsu; if (!(tempbp->f1 & SELECT)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE); /* make sure selected bpoint is discarded */ if (sel == 1) selbpoints[a-nu->pntsu]= 1; } /* right control point */ if (a+nu->pntsu < nu->pntsu*nu->pntsv) { tempbp= bp-nu->pntsu; if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE); } /* lower control point. skip next bp in case selection was made */ if (a%nu->pntsu != 1) { sel= 0; tempbp= bp+1; if (!(tempbp->f1 & 1)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE); if (sel) { bp++; a--; } } } bp++; a--; } MEM_freeN(selbpoints); } } else { select_adjacent_cp(editnurb, 1, 0, SELECT); select_adjacent_cp(editnurb, -1, 0, SELECT); } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_more(wmOperatorType *ot) { /* identifiers */ ot->name = "Select More"; ot->idname = "CURVE_OT_select_more"; /* api callbacks */ ot->exec = select_more_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /******************** select less operator *****************/ /* basic method: deselect if control point doesn't have all neighbors selected */ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BPoint *bp; BezTriple *bezt; int a; short sel= 0, lastsel= 0; short *selbpoints; if (obedit->type==OB_SURF) { for (nu= editnurb->first; nu; nu= nu->next) { a= nu->pntsu*nu->pntsv; bp= nu->bp; selbpoints= MEM_callocN(sizeof(short)*a, "selectlist"); while (a--) { if ((bp->hide==0) && (bp->f1 & SELECT)) { sel= 0; /* check if neighbors have been selected */ /* edges of surface are an exception */ if ((a+1)%nu->pntsu==0) sel++; else { bp--; if ((selbpoints[a+1]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++; bp++; } if ((a+1)%nu->pntsu==1) sel++; else { bp++; if ((bp->hide==0) && (bp->f1 & SELECT)) sel++; bp--; } if (a+1 > nu->pntsu*nu->pntsv-nu->pntsu) sel++; else { bp-=nu->pntsu; if ((selbpoints[a+nu->pntsu]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++; bp+=nu->pntsu; } if (a < nu->pntsu) sel++; else { bp+=nu->pntsu; if ((bp->hide==0) && (bp->f1 & SELECT)) sel++; bp-=nu->pntsu; } if (sel!=4) { select_bpoint(bp, DESELECT, 1, VISIBLE); selbpoints[a]= 1; } } else lastsel= 0; bp++; } MEM_freeN(selbpoints); } } else { for (nu= editnurb->first; nu; nu= nu->next) { lastsel=0; /* check what type of curve/nurb it is */ if (nu->type == CU_BEZIER) { a= nu->pntsu; bezt= nu->bezt; while (a--) { if ((bezt->hide==0) && (bezt->f2 & SELECT)) { if (lastsel==1) sel= 1; else sel= 0; /* check if neighbors have been selected */ /* first and last are exceptions */ if (a==nu->pntsu-1) sel++; else { bezt--; if ((bezt->hide==0) && (bezt->f2 & SELECT)) sel++; bezt++; } if (a==0) sel++; else { bezt++; if ((bezt->hide==0) && (bezt->f2 & SELECT)) sel++; bezt--; } if (sel!=2) { select_beztriple(bezt, DESELECT, 1, VISIBLE); lastsel= 1; } else lastsel= 0; } else lastsel= 0; bezt++; } } else { a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a--) { if ((lastsel==0) && (bp->hide==0) && (bp->f1 & SELECT)) { if (lastsel!=0) sel= 1; else sel= 0; /* first and last are exceptions */ if (a==nu->pntsu*nu->pntsv-1) sel++; else { bp--; if ((bp->hide==0) && (bp->f1 & SELECT)) sel++; bp++; } if (a==0) sel++; else { bp++; if ((bp->hide==0) && (bp->f1 & SELECT)) sel++; bp--; } if (sel!=2) { select_bpoint(bp, DESELECT, 1, VISIBLE); lastsel= 1; } else lastsel= 0; } else lastsel= 0; bp++; } } } } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_less(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Less"; ot->idname = "CURVE_OT_select_less"; /* api callbacks */ ot->exec = select_less_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /********************** select random *********************/ static void selectrandom_curve(ListBase *editnurb, float randfac) { Nurb *nu; BezTriple *bezt; BPoint *bp; int a; BLI_srand( BLI_rand() ); /* random seed */ for (nu= editnurb->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if (BLI_frand() < randfac) select_beztriple(bezt, SELECT, 1, VISIBLE); bezt++; } } else { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if (BLI_frand() < randfac) select_bpoint(bp, SELECT, 1, VISIBLE); bp++; } } } } static int select_random_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); if (!RNA_boolean_get(op->ptr, "extend")) CU_deselect_all(obedit); selectrandom_curve(editnurb, RNA_float_get(op->ptr, "percent")/100.0f); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_random(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Random"; ot->idname = "CURVE_OT_select_random"; /* api callbacks */ ot->exec = select_random_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly", 0.f, 100.0f); RNA_def_boolean(ot->srna, "extend", FALSE, "Extend Selection", "Extend selection instead of deselecting everything first"); } /********************* every nth number of point *******************/ static int point_on_nurb(Nurb *nu, void *point) { if (nu->bezt) { BezTriple *bezt= (BezTriple*)point; return bezt >= nu->bezt && bezt < nu->bezt + nu->pntsu; } else { BPoint *bp= (BPoint*)point; return bp >= nu->bp && bp < nu->bp + nu->pntsu * nu->pntsv; } } static Nurb *get_lastsel_nurb(Curve *cu) { ListBase *nubase= BKE_curve_editNurbs_get(cu); Nurb *nu= nubase->first; if (!cu->lastsel) return NULL; while (nu) { if (point_on_nurb(nu, cu->lastsel)) return nu; nu= nu->next; } return NULL; } static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth) { int a, start; start= bezt - nu->bezt; a= nu->pntsu; bezt= nu->bezt + a - 1; while (a--) { if (abs(start - a) % nth) { select_beztriple(bezt, DESELECT, 1, HIDDEN); } bezt--; } } static void select_nth_bp(Nurb *nu, BPoint *bp, int nth) { int a, startrow, startpnt; int dist, row, pnt; startrow= (bp - nu->bp) / nu->pntsu; startpnt= (bp - nu->bp) % nu->pntsu; a= nu->pntsu * nu->pntsv; bp= nu->bp + a - 1; row = nu->pntsv - 1; pnt = nu->pntsu - 1; while (a--) { dist= abs(pnt - startpnt) + abs(row - startrow); if (dist % nth) { select_bpoint(bp, DESELECT, 1, HIDDEN); } pnt--; if (pnt < 0) { pnt= nu->pntsu - 1; row--; } bp--; } } int CU_select_nth(Object *obedit, int nth) { Curve *cu= (Curve*)obedit->data; Nurb *nu; nu= get_lastsel_nurb(cu); if (!nu) return 0; if (nu->bezt) { select_nth_bezt(nu, cu->lastsel, nth); } else { select_nth_bp(nu, cu->lastsel, nth); } return 1; } static int select_nth_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); int nth= RNA_int_get(op->ptr, "nth"); if (!CU_select_nth(obedit, nth)) { if (obedit->type == OB_SURF) { BKE_report(op->reports, RPT_ERROR, "Surface hasn't got active point"); } else { BKE_report(op->reports, RPT_ERROR, "Curve hasn't got active point"); } return OPERATOR_CANCELLED; } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_select_nth(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Nth"; ot->description = ""; ot->idname = "CURVE_OT_select_nth"; /* api callbacks */ ot->exec = select_nth_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; RNA_def_int(ot->srna, "nth", 2, 2, 100, "Nth Selection", "", 1, INT_MAX); } /********************** add duplicate operator *********************/ static int duplicate_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); adduplicateflagNurb(obedit, 1); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void CURVE_OT_duplicate(wmOperatorType *ot) { /* identifiers */ ot->name = "Duplicate Curve"; ot->description = "Duplicate selected control points and segments between them"; ot->idname = "CURVE_OT_duplicate"; /* api callbacks */ ot->exec = duplicate_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /********************** delete operator *********************/ static int delete_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; EditNurb *editnurb= cu->editnurb; ListBase *nubase= &editnurb->nurbs; Nurb *nu, *nu1; BezTriple *bezt, *bezt1, *bezt2; BPoint *bp, *bp1, *bp2; int a, cut= 0, type= RNA_enum_get(op->ptr, "type"); int nuindex= 0; if (obedit->type==OB_SURF) { if (type==0) { deleteflagNurb(C, op, 1); } else { keyIndex_delNurbList(editnurb, nubase); BKE_nurbList_free(nubase); if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } if (type==0) { /* first loop, can we remove entire pieces? */ Nurb *next; nu= nubase->first; while (nu) { next= nu->next; if (nu->type == CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; if (a) { while (a) { if ( BEZSELECTED_HIDDENHANDLES(cu, bezt) ); else break; a--; bezt++; } if (a==0) { if (cu->actnu == nuindex) cu->actnu= -1; 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); else break; a--; bp++; } if (a==0) { if (cu->actnu == nuindex) cu->actnu= -1; 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 nu= next; nuindex++; } /* 2nd loop, delete small pieces: just for curves */ nu= nubase->first; while (nu) { next= nu->next; type= 0; if (nu->type == CU_BEZIER) { int delta= 0; bezt= nu->bezt; for (a=0;apntsu;a++) { if ( BEZSELECTED_HIDDENHANDLES(cu, bezt) ) { memmove(bezt, bezt+1, (nu->pntsu-a-1)*sizeof(BezTriple)); keyIndex_delBezt(editnurb, bezt + delta); keyIndex_updateBezt(editnurb, bezt + 1, bezt, nu->pntsu-a-1); nu->pntsu--; a--; type= 1; delta++; } 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) { int delta= 0; bp= nu->bp; for (a=0;apntsu;a++) { if ( bp->f1 & SELECT ) { memmove(bp, bp+1, (nu->pntsu-a-1)*sizeof(BPoint)); keyIndex_delBP(editnurb, bp + delta); keyIndex_updateBP(editnurb, bp+1, bp, nu->pntsu-a-1); nu->pntsu--; a--; type= 1; delta++; } 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); } nu= next; } } else if (type==1) { /* erase segment */ /* find the 2 selected points */ bezt1= bezt2= NULL; bp1= bp2= NULL; nu1= NULL; nuindex= 0; for (nu= nubase->first; nu; nu= nu->next) { if (nu->type == CU_BEZIER) { bezt= nu->bezt; for (a=0; apntsu-1; a++) { if ( BEZSELECTED_HIDDENHANDLES(cu, bezt) ) { bezt1= bezt; bezt2= bezt+1; if ((bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT)) { /* pass */ } else { /* maybe do not make cyclic */ if (a==0 && (nu->flagu & CU_NURB_CYCLIC) ) { bezt2= bezt+(nu->pntsu-1); if ( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) { nu->flagu &= ~CU_NURB_CYCLIC; BKE_nurb_handles_calc(nu); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); } } return OPERATOR_FINISHED; } cut= a; nu1= nu; break; } bezt++; } } else if (nu->pntsv==1) { bp= nu->bp; for (a=0; apntsu-1; a++) { if ( bp->f1 & SELECT ) { bp1= bp; bp2= bp+1; if (bp2->f1 & 1) { /* pass */ } else { /* maybe do not make cyclic */ if (a==0 && (nu->flagu & CU_NURB_CYCLIC) ) { bp2= bp+(nu->pntsu-1); if ( bp2->f1 & SELECT ) { nu->flagu &= ~CU_NURB_CYCLIC; WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); } } return OPERATOR_FINISHED; } cut= a; nu1= nu; break; } bp++; } } if (nu1) break; nuindex++; } if (nu1) { if (bezt1) { if (nu1->pntsu==2) { /* remove completely */ if (cu->actnu == nuindex) cu->actnu= -1; BLI_remlink(nubase, nu); BKE_nurb_free(nu); nu = NULL; } else if (nu1->flagu & CU_NURB_CYCLIC) { /* cyclic */ bezt = (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb1"); ED_curve_beztcpy(editnurb, bezt, nu1->bezt, cut+1); a= nu1->pntsu-cut-1; ED_curve_beztcpy(editnurb, nu1->bezt, bezt2, a); ED_curve_beztcpy(editnurb, nu1->bezt+a, bezt, cut+1); nu1->flagu &= ~CU_NURB_CYCLIC; MEM_freeN(bezt); BKE_nurb_handles_calc(nu); } else { /* add new curve */ /* seems to be an error here... but where? (a can become zero) */ nu = (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb2"); memcpy(nu, nu1, sizeof(Nurb)); BLI_addtail(nubase, nu); nu->bezt = (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb3"); ED_curve_beztcpy(editnurb, nu->bezt, nu1->bezt, cut+1); a= nu1->pntsu-cut-1; bezt = (BezTriple*)MEM_mallocN(a * sizeof(BezTriple), "delNurb4"); ED_curve_beztcpy(editnurb, bezt, nu1->bezt+cut+1, a); MEM_freeN(nu1->bezt); nu1->bezt= bezt; nu1->pntsu= a; nu->pntsu= cut+1; BKE_nurb_handles_calc(nu); BKE_nurb_handles_calc(nu1); } } else if (bp1) { if (nu1->pntsu==2) { /* remove completely */ if (cu->actnu == nuindex) cu->actnu= -1; BLI_remlink(nubase, nu); BKE_nurb_free(nu); nu= NULL; } else if (nu1->flagu & CU_NURB_CYCLIC) { /* cyclic */ bp = (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb5"); ED_curve_bpcpy(editnurb, bp, nu1->bp, cut+1); a= nu1->pntsu-cut-1; ED_curve_bpcpy(editnurb, nu1->bp, bp2, a); ED_curve_bpcpy(editnurb, nu1->bp+a, bp, cut+1); nu1->flagu &= ~CU_NURB_CYCLIC; MEM_freeN(bp); } else { /* add new curve */ nu = (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb6"); memcpy(nu, nu1, sizeof(Nurb)); BLI_addtail(nubase, nu); nu->bp = (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb7"); ED_curve_bpcpy(editnurb, nu->bp, nu1->bp, cut+1); a= nu1->pntsu-cut-1; bp = (BPoint*)MEM_mallocN(a * sizeof(BPoint), "delNurb8"); ED_curve_bpcpy(editnurb, bp, nu1->bp+cut+1, a); MEM_freeN(nu1->bp); nu1->bp= bp; nu1->pntsu= a; nu1->knotsu= NULL; nu->pntsu= cut+1; BKE_nurb_order_clamp_u(nu); BKE_nurb_knot_calc_u(nu); BKE_nurb_order_clamp_u(nu1); BKE_nurb_knot_calc_u(nu1); } } } } else if (type==2) { cu->actnu= -1; keyIndex_delNurbList(editnurb, nubase); BKE_nurbList_free(nubase); } if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, obedit); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } static int delete_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { Object *obedit= CTX_data_edit_object(C); uiPopupMenu *pup; uiLayout *layout; if (obedit->type==OB_SURF) { pup= uiPupMenuBegin(C, "Delete", ICON_NONE); layout= uiPupMenuLayout(pup); uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 0); uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 2); uiPupMenuEnd(C, pup); } else { pup= uiPupMenuBegin(C, "Delete", ICON_NONE); layout= uiPupMenuLayout(pup); uiItemsEnumO(layout, op->type->idname, "type"); uiPupMenuEnd(C, pup); } return OPERATOR_CANCELLED; } void CURVE_OT_delete(wmOperatorType *ot) { static EnumPropertyItem type_items[] = { {0, "SELECTED", 0, "Select", ""}, {1, "SEGMENT", 0, "Segment", ""}, {2, "ALL", 0, "All", ""}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ ot->name = "Delete"; ot->description = "Delete selected control points or segments"; ot->idname = "CURVE_OT_delete"; /* api callbacks */ ot->exec = delete_exec; ot->invoke = delete_invoke; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Which elements to delete"); } /********************** shade smooth/flat operator *********************/ static int shade_smooth_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; int clear= (strcmp(op->idname, "CURVE_OT_shade_flat") == 0); if (obedit->type != OB_CURVE) return OPERATOR_CANCELLED; for (nu= editnurb->first; nu; nu= nu->next) { if (isNurbsel(nu)) { if (!clear) nu->flag |= CU_SMOOTH; else nu->flag &= ~CU_SMOOTH; } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } void CURVE_OT_shade_smooth(wmOperatorType *ot) { /* identifiers */ ot->name = "Shade Smooth"; ot->idname = "CURVE_OT_shade_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"; /* api callbacks */ ot->exec = shade_smooth_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /************** join operator, to be used externally? ****************/ int join_curve_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain= CTX_data_main(C); Scene *scene= CTX_data_scene(C); Object *ob= CTX_data_active_object(C); Curve *cu; Nurb *nu, *newnu; BezTriple *bezt; BPoint *bp; ListBase tempbase; float imat[4][4], cmat[4][4]; int a; tempbase.first= tempbase.last= NULL; /* trasnform all selected curves inverse in obact */ invert_m4_m4(imat, ob->obmat); CTX_DATA_BEGIN (C, Base*, base, selected_editable_bases) { if (base->object->type==ob->type) { if (base->object != ob) { cu= base->object->data; if (cu->nurb.first) { /* watch it: switch order here really goes wrong */ mult_m4_m4m4(cmat, imat, base->object->obmat); nu= cu->nurb.first; while (nu) { newnu= BKE_nurb_duplicate(nu); if (ob->totcol) { /* TODO, merge material lists */ CLAMP(newnu->mat_nr, 0, ob->totcol-1); } else newnu->mat_nr= 0; BLI_addtail(&tempbase, newnu); if ( (bezt= newnu->bezt) ) { a= newnu->pntsu; while (a--) { 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++; } } nu= nu->next; } } ED_base_object_free_and_unlink(bmain, scene, base); } } } CTX_DATA_END; cu= ob->data; BLI_movelisttolist(&cu->nurb, &tempbase); DAG_scene_sort(bmain, scene); // because we removed object(s), call before editmode! ED_object_enter_editmode(C, EM_WAITCURSOR); ED_object_exit_editmode(C, EM_FREEDATA|EM_WAITCURSOR|EM_DO_UNDO); WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene); return OPERATOR_FINISHED; } /************ add primitive, used by object/ module ****************/ static const char *get_curve_defname(int type) { int stype= type & CU_PRIMITIVE; if ((type & CU_TYPE)==CU_BEZIER) { switch (stype) { case CU_PRIM_CURVE: return "BezierCurve"; case CU_PRIM_CIRCLE: return "BezierCircle"; case CU_PRIM_PATH: return "CurvePath"; default: return "Curve"; } } else { switch (stype) { case CU_PRIM_CURVE: return "NurbsCurve"; case CU_PRIM_CIRCLE: return "NurbsCircle"; case CU_PRIM_PATH: return "NurbsPath"; default: return "Curve"; } } } static const char *get_surf_defname(int type) { int stype= type & CU_PRIMITIVE; switch (stype) { case CU_PRIM_CURVE: return "SurfCurve"; case CU_PRIM_CIRCLE: return "SurfCircle"; case CU_PRIM_PATCH: return "SurfPatch"; case CU_PRIM_SPHERE: return "SurfSphere"; case CU_PRIM_DONUT: return "SurfTorus"; default: return "Surface"; } } Nurb *add_nurbs_primitive(bContext *C, float mat[4][4], int type, int newob) { static int xzproj= 0; /* this function calls itself... */ Object *obedit= CTX_data_edit_object(C); ListBase *editnurb= object_editcurve_get(obedit); View3D *v3d= CTX_wm_view3d(C); RegionView3D *rv3d= ED_view3d_context_rv3d(C); Nurb *nu = NULL; BezTriple *bezt; BPoint *bp; Curve *cu= (Curve*)obedit->data; float vec[3], zvec[3]= {0.0f, 0.0f, 1.0f}; float umat[4][4]= MAT4_UNITY, viewmat[4][4]= MAT4_UNITY; float fac; int a, b; const float grid= v3d ? v3d->grid : 1.0f; const int cutype= (type & CU_TYPE); // poly, bezier, nurbs, etc const int stype= (type & CU_PRIMITIVE); const int force_3d = ((Curve *)obedit->data)->flag & CU_3D; /* could be adding to an existing 3D curve */ if (rv3d) { copy_m4_m4(viewmat, rv3d->viewmat); copy_v3_v3(zvec, rv3d->viewinv[2]); } setflagsNurb(editnurb, 0); /* these types call this function to return a Nurb */ if (stype!=CU_PRIM_TUBE && stype!=CU_PRIM_DONUT) { nu = (Nurb*)MEM_callocN(sizeof(Nurb), "addNurbprim"); nu->type= cutype; nu->resolu= cu->resolu; nu->resolv= cu->resolv; } switch (stype) { case CU_PRIM_CURVE: /* curve */ nu->resolu= cu->resolu; if (cutype==CU_BEZIER) { if (!force_3d) nu->flag |= CU_2D; nu->pntsu= 2; nu->bezt = (BezTriple*)MEM_callocN(2 * sizeof(BezTriple), "addNurbprim1"); bezt= nu->bezt; bezt->h1= bezt->h2= HD_ALIGN; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->radius = 1.0; bezt->vec[1][0]+= -grid; bezt->vec[0][0]+= -1.5f*grid; bezt->vec[0][1]+= -0.5f*grid; bezt->vec[2][0]+= -0.5f*grid; bezt->vec[2][1]+= 0.5f*grid; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); bezt++; bezt->h1= bezt->h2= HD_ALIGN; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->radius = bezt->weight = 1.0; bezt->vec[0][0] = 0; bezt->vec[0][1] = 0; bezt->vec[1][0] = grid; bezt->vec[1][1] = 0; bezt->vec[2][0] = grid*2; bezt->vec[2][1] = 0; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); BKE_nurb_handles_calc(nu); } else { nu->pntsu= 4; nu->pntsv= 1; nu->orderu= 4; nu->bp= callocstructN(BPoint, 4, "addNurbprim3"); bp= nu->bp; for (a=0;a<4;a++, bp++) { bp->vec[3]= 1.0; bp->f1= SELECT; bp->radius = bp->weight = 1.0; } bp= nu->bp; bp->vec[0]+= -1.5f*grid; bp++; bp->vec[0]+= -grid; bp->vec[1]+= grid; bp++; bp->vec[0]+= grid; bp->vec[1]+= grid; bp++; bp->vec[0]+= 1.5f*grid; bp= nu->bp; for (a=0;a<4;a++, bp++) mul_m4_v3(mat, bp->vec); if (cutype==CU_NURBS) { nu->knotsu= NULL; /* nurbs_knot_calc_u allocates */ BKE_nurb_knot_calc_u(nu); } } break; case CU_PRIM_PATH: /* 5 point path */ nu->pntsu= 5; nu->pntsv= 1; nu->orderu= 5; nu->flagu= CU_NURB_ENDPOINT; /* endpoint */ nu->resolu= cu->resolu; nu->bp= callocstructN(BPoint, 5, "addNurbprim3"); bp= nu->bp; for (a=0;a<5;a++, bp++) { bp->vec[3]= 1.0; bp->f1= SELECT; bp->radius = bp->weight = 1.0; } bp= nu->bp; bp->vec[0]+= -2.0f*grid; bp++; bp->vec[0]+= -grid; bp++; bp++; bp->vec[0]+= grid; bp++; bp->vec[0]+= 2.0f*grid; bp= nu->bp; for (a=0;a<5;a++, bp++) mul_m4_v3(mat, bp->vec); if (cutype==CU_NURBS) { nu->knotsu= NULL; /* nurbs_knot_calc_u allocates */ BKE_nurb_knot_calc_u(nu); } break; case CU_PRIM_CIRCLE: /* circle */ nu->resolu= cu->resolu; if (cutype==CU_BEZIER) { if (!force_3d) nu->flag |= CU_2D; nu->pntsu= 4; nu->bezt= callocstructN(BezTriple, 4, "addNurbprim1"); nu->flagu= CU_NURB_CYCLIC; bezt= nu->bezt; bezt->h1= bezt->h2= HD_AUTO; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->vec[1][0]+= -grid; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); bezt->radius = bezt->weight = 1.0; bezt++; bezt->h1= bezt->h2= HD_AUTO; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->vec[1][1]+= grid; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); bezt->radius = bezt->weight = 1.0; bezt++; bezt->h1= bezt->h2= HD_AUTO; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->vec[1][0]+= grid; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); bezt->radius = bezt->weight = 1.0; bezt++; bezt->h1= bezt->h2= HD_AUTO; bezt->f1= bezt->f2= bezt->f3= SELECT; bezt->vec[1][1]+= -grid; for (a=0;a<3;a++) mul_m4_v3(mat, bezt->vec[a]); bezt->radius = bezt->weight = 1.0; BKE_nurb_handles_calc(nu); } else if ( cutype==CU_NURBS ) { /* nurb */ nu->pntsu= 8; nu->pntsv= 1; nu->orderu= 4; nu->bp= callocstructN(BPoint, 8, "addNurbprim6"); nu->flagu= CU_NURB_CYCLIC; bp= nu->bp; for (a=0; a<8; a++) { bp->f1= SELECT; if (xzproj==0) { bp->vec[0]+= nurbcircle[a][0]*grid; bp->vec[1]+= nurbcircle[a][1]*grid; } else { bp->vec[0]+= 0.25f*nurbcircle[a][0]*grid-0.75f*grid; bp->vec[2]+= 0.25f*nurbcircle[a][1]*grid; } if (a & 1) bp->vec[3]= 0.25*M_SQRT2; else bp->vec[3]= 1.0; mul_m4_v3(mat, bp->vec); bp->radius = bp->weight = 1.0; bp++; } BKE_nurb_knot_calc_u(nu); } break; case CU_PRIM_PATCH: /* 4x4 patch */ if ( cutype==CU_NURBS ) { /* nurb */ nu->pntsu= 4; nu->pntsv= 4; nu->orderu= 4; nu->orderv= 4; nu->flag= CU_SMOOTH; nu->bp= callocstructN(BPoint, 4*4, "addNurbprim6"); nu->flagu= 0; nu->flagv= 0; bp= nu->bp; for (a=0; a<4; a++) { for (b=0; b<4; b++) { bp->f1= SELECT; fac= (float)a -1.5f; bp->vec[0]+= fac*grid; fac= (float)b -1.5f; bp->vec[1]+= fac*grid; if (a==1 || a==2) if (b==1 || b==2) { bp->vec[2]+= grid; } mul_m4_v3(mat, bp->vec); bp->vec[3]= 1.0; bp++; } } BKE_nurb_knot_calc_u(nu); BKE_nurb_knot_calc_v(nu); } break; case CU_PRIM_TUBE: /* Cylinder */ if ( cutype==CU_NURBS ) { nu= add_nurbs_primitive(C, mat, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */ nu->resolu= cu->resolu; nu->flag= CU_SMOOTH; BLI_addtail(editnurb, nu); /* temporal for extrude and translate */ vec[0]=vec[1]= 0.0; vec[2]= -grid; if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) { /* pass */ } else { mul_mat3_m4_v3(mat, vec); } translateflagNurb(editnurb, 1, vec); extrudeflagNurb(cu->editnurb, 1); vec[0]= -2*vec[0]; vec[1]= -2*vec[1]; vec[2]= -2*vec[2]; translateflagNurb(editnurb, 1, vec); BLI_remlink(editnurb, nu); a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a-- >0) { bp->f1 |= SELECT; bp++; } } break; case CU_PRIM_SPHERE: /* sphere */ if ( cutype==CU_NURBS ) { float tmp_cent[3] = {0.f, 0.f, 0.f}; float tmp_vec[3] = {0.f, 0.f, 1.f}; nu->pntsu= 5; nu->pntsv= 1; nu->orderu= 3; nu->resolu= cu->resolu; nu->resolv= cu->resolv; nu->flag= CU_SMOOTH; nu->bp= callocstructN(BPoint, 5, "addNurbprim6"); nu->flagu= 0; bp= nu->bp; for (a=0; a<5; a++) { bp->f1= SELECT; bp->vec[0]+= nurbcircle[a][0]*grid; bp->vec[2]+= nurbcircle[a][1]*grid; if (a & 1) bp->vec[3]= 0.5*M_SQRT2; else bp->vec[3]= 1.0; mul_m4_v3(mat, bp->vec); bp++; } nu->flagu= CU_NURB_BEZIER; BKE_nurb_knot_calc_u(nu); BLI_addtail(editnurb, nu); /* temporal for spin */ if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) spin_nurb(umat, obedit, tmp_vec, tmp_cent); else if ((U.flag & USER_ADD_VIEWALIGNED)) spin_nurb(viewmat, obedit, zvec, mat[3]); else spin_nurb(umat, obedit, tmp_vec, mat[3]); BKE_nurb_knot_calc_v(nu); a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a-- >0) { bp->f1 |= SELECT; bp++; } BLI_remlink(editnurb, nu); } break; case CU_PRIM_DONUT: /* torus */ if ( cutype==CU_NURBS ) { float tmp_cent[3] = {0.f, 0.f, 0.f}; float tmp_vec[3] = {0.f, 0.f, 1.f}; xzproj= 1; nu= add_nurbs_primitive(C, mat, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */ xzproj= 0; nu->resolu= cu->resolu; nu->resolv= cu->resolv; nu->flag= CU_SMOOTH; BLI_addtail(editnurb, nu); /* temporal for spin */ /* same as above */ if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) spin_nurb(umat, obedit, tmp_vec, tmp_cent); else if ((U.flag & USER_ADD_VIEWALIGNED)) spin_nurb(viewmat, obedit, zvec, mat[3]); else spin_nurb(umat, obedit, tmp_vec, mat[3]); BLI_remlink(editnurb, nu); a= nu->pntsu*nu->pntsv; bp= nu->bp; while (a-- >0) { bp->f1 |= SELECT; bp++; } } break; default: /* should never happen */ BLI_assert(!"invalid nurbs type"); return NULL; } BLI_assert(nu != NULL); if (nu) { /* should always be set */ nu->flag |= CU_SMOOTH; BKE_nurb_test2D(nu); } return nu; } static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) { Object *obedit= CTX_data_edit_object(C); ListBase *editnurb; Nurb *nu; int newob= 0; int enter_editmode, is_aligned; unsigned int layer; float loc[3], rot[3]; float mat[4][4]; if (!ED_object_add_generic_get_opts(C, op, loc, rot, &enter_editmode, &layer, &is_aligned)) return OPERATOR_CANCELLED; if (!isSurf) { /* adding curve */ if (obedit==NULL || obedit->type!=OB_CURVE) { Curve *cu; obedit= ED_object_add_type(C, OB_CURVE, loc, rot, TRUE, layer); newob = 1; cu= (Curve*)obedit->data; cu->flag |= CU_DEFORM_FILL; if (type & CU_PRIM_PATH) cu->flag |= CU_PATH|CU_3D; } else DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); } else { /* adding surface */ if (obedit==NULL || obedit->type!=OB_SURF) { obedit= ED_object_add_type(C, OB_SURF, loc, rot, TRUE, layer); newob = 1; } else DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); } /* rename here, the undo stack checks name for valid undo pushes */ if (newob) { if (obedit->type==OB_CURVE) { rename_id((ID *)obedit, get_curve_defname(type)); rename_id((ID *)obedit->data, get_curve_defname(type)); } else { rename_id((ID *)obedit, get_surf_defname(type)); rename_id((ID *)obedit->data, get_surf_defname(type)); } } /* ED_object_add_type doesnt do an undo, is needed for redo operator on primitive */ if (newob && enter_editmode) ED_undo_push(C, "Enter Editmode"); ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); nu= add_nurbs_primitive(C, mat, type, newob); editnurb= object_editcurve_get(obedit); BLI_addtail(editnurb, nu); /* userdef */ if (newob && !enter_editmode) { ED_object_exit_editmode(C, EM_FREEDATA); } WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, obedit); return OPERATOR_FINISHED; } static int curve_prim_add(bContext *C, wmOperator *op, int type) { return curvesurf_prim_add(C, op, type, 0); } static int surf_prim_add(bContext *C, wmOperator *op, int type) { return curvesurf_prim_add(C, op, type, 1); } /* ******************** Curves ******************* */ static int add_primitive_bezier_exec(bContext *C, wmOperator *op) { return curve_prim_add(C, op, CU_BEZIER|CU_PRIM_CURVE); } void CURVE_OT_primitive_bezier_curve_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Bezier"; ot->description = "Construct a Bezier Curve"; ot->idname = "CURVE_OT_primitive_bezier_curve_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_bezier_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_bezier_circle_exec(bContext *C, wmOperator *op) { return curve_prim_add(C, op, CU_BEZIER|CU_PRIM_CIRCLE); } void CURVE_OT_primitive_bezier_circle_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Bezier Circle"; ot->description = "Construct a Bezier Circle"; ot->idname = "CURVE_OT_primitive_bezier_circle_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_bezier_circle_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_curve_exec(bContext *C, wmOperator *op) { return curve_prim_add(C, op, CU_NURBS|CU_PRIM_CURVE); } void CURVE_OT_primitive_nurbs_curve_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Nurbs Curve"; ot->description = "Construct a Nurbs Curve"; ot->idname = "CURVE_OT_primitive_nurbs_curve_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_curve_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_circle_exec(bContext *C, wmOperator *op) { return curve_prim_add(C, op, CU_NURBS|CU_PRIM_CIRCLE); } void CURVE_OT_primitive_nurbs_circle_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Nurbs Circle"; ot->description = "Construct a Nurbs Circle"; ot->idname = "CURVE_OT_primitive_nurbs_circle_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_circle_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_curve_path_exec(bContext *C, wmOperator *op) { return curve_prim_add(C, op, CU_NURBS|CU_PRIM_PATH); } void CURVE_OT_primitive_nurbs_path_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Path"; ot->description = "Construct a Path"; ot->idname = "CURVE_OT_primitive_nurbs_path_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_curve_path_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } /* **************** NURBS surfaces ********************** */ static int add_primitive_nurbs_surface_curve_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_CURVE|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_curve_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Curve"; ot->description = "Construct a Nurbs surface Curve"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_curve_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_curve_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_surface_circle_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_CIRCLE|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_circle_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Circle"; ot->description = "Construct a Nurbs surface Circle"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_circle_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_circle_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_surface_surface_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_PATCH|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_surface_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Patch"; ot->description = "Construct a Nurbs surface Patch"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_surface_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_surface_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_surface_cylinder_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_TUBE|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_cylinder_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Cylinder"; ot->description = "Construct a Nurbs surface Cylinder"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_cylinder_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_cylinder_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_surface_sphere_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_SPHERE|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_sphere_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Sphere"; ot->description = "Construct a Nurbs surface Sphere"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_sphere_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_sphere_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } static int add_primitive_nurbs_surface_torus_exec(bContext *C, wmOperator *op) { return surf_prim_add(C, op, CU_PRIM_DONUT|CU_NURBS); } void SURFACE_OT_primitive_nurbs_surface_torus_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Surface Torus"; ot->description = "Construct a Nurbs surface Torus"; ot->idname = "SURFACE_OT_primitive_nurbs_surface_torus_add"; /* api callbacks */ ot->invoke = ED_object_add_generic_invoke; ot->exec = add_primitive_nurbs_surface_torus_exec; ot->poll = ED_operator_scene_editable; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ED_object_add_generic_props(ot, TRUE); } /***************** clear tilt operator ********************/ static int clear_tilt_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit= CTX_data_edit_object(C); Curve *cu= obedit->data; ListBase *editnurb= object_editcurve_get(obedit); Nurb *nu; BezTriple *bezt; BPoint *bp; int a; for (nu= editnurb->first; nu; nu= nu->next) { if ( nu->bezt ) { bezt= nu->bezt; a= nu->pntsu; while (a--) { if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) bezt->alfa= 0.0; bezt++; } } else if (nu->bp) { bp= nu->bp; a= nu->pntsu*nu->pntsv; while (a--) { if (bp->f1 & SELECT) bp->alfa= 0.0; bp++; } } } WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); DAG_id_tag_update(obedit->data, 0); return OPERATOR_FINISHED; } void CURVE_OT_tilt_clear(wmOperatorType *ot) { /* identifiers */ ot->name = "Clear Tilt"; ot->idname = "CURVE_OT_tilt_clear"; /* api callbacks */ ot->exec = clear_tilt_exec; ot->poll = ED_operator_editcurve; /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } /****************** undo for curves ****************/ static void *undo_check_lastsel(void *lastsel, Nurb *nu, Nurb *newnu) { if (nu->bezt) { BezTriple *lastbezt= (BezTriple*)lastsel; if (lastbezt >= nu->bezt && lastbezt < nu->bezt + nu->pntsu) { return newnu->bezt + (lastbezt - nu->bezt); } } else { BPoint *lastbp= (BPoint*)lastsel; if (lastbp >= nu->bp && lastbp < nu->bp + nu->pntsu*nu->pntsv) { return newnu->bp + (lastbp - nu->bp); } } return NULL; } static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v) { Curve *cu= cu_v; UndoCurve *undoCurve= ucu; ListBase *undobase= &undoCurve->nubase; ListBase *editbase= BKE_curve_editNurbs_get(cu); Nurb *nu, *newnu; EditNurb *editnurb= cu->editnurb; void *lastsel= NULL; AnimData *ad= BKE_animdata_from_id(&cu->id); BKE_nurbList_free(editbase); if (undoCurve->undoIndex) { BLI_ghash_free(editnurb->keyindex, NULL, (GHashValFreeFP)MEM_freeN); editnurb->keyindex= dupli_keyIndexHash(undoCurve->undoIndex); } if (ad) { if (ad->action) { free_fcurves(&ad->action->curves); copy_fcurves(&ad->action->curves, &undoCurve->fcurves); } free_fcurves(&ad->drivers); copy_fcurves(&ad->drivers, &undoCurve->drivers); } /* copy */ for (nu= undobase->first; nu; nu= nu->next) { newnu= BKE_nurb_duplicate(nu); if (lastsel == NULL) { lastsel= undo_check_lastsel(undoCurve->lastsel, nu, newnu); } if (editnurb->keyindex) { keyIndex_updateNurb(editnurb, nu, newnu); } BLI_addtail(editbase, newnu); } cu->lastsel= lastsel; cu->actnu= undoCurve->actnu; ED_curve_updateAnimPaths(cu); } static void *editCurve_to_undoCurve(void *UNUSED(edata), void *cu_v) { Curve *cu= cu_v; ListBase *nubase= BKE_curve_editNurbs_get(cu); UndoCurve *undoCurve; EditNurb *editnurb= cu->editnurb, tmpEditnurb; Nurb *nu, *newnu; void *lastsel= NULL; AnimData *ad= BKE_animdata_from_id(&cu->id); undoCurve= MEM_callocN(sizeof(UndoCurve), "undoCurve"); if (editnurb->keyindex) { undoCurve->undoIndex= dupli_keyIndexHash(editnurb->keyindex); tmpEditnurb.keyindex= undoCurve->undoIndex; } if (ad) { if (ad->action) copy_fcurves(&undoCurve->fcurves, &ad->action->curves); copy_fcurves(&undoCurve->drivers, &ad->drivers); } /* copy */ for (nu= nubase->first; nu; nu= nu->next) { newnu= BKE_nurb_duplicate(nu); if (lastsel == NULL) { lastsel= undo_check_lastsel(cu->lastsel, nu, newnu); } if (undoCurve->undoIndex) { keyIndex_updateNurb(&tmpEditnurb, nu, newnu); } BLI_addtail(&undoCurve->nubase, newnu); } undoCurve->lastsel= lastsel; undoCurve->actnu= cu->actnu; return undoCurve; } static void free_undoCurve(void *ucv) { UndoCurve *undoCurve= ucv; BKE_nurbList_free(&undoCurve->nubase); if (undoCurve->undoIndex) BLI_ghash_free(undoCurve->undoIndex, NULL, (GHashValFreeFP)MEM_freeN); free_fcurves(&undoCurve->fcurves); free_fcurves(&undoCurve->drivers); MEM_freeN(undoCurve); } static void *get_data(bContext *C) { Object *obedit= CTX_data_edit_object(C); return obedit; } /* and this is all the undo system needs to know */ void undo_push_curve(bContext *C, const char *name) { undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL); } 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); } int ED_curve_actSelection(Curve *cu, float center[3]) { Nurb *nu= get_lastsel_nurb(cu); if (!nu) return 0; if (nu->bezt) { BezTriple *bezt= cu->lastsel; copy_v3_v3(center, bezt->vec[1]); } else { BPoint *bp= cu->lastsel; copy_v3_v3(center, bp->vec); } return 1; }