diff options
author | Ton Roosendaal <ton@blender.org> | 2009-01-14 15:26:45 +0300 |
---|---|---|
committer | Ton Roosendaal <ton@blender.org> | 2009-01-14 15:26:45 +0300 |
commit | ea2d6fe0d082b9069adfd72b704560937df3d855 (patch) | |
tree | df06dc078d4852c91b988e2d91c2e2808fb86491 /source/blender/editors/curve | |
parent | 07e85b265de672a4018cdefe14082576d92a763c (diff) |
2.5
Another one back: editcurve.c
- removed global editNurb everywhere
(cu->editnurb now has listbase with edit data)
- also added 'active bpoint' and 'active nurb' in
Curve struct
- editmode in/out works, mouse/border select works
Diffstat (limited to 'source/blender/editors/curve')
-rw-r--r-- | source/blender/editors/curve/editcurve.c | 4712 |
1 files changed, 4712 insertions, 0 deletions
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c new file mode 100644 index 00000000000..af69fcbb8c7 --- /dev/null +++ b/source/blender/editors/curve/editcurve.c @@ -0,0 +1,4712 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ + +#include <math.h> +#include <string.h> + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif +#include <stdlib.h> +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_dynstr.h" +#include "BLI_rand.h" + +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_edit.h" +#include "ED_object.h" +#include "ED_types.h" +#include "ED_util.h" +#include "ED_view3d.h" + +/* still need to eradicate a few :( */ +#define callocstructN(x,y,name) (x*)MEM_callocN((y)* sizeof(x),name) + +/* for curve objects in editmode that can have hidden handles - may use for IPO's later */ +#define BEZSELECTED_HIDDENHANDLES(bezt) ((G.f & G_HIDDENHANDLES) ? (bezt)->f2 & SELECT : BEZSELECTED(bezt)) + +/* XXX */ +static void BIF_undo_push() {} +static void waitcursor() {} +static void error() {} +static int okee() {return 0;} +static int pupmenu() {return 0;} +static int button() {return 0;} +static float fbutton() {return 0;} +static void adduplicate() {} +static void error_libdata() {} +/* XXX */ + +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 *curve_get_editcurve(Object *ob) +{ + if(ob && ELEM(ob->type, OB_CURVE, OB_SURF)) { + Curve *cu= ob->data; + return cu->editnurb; + } + return NULL; +} + +/* this replaces the active flag used in uv/face mode */ +void set_actNurb(Object *obedit, Nurb *nu) +{ + Curve *cu= obedit->data; + + if (nu==NULL) { + cu->actnu = -1; + } else { + cu->actnu = BLI_findindex(cu->editnurb, nu); + } +} + +Nurb *get_actNurb(Object *obedit) +{ + Curve *cu= obedit->data; + + return BLI_findlink(cu->editnurb, 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); +} + +short isNurbsel(Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a; + + if((nu->type & 7)==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; +} + +int isNurbsel_count(Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a, sel=0; + + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if (BEZSELECTED_HIDDENHANDLES(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= curve_get_editcurve(obedit); + Nurb *nu; + int a, num; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu) && (nu->type & 7)==CU_NURBS) { + if(nu->knotsu) { + num= KNOTSU(nu); + for(a=0;a<num;a++) printf("knotu %d: %f\n", a, nu->knotsu[a]); + } + if(nu->knotsv) { + num= KNOTSV(nu); + for(a=0;a<num;a++) printf("knotv %d: %f\n", a, nu->knotsv[a]); + } + } + } +} + +/* ********************* LOAD and MAKE *************** */ + +/* load editNurb in object */ +void load_editNurb(Object *obedit) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + + if(obedit==NULL) return; + + if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + Nurb *nu, *newnu; + KeyBlock *actkey; + int totvert= count_curveverts(editnurb); + + /* are there keys? */ + actkey = ob_get_keyblock(obedit); + if(actkey) { + /* active key: the vertices */ + + if(totvert) { + if(actkey->data) MEM_freeN(actkey->data); + + actkey->data= MEM_callocN(cu->key->elemsize*totvert, "actkey->data"); + actkey->totelem= totvert; + + curve_to_key(cu, actkey, editnurb); + } + } + + if(cu->key && actkey!=cu->key->refkey) { + ; + } + else { + freeNurblist(&(cu->nurb)); + + for(nu= editnurb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(&(cu->nurb), newnu); + + if((nu->type & 7)==CU_NURBS) { + clamp_nurb_order_u(nu); + } + } + } + } + + set_actNurb(obedit, NULL); +} + +/* make copy in cu->editnurb */ +void make_editNurb(Object *obedit) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *newnu; + KeyBlock *actkey; + + if(obedit==NULL) return; + + if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + + if(editnurb) + freeNurblist(editnurb); + else + editnurb= cu->editnurb= MEM_callocN(sizeof(ListBase), "editnurb"); + + nu= cu->nurb.first; + cu->lastselbp= NULL; /* for select row */ + + while(nu) { + newnu= duplicateNurb(nu); + test2DNurb(newnu); // after join, or any other creation of curve + BLI_addtail(editnurb, newnu); + nu= nu->next; + } + + actkey = ob_get_keyblock(obedit); + if(actkey) { + strcpy(G.editModeTitleExtra, "(Key) "); + key_to_curve(actkey, cu, editnurb); + } + } + + set_actNurb(obedit, NULL); +} + +void remake_editNurb(Object *obedit) +{ + + if(okee("Reload original data")==0) return; + + make_editNurb(obedit); +} + +void free_editNurb(Object *obedit) +{ + Curve *cu= obedit->data; + + if(cu->editnurb) { + freeNurblist(cu->editnurb); + MEM_freeN(cu->editnurb); + cu->editnurb= NULL; + } +} + +void separate_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu, *nu1; + Object *oldob; + Base *base, *oldbase; + Curve *cu; + ListBase editnurbo; + + if( v3d==0 || (v3d->lay & obedit->lay)==0 ) return; + + if(okee("Separate")==0) return; + + waitcursor(1); + + cu= obedit->data; + if(cu->key) { + error("Can't separate a curve with vertex keys"); + return; + } + + /* we are going to trick everything as follows: + * 1. duplicate base: this is the new one, remember old pointer + * 2. set aside all NOT selected curves/nurbs + * 3. load_ebaseNurb(): this will be the new base + * 4. freelist and restore old nurbs + */ + + /* only edit-base selected */ + base= FIRSTBASE; + while(base) { + if(base->lay & v3d->lay) { + if(base->object==obedit) base->flag |= 1; + else base->flag &= ~1; + } + base= base->next; + } + + /* set aside: everything that is not selected */ + editnurbo.first= editnurbo.last= 0; + nu= editnurb->first; + while(nu) { + nu1= nu->next; + if(isNurbsel(nu)==0) { + BLI_remlink(editnurb, nu); + BLI_addtail(&editnurbo, nu); + } + nu= nu1; + } + + oldob= obedit; + oldbase= BASACT; + + adduplicate(1, 0); /* no transform and zero so do get a linked dupli */ + + obedit= BASACT->object; /* basact is set in adduplicate() */ + + obedit->data= copy_curve(cu); + /* because new curve is a copy: reduce user count */ + cu->id.us--; + + load_editNurb(obedit); + + BASACT->flag &= ~SELECT; + + if(editnurb->first) freeNurblist(editnurb); + + *editnurb= editnurbo; + + obedit= 0; /* displists behave different in edit mode */ + DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA); /* this is the separated one */ + DAG_object_flush_update(scene, oldob, OB_RECALC_DATA); /* this is the original one */ + + obedit= oldob; + BASACT= oldbase; + BASACT->flag |= SELECT; + + waitcursor(0); + + set_actNurb(obedit, NULL); +} + +/* ******************* FLAGS ********************* */ + + +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 collumn 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; b<nu->pntsv; b++) { + sel= 0; + for(a=0; a<nu->pntsu; 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; a<nu->pntsu; a++) { + sel= 0; + bp= nu->bp+a; + for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsv) { + if(*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; +} + +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 & 7)==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++; + } + } + } +} + +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 & 7)==CU_NURBS) { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + + while(a--) { + if(bp->f1 & flag) { + bp->vec[0]-=cent[0]; + bp->vec[1]-=cent[1]; + bp->vec[2]-=cent[2]; + Mat3MulVecfl(rotmat, bp->vec); + bp->vec[0]+=cent[0]; + bp->vec[1]+=cent[1]; + bp->vec[2]+=cent[2]; + } + bp++; + } + } + } +} + + +void translateflagNurb(ListBase *editnurb, short flag, float *vec) +{ + /* 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 & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(bezt->f1 & flag) VecAddf(bezt->vec[0], bezt->vec[0], vec); + if(bezt->f2 & flag) VecAddf(bezt->vec[1], bezt->vec[1], vec); + if(bezt->f3 & flag) VecAddf(bezt->vec[2], bezt->vec[2], vec); + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(bp->f1 & flag) VecAddf(bp->vec, bp->vec, vec); + bp++; + } + } + + test2DNurb(nu); + } +} + +void weightflagNurb(ListBase *editnurb, short flag, float w, int mode) /* mode==0: replace, mode==1: multiply */ +{ + Nurb *nu; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_NURBS) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(bp->f1 & flag) { + if(mode==1) bp->vec[3]*= w; + else bp->vec[3]= w; + } + bp++; + } + } + } +} + +void deleteflagNurb(Scene *scene, short flag) +{ + Object *obedit= scene->obedit; // XXX use context + Curve *cu= obedit->data; + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *next; + BPoint *bp, *bpn, *newbp; + int a, b, newu, newv, sel; + + if(obedit && obedit->type==OB_SURF); + else return; + + cu->lastselbp= 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); + freeNurb(nu); nu=NULL; + } + else { + /* is nurb in U direction selected */ + newv= nu->pntsv; + bp= nu->bp; + for(b=0; b<nu->pntsv; b++) { + sel= 0; + for(a=0; a<nu->pntsu; a++, bp++) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsu) { + 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; b<nu->pntsv; b++) { + if((bp->f1 & flag)==0) { + memcpy(bpn, bp, nu->pntsu*sizeof(BPoint)); + bpn+= nu->pntsu; + } + bp+= nu->pntsu; + } + nu->pntsv= newv; + MEM_freeN(nu->bp); + nu->bp= newbp; + clamp_nurb_order_v(nu); + + makeknots(nu, 2, nu->flagv>>1); + } + else { + /* is the nurb in V direction selected */ + newu= nu->pntsu; + for(a=0; a<nu->pntsu; a++) { + bp= nu->bp+a; + sel= 0; + for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsv) { + 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; b<nu->pntsv; b++) { + for(a=0; a<nu->pntsu; a++, bp++) { + if((bp->f1 & flag)==0) { + *bpn= *bp; + bpn++; + } + } + } + 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); + clamp_nurb_order_u(nu); + if(nu->knotsv) MEM_freeN(nu->knotsv); + nu->knotsv= NULL; + } + else { + nu->pntsu= newu; + clamp_nurb_order_u(nu); + } + makeknots(nu, 1, nu->flagu>>1); + } + } + } + nu= next; + } +} + +/* only for OB_SURF */ +short extrudeflagNurb(ListBase *editnurb, int flag) +{ + Nurb *nu; + BPoint *bp, *bpn, *newbp; + int ok= 0, a, u, v, len; + + nu= editnurb->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"); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) ); + bp= newbp+ nu->pntsu; + memcpy(bp, nu->bp, nu->pntsu*sizeof(BPoint) ); + 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; + makeknots(nu, 2, nu->flagv>>1); + } + } + else { + /* which row or collumn 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; + memcpy(newbp+nu->pntsu, nu->bp, len*sizeof(BPoint) ); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) ); + bp= newbp; + } + else { + len= nu->pntsv*nu->pntsu; + memcpy(newbp, nu->bp, len*sizeof(BPoint) ); + memcpy(newbp+len, nu->bp+len-nu->pntsu, nu->pntsu*sizeof(BPoint) ); + bp= newbp+len; + } + + a= nu->pntsu; + while(a--) { + select_bpoint(bp, SELECT, flag, HIDDEN); + bp++; + } + + MEM_freeN(nu->bp); + nu->bp= newbp; + nu->pntsv++; + makeknots(nu, 2, nu->flagv>>1); + } + else if(v==0 || v== nu->pntsu-1) { /* collumn 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; a<nu->pntsv; a++) { + if(v==0) { + *bpn= *bp; + bpn->f1 |= flag; + bpn++; + } + memcpy(bpn, bp, nu->pntsu*sizeof(BPoint)); + 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++; + makeknots(nu, 1, nu->flagu>>1); + } + } + } + nu= nu->next; + } + + return ok; +} + +void adduplicateflagNurb(Scene *scene, short flag) +{ + Object *obedit= scene->obedit; // XXX context + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *newnu; + BezTriple *bezt, *bezt1; + BPoint *bp, *bp1; + int a, b, starta, enda, newu, newv; + char *usel; + + nu= editnurb->last; + while(nu) { + if( (nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + for(a=0; a<nu->pntsu; 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_CYCLIC) { + if(starta!=0 || enda!=nu->pntsu-1) { + newnu->flagu &= ~CU_CYCLIC; + } + } + } + bezt++; + } + } + else if(nu->pntsv==1) { /* because UV Nurb has a different method for dupli */ + bp= nu->bp; + for(a=0; a<nu->pntsu; 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_CYCLIC) { + if(starta!=0 || enda!=nu->pntsu-1) { + newnu->flagu &= ~CU_CYCLIC; + } + } + + /* knots */ + newnu->knotsu= NULL; + makeknots(newnu, 1, newnu->flagu>>1); + } + 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; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp++) { + if(bp->f1 & flag) usel[b]++; + } + } + newu= 0; + newv= 0; + for(a=0; a<nu->pntsu; a++) { + if(usel[a]) { + if(newv==0 || usel[a]==newv) { + newv= usel[a]; + newu++; + } + else { + newv= 0; + break; + } + } + } + if(newu==0 || newv==0) { + 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"); + clamp_nurb_order_u(newnu); + clamp_nurb_order_v(newnu); + + newnu->knotsu= newnu->knotsv= NULL; + + bp= newnu->bp; + bp1= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp1++) { + if(bp1->f1 & flag) { + memcpy(bp, bp1, sizeof(BPoint)); + select_bpoint(bp1, DESELECT, flag, HIDDEN); + bp++; + } + } + } + if (check_valid_nurb_u(newnu)) { + if(nu->pntsu==newnu->pntsu && nu->knotsu) { + newnu->knotsu= MEM_dupallocN( nu->knotsu ); + } else { + makeknots(newnu, 1, newnu->flagu>>1); + } + } + if (check_valid_nurb_v(newnu)) { + if(nu->pntsv==newnu->pntsv && nu->knotsv) { + newnu->knotsv= MEM_dupallocN( nu->knotsv ); + } else { + makeknots(newnu, 2, newnu->flagv>>1); + } + } + } + MEM_freeN(usel); + } + } + + nu= nu->prev; + } + + /* actnu changed */ +} + + +void switchdirectionNurb2(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu; + + if(v3d==0 || !(obedit->lay & v3d->lay)) + return; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( isNurbsel(nu) ) switchdirectionNurb(nu); + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + BIF_undo_push("Switch direction"); +} + +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); +} + +void setweightNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX context + ListBase *editnurb= curve_get_editcurve(obedit); + static float weight= 1.0f; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + if(fbutton(&weight, 0.0f, 1.0f, 10, 10, "Set Weight")) { + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) + bezt->weight= weight; + } + } + else if(nu->bp) { + for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) { + if(bp->f1 & SELECT) + bp->weight= weight; + } + } + } + } + BIF_undo_push("Set Curve Weight"); + DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA); +} + +void setradiusNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + static float radius= 1.0f; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + if(fbutton(&radius, 0.0001f, 10.0f, 10, 10, "Set Radius")) { + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) + bezt->radius= radius; + } + } + else if(nu->bp) { + for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) { + if(bp->f1 & SELECT) + bp->radius= radius; + } + } + } + } + BIF_undo_push("Set Curve Radius"); + DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA); +} + +void smoothNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt, *beztOrig; + BPoint *bp, *bpOrig; + int a, i, change = 0; + + /* floats for smoothing */ + float val, newval, offset; + + 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; a<nu->pntsu-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.5) + ((beztOrig+(a+1))->vec[1][i] * 0.5); + offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - 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) + calchandlesNurb(nu); + } else if (nu->bp) { + bpOrig = MEM_dupallocN( nu->bp ); + /* Same as above, keep these the same! */ + for(bp=nu->bp+1, a=1; a<nu->pntsu-1; a++, bp++) { + if(bp->f1 & SELECT) { + for(i=0; i<3; i++) { + val = bp->vec[i]; + newval = ((bpOrig+(a-1))->vec[i] * 0.5) + ((bpOrig+(a+1))->vec[i] * 0.5); + offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - val; + + bp->vec[i] += offset; + } + } + } + MEM_freeN(bpOrig); + } + } + BIF_undo_push("Smooth Curve"); + DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA); +} + +/* TODO, make smoothing distance based */ +void smoothradiusNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + /* use for smoothing */ + int last_sel; + int start_sel, end_sel; /* selection indicies, 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 = end_sel = -1; + for(bezt=nu->bezt+last_sel, a=last_sel; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) { + start_sel = a; + break; + } + } + /* incase there are no other selected verts */ + end_sel = start_sel; + for(bezt=nu->bezt+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bezt++) { + if((bezt->f2 & SELECT)==0) { + break; + } + end_sel = a; + } + + if (start_sel == -1) { + last_sel = nu->pntsu; /* next... */ + } else { + last_sel = end_sel; /* before we modify it */ + + /* now blend between start and end sel */ + start_rad = end_rad = -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.0 && end_rad >= 0.0) (nu->bezt+start_sel)->radius = (start_rad + end_rad)/2; + else if (start_rad >= 0.0) (nu->bezt+start_sel)->radius = start_rad; + else if (end_rad >= 0.0) (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 dont 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 dont 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.0-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 = end_sel = -1; + for(bp=nu->bp+last_sel, a=last_sel; a<nu->pntsu; a++, bp++) { + if(bp->f1 & SELECT) { + start_sel = a; + break; + } + } + /* incase there are no other selected verts */ + end_sel = start_sel; + for(bp=nu->bp+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bp++) { + if((bp->f1 & SELECT)==0) { + break; + } + end_sel = a; + } + + if (start_sel == -1) { + last_sel = nu->pntsu; /* next... */ + } else { + last_sel = end_sel; /* before we modify it */ + + /* now blend between start and end sel */ + start_rad = end_rad = -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.0 && end_rad >= 0.0) (nu->bp+start_sel)->radius = (start_rad + end_rad)/2; + else if (start_rad >= 0.0) (nu->bp+start_sel)->radius = start_rad; + else if (end_rad >= 0.0) (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 dont 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 dont 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.0-fac) + end_rad*fac; + } + } + } + } + } + } + BIF_undo_push("Smooth Curve Radius"); + DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA); +} + + + +/* **************** EDIT ************************ */ + +/* next == 1 -> select next */ +/* next == -1 -> select previous */ +/* cont == 1 -> select continuously */ +/* selstatus, inverts behaviour */ +static void select_adjacent_cp(ListBase *editnurb, short next, short cont, short selstatus) +{ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + short lastsel= 0, sel=0; + + if(next==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + lastsel=0; + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + if(next < 0) bezt= (nu->bezt + (a-1)); + while(a--) { + if(a-abs(next) < 0) break; + sel= 0; + if((lastsel==0) && (bezt->hide==0) && ((bezt->f2 & SELECT) || (selstatus==0))) { + bezt+=next; + if(!(bezt->f2 & SELECT) || (selstatus==0)) { + 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; + sel=0; + if((lastsel==0) && (bp->hide==0) && ((bp->f1 & SELECT) || (selstatus==0))) { + bp+=next; + if(!(bp->f1 & SELECT) || (selstatus==0)) { + 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)); + } + } + } +} + +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 & 7)==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; +} + + +/* (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(Scene *scene, short selfirst, short doswap, short selstatus) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + short sel; + + if(obedit==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + sel= 0; + if((nu->type & 7)==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--) { + 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) { + if(doswap) sel= swap_selection_bpoint(bp); + else sel= select_bpoint(bp, selstatus, 1, VISIBLE); + + if(sel==1) break; + } + } + } + } + + BIF_undo_push("Select/Deselect End"); +} + +void deselectall_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + + if(!v3d || !(obedit->lay & v3d->lay)) + return; + + if(nurb_has_selected_cps(editnurb)) { /* deselect all */ + selectend_nurb(scene, FIRST, 0, DESELECT); /* set first control points as unselected */ + select_adjacent_cp(editnurb, 1, 1, DESELECT); /* cascade selection */ + } + else { /* select all */ + selectend_nurb(scene, FIRST, 0, SELECT); /* set first control points as selected */ + select_adjacent_cp(editnurb, 1, 1, SELECT); /* cascade selection */ + } + + BIF_undo_push("Deselect all"); +} + +void hideNurb(Scene *scene, int swap) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a, sel; + + if(obedit==0) return; + + BIF_undo_push("Hide"); + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + sel= 0; + while(a--) { + if(BEZSELECTED_HIDDENHANDLES(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(swap==0 && (bp->f1 & SELECT)) { + select_bpoint(bp, DESELECT, 1, HIDDEN); + bp->hide= 1; + } + else if(swap && (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_object_flush_update(scene, obedit, OB_RECALC_DATA); +} + +void revealNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + + if(obedit==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + nu->hide= 0; + if((nu->type & 7)==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_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Reveal"); + +} + +void selectswapNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + + if(obedit==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(bezt->hide==0) { + bezt->f2 ^= SELECT; /* always do the center point */ + if ((G.f & G_HIDDENHANDLES)==0) { + bezt->f1 ^= SELECT; + bezt->f3 ^= SELECT; + } + } + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + swap_selection_bpoint(bp); + bp++; + } + } + } + + BIF_undo_push("Select swap"); + +} + +/** 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. + * + * @deffunc subdividenurb subdivideNurb(void) + * @return Nothing + * @param None +*/ +void subdivideNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *prevbezt, *bezt, *beztnew, *beztn; + BPoint *bp, *prevbp, *bpnew, *bpn; + float vec[15]; + int a, b, sel, amount, *usel, *vsel; + + // printf("*** subdivideNurb: entering subdivide\n"); + + for(nu= editnurb->first; nu; nu= nu->next) { + amount= 0; + if((nu->type & 7)==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_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(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) amount++; + prevbezt= bezt; + bezt++; + } + + if(amount) { + /* insert */ + beztnew = + (BezTriple*)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb"); + beztn= beztnew; + if(nu->flagu & CU_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)); + beztn++; + + if( BEZSELECTED_HIDDENHANDLES(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) { + memcpy(beztn, bezt, sizeof(BezTriple)); + + /* midpoint subdividing */ + VecMidf(vec, prevbezt->vec[1], prevbezt->vec[2]); + VecMidf(vec+3, prevbezt->vec[2], bezt->vec[0]); + VecMidf(vec+6, bezt->vec[0], bezt->vec[1]); + + VecMidf(vec+9, vec, vec+3); + VecMidf(vec+12, vec+3, vec+6); + + /* change handle of prev beztn */ + VECCOPY((beztn-1)->vec[2], vec); + /* new point */ + VECCOPY(beztn->vec[0], vec+9); + VecMidf(beztn->vec[1], vec+9, vec+12); + VECCOPY(beztn->vec[2], vec+12); + /* handle of next bezt */ + if(a==0 && (nu->flagu & CU_CYCLIC)) {VECCOPY(beztnew->vec[0], vec+6);} + else {VECCOPY(bezt->vec[0], vec+6);} + + beztn->radius = (prevbezt->radius + bezt->radius)/2.0f; + beztn->weight = (prevbezt->weight + bezt->weight)/2.0f; + + beztn++; + } + + prevbezt= bezt; + bezt++; + } + /* last point */ + if((nu->flagu & CU_CYCLIC)==0) memcpy(beztn, prevbezt, sizeof(BezTriple)); + + MEM_freeN(nu->bezt); + nu->bezt= beztnew; + nu->pntsu+= amount; + + calchandlesNurb(nu); + } + } /* End of 'if((nu->type & 7)==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_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++; + prevbp= bp; + bp++; + } + + if(amount) { + /* insert */ + bpnew = + (BPoint*)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2"); + bpn= bpnew; + + if(nu->flagu & CU_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)); + bpn++; + + if( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) { + // printf("*** subdivideNurb: insert 'linear' point\n"); + memcpy(bpn, bp, sizeof(BPoint)); + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + + } + prevbp= bp; + bp++; + } + if((nu->flagu & CU_CYCLIC)==0) memcpy(bpn, prevbp, sizeof(BPoint)); /* last point */ + + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu+= amount; + + if(nu->type & 4) { + makeknots(nu, 1, nu->flagu>>1); + } + } + } /* End of 'else if(nu->pntsv==1)' */ + else if((nu->type & 7)==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 opration. 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; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + if(bp->f1 & SELECT) { + usel[b]++; + vsel[a]++; + sel++; + } + bp++; + } + } + if( sel == (nu->pntsu*nu->pntsv) ) { /* subdivide entire nurb */ + /* Global subdivision is a special case of partial + subdivision. Strange it is considered separately... */ + bpn=bpnew= MEM_mallocN( (2*nu->pntsu-1)*(2*nu->pntsv-1)*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + /* first subdivide rows */ + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + if(b<nu->pntsu-1) { + *bpn= *bp; + prevbp= bp-1; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + } + } + bpn+= (2*nu->pntsu-1); + } + /* now insert new */ + bpn= bpnew+(2*nu->pntsu-1); + bp= bpnew+(4*nu->pntsu-2); + prevbp= bpnew; + for(a=1; a<nu->pntsv; a++) { + + for(b=0; b<2*nu->pntsu-1; b++) { + *bpn= *bp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + bp++; + prevbp++; + } + bp+= (2*nu->pntsu-1); + bpn+= (2*nu->pntsu-1); + prevbp+= (2*nu->pntsu-1); + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu= 2*nu->pntsu-1; + nu->pntsv= 2*nu->pntsv-1; + makeknots(nu, 1, nu->flagu>>1); + makeknots(nu, 2, nu->flagv>>1); + } /* End of 'if(sel== nu->pntsu*nu->pntsv)' (subdivide entire NURB) */ + else { + /* subdivide in v direction? */ + sel= 0; + for(a=0; a<nu->pntsv-1; a++) { + if(vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu) sel++; + } + + if(sel) { /* V ! */ + bpn=bpnew= MEM_mallocN( (sel+nu->pntsv)*nu->pntsu*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + } + if( (a<nu->pntsv-1) && vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu ) { + prevbp= bp- nu->pntsu; + for(b=0; b<nu->pntsu; b++) { + /* + This simple bisection must be replaces by a + subtle resampling of a number of points. Our + task is made slightly easier because each + point in our curve is a separate data + node. (is it?) + */ + *bpn= *prevbp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + prevbp++; + bp++; + } + bp-= nu->pntsu; + } + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsv+= sel; + makeknots(nu, 2, nu->flagv>>1); + } + else { + /* or in u direction? */ + sel= 0; + for(a=0; a<nu->pntsu-1; a++) { + if(usel[a]==nu->pntsv && usel[a+1]==nu->pntsv) sel++; + } + + if(sel) { /* U ! */ + /* Inserting U points is sort of 'default' Flat curves only get */ + /* U points inserted in them. */ + bpn=bpnew= MEM_mallocN( (sel+nu->pntsu)*nu->pntsv*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + if( (b<nu->pntsu-1) && usel[b]==nu->pntsv && usel[b+1]==nu->pntsv ) { + /* + One thing that bugs me here is that the + orders of things are not the same as in + the JW piece. Also, this implies that we + handle at most 3rd order curves? I miss + some symmetry here... + */ + prevbp= bp- 1; + *bpn= *prevbp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + } + } + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu+= sel; + makeknots(nu, 1, nu->flagu>>1); /* shift knots + forward */ + } + } + } + MEM_freeN(usel); + MEM_freeN(vsel); + + } /* End of 'if((nu->type & 7)==CU_NURBS)' */ + } + + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + BIF_undo_push("Subdivide"); + +} + +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; short 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 (temp<data->dist) { + data->dist = temp; + + data->bp = bp; + data->bezt = bezt; + data->nurb = nu; + data->hpoint = bezt?beztindex:0; + } +} + +static short findnearestNurbvert(ViewContext *vc, short sel, short 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; short dist, hpoint, select, mval[2]; } data = {0}; + + data.dist = 100; + data.hpoint = 0; + data.select = sel; + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + + 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= 0; + *bezt= 0; + *bp= 0; + for(nu1= editnurb->first; nu1; nu1= nu1->next) { + if((nu1->type & 7)==CU_BEZIER) { + bezt1= nu1->bezt; + a= nu1->pntsu; + while(a--) { + if( (bezt1->f1 & SELECT) || (bezt1->f2 & SELECT) || (bezt1->f3 & SELECT) ) { + if(*nu!=0 && *nu!= nu1) { + *nu= 0; + *bp= 0; + *bezt= 0; + return; + } + else if(*bezt || *bp) { + *bp= 0; + *bezt= 0; + } + else { + *bezt= bezt1; + *nu= nu1; + } + } + bezt1++; + } + } + else { + bp1= nu1->bp; + a= nu1->pntsu*nu1->pntsv; + while(a--) { + if( bp1->f1 & 1 ) { + if(*nu!=0 && *nu!= nu1) { + *bp= 0; + *bezt= 0; + *nu= 0; + return; + } + else if(*bezt || *bp) { + *bp= 0; + *bezt= 0; + } + else { + *bp= bp1; + *nu= nu1; + } + } + bp1++; + } + } + } +} + +int convertspline(short type, Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a, c, nr; + + if((nu->type & 7)==0) { /* 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--) { + VECCOPY(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= 0; + nu->pntsu= nr; + nu->type &= ~7; + nu->type |= 1; + calchandlesNurb(nu); + } + else if(type==CU_NURBS) { + nu->type &= ~7; + nu->type+= 4; + nu->orderu= 4; + nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */ + nu->flagu += 4; + makeknots(nu, 1, nu->flagu>>1); + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + bp->vec[3]= 1.0; + bp++; + } + } + } + else if((nu->type & 7)==CU_BEZIER) { /* Bezier */ + if(type==0 || type==4) { /* to Poly or Nurb */ + 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==0 && bezt->h1==HD_VECT && bezt->h2==HD_VECT) { + /* vector handle becomes 1 poly vertice */ + VECCOPY(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++) { + VECCOPY(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= 0; + nu->pntsu= nr; + nu->pntsv= 1; + nu->orderu= 4; + nu->orderv= 1; + nu->type &= ~7; + nu->type+= type; + if(nu->flagu & CU_CYCLIC) c= nu->orderu-1; + else c= 0; + if(type== 4) { + nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */ + nu->flagu += 4; + makeknots(nu, 1, nu->flagu>>1); + } + } + } + else if( (nu->type & 7)==CU_NURBS) { + if(type==0) { /* to Poly */ + nu->type &= ~7; + 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--) { + VECCOPY(bezt->vec[0], bp->vec); + bezt->f1= bp->f1; + bp++; + VECCOPY(bezt->vec[1], bp->vec); + bezt->f2= bp->f1; + bp++; + VECCOPY(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= 0; + MEM_freeN(nu->knotsu); + nu->knotsu= NULL; + nu->pntsu= nr; + nu->type &= ~7; + nu->type+= 1; + } + } + } + return 0; +} + +void setsplinetype(Scene *scene, short type) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + + if(type==CU_CARDINAL || type==CU_BSPLINE) { + error("Not implemented yet"); + return; + } + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + if (convertspline(type, nu)) + error("no conversion possible"); + } + } + BIF_undo_push("Set spline type"); + +} + +/* ******************** SKINNING LOFTING!!! ******************** */ + +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; v<nu->pntsv; v++) { + for(u=0; u<nu->pntsu; u++, bp1++) { + bp2= temp + (nu->pntsu-u-1)*(nu->pntsv) + v; + *bp1= *bp2; + } + } + + MEM_freeN(temp); +} + +int is_u_selected(Nurb *nu, int u) +{ + BPoint *bp; + int v; + + /* what about resolu == 2? */ + bp= nu->bp+u; + for(v=0; v<nu->pntsv-1; v++, bp+=nu->pntsu) { + if(v) if(bp->f1 & SELECT) return 1; + } + + return 0; +} + +/* ******************************** */ + +typedef struct NurbSort { + struct NurbSort *next, *prev; + Nurb *nu; + float vec[3]; +} NurbSort; + +static ListBase nsortbase= {0, 0}; +/* static NurbSort *nusmain; */ /* this var seems to go unused... at least in this file */ + +void make_selection_list_nurb(ListBase *editnurb) +{ + ListBase nbase= {0, 0}; + 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--) { + VecAddf(nus->vec, nus->vec, bp->vec); + bp++; + } + VecMulf(nus->vec, 1.0/(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= 0; + + nustest= nbase.first; + while(nustest) { + dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.first)->vec); + + if(dist<headdist) { + headdist= dist; + headdo= nustest; + } + dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.last)->vec); + + if(dist<taildist) { + taildist= dist; + taildo= nustest; + } + nustest= nustest->next; + } + + if(headdist<taildist) { + BLI_remlink(&nbase, headdo); + BLI_addhead(&nsortbase, headdo); + } + else { + BLI_remlink(&nbase, taildo); + BLI_addtail(&nsortbase, taildo); + } + } +} + +void merge_2_nurb(ListBase *editnurb, Nurb *nu1, Nurb *nu2) +{ + BPoint *bp, *bp1, *bp2, *temp; + float len1, len2; + int origu, u, v; + + /* first nurbs will be changed to make u = resolu-1 selected */ + /* 2nd nurbs will be changed to make u = 0 selected */ + + /* first nurbs: u = resolu-1 selected */ + + if( is_u_selected(nu1, nu1->pntsu-1) ); + 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_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 { + 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 ) { + error("Resolution doesn't match"); + return; + } + + /* ok, now nu1 has the rightmost collumn and nu2 the leftmost collumn selected */ + /* maybe we need a 'v' flip of nu2? */ + + bp1= nu1->bp+nu1->pntsu-1; + bp2= nu2->bp; + len1= 0.0; + + for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2+=nu2->pntsu) { + len1+= VecLenf(bp1->vec, bp2->vec); + } + + bp1= nu1->bp + nu1->pntsu-1; + bp2= nu2->bp + nu2->pntsu*(nu2->pntsv-1); + len2= 0.0; + + for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2-=nu2->pntsu) { + len2+= VecLenf(bp1->vec, bp2->vec); + } + + /* merge */ + origu= nu1->pntsu; + nu1->pntsu+= nu2->pntsu; + if(nu1->orderu<3) nu1->orderu++; + if(nu1->orderv<3) nu1->orderv++; + temp= nu1->bp; + nu1->bp= MEM_mallocN(nu1->pntsu*nu1->pntsv*sizeof(BPoint), "mergeBP"); + + bp= nu1->bp; + bp1= temp; + + for(v=0; v<nu1->pntsv; v++) { + + /* switch direction? */ + if(len1<len2) bp2= nu2->bp + v*nu2->pntsu; + else bp2= nu2->bp + (nu1->pntsv-v-1)*nu2->pntsu; + + for(u=0; u<nu1->pntsu; u++, bp++) { + if(u<origu) { + *bp= *bp1; bp1++; + select_bpoint(bp, SELECT, 1, HIDDEN); + } + else { + *bp= *bp2; bp2++; + } + } + } + + if((nu1->type & 7)==4) { + /* merge knots */ + makeknots(nu1, 1, nu1->flagu>>1); + + /* make knots, for merged curved for example */ + makeknots(nu1, 2, nu1->flagv>>1); + } + + MEM_freeN(temp); + BLI_remlink(editnurb, nu2); + freeNurb(nu2); +} + +void merge_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + NurbSort *nus1, *nus2; + int ok= 1; + + make_selection_list_nurb(editnurb); + + if(nsortbase.first == nsortbase.last) { + BLI_freelistN(&nsortbase); + error("Too few selections to merge"); + return; + } + + 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) { + error("Resolution doesn't match"); + BLI_freelistN(&nsortbase); + return; + } + + while(nus2) { + merge_2_nurb(editnurb, nus1->nu, nus2->nu); + nus2= nus2->next; + } + + BLI_freelistN(&nsortbase); + + set_actNurb(obedit, NULL); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + BIF_undo_push("Merge"); + +} + + +void addsegment_nurb(Scene *scene) +{ + /* joins 2 curves */ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *nu1=0, *nu2=0; + BezTriple *bezt; + BPoint *bp; + float *fp, offset; + int a; + + /* first decide if this is a surface merge! */ + if(obedit->type==OB_SURF) nu= editnurb->first; + else nu= NULL; + + while(nu) { + if( isNurbsel(nu) ) { + + if(nu->pntsu>1 && nu->pntsv>1) break; + if(isNurbsel_count(nu)>1) break; + if(isNurbsel_count(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) { + merge_nurb(scene); + return; + } + + /* find both nurbs and points, nu1 will be put behind nu2 */ + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->flagu & CU_CYCLIC)==0) { /* not cyclic */ + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + if(nu1==0) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) nu1= nu; + else { + bezt= bezt+(nu->pntsu-1); + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu1= nu; + switchdirectionNurb(nu); + } + } + } + else if(nu2==0) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu2= nu; + switchdirectionNurb(nu); + } + else { + bezt= bezt+(nu->pntsu-1); + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu2= nu; + } + } + } + else break; + } + else if(nu->pntsv==1) { + bp= nu->bp; + if(nu1==0) { + if( bp->f1 & SELECT) nu1= nu; + else { + bp= bp+(nu->pntsu-1); + if( bp->f1 & SELECT ) { + nu1= nu; + switchdirectionNurb(nu); + } + } + } + else if(nu2==0) { + if( bp->f1 & SELECT ) { + nu2= nu; + switchdirectionNurb(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 & 7)==CU_BEZIER) { + bezt = + (BezTriple*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BezTriple), "addsegmentN"); + memcpy(bezt, nu2->bezt, nu2->pntsu*sizeof(BezTriple)); + memcpy(bezt+nu2->pntsu, nu1->bezt, nu1->pntsu*sizeof(BezTriple)); + MEM_freeN(nu1->bezt); + nu1->bezt= bezt; + nu1->pntsu+= nu2->pntsu; + BLI_remlink(editnurb, nu2); + freeNurb(nu2); nu2= NULL; + calchandlesNurb(nu1); + } + else { + bp = + (BPoint*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BPoint), "addsegmentN2"); + memcpy(bp, nu2->bp, nu2->pntsu*sizeof(BPoint) ); + memcpy(bp+nu2->pntsu, nu1->bp, nu1->pntsu*sizeof(BPoint)); + MEM_freeN(nu1->bp); + nu1->bp= bp; + + a= nu1->pntsu+nu1->orderu; + + nu1->pntsu+= nu2->pntsu; + BLI_remlink(editnurb, nu2); + + /* now join the knots */ + if((nu1->type & 7)==4) { + if(nu1->knotsu==NULL) { + makeknots(nu1, 1, nu1->flagu>>1); + } + 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.0; + fp= nu1->knotsu+a; + for(a=0; a<nu2->pntsu; a++, fp++) { + if(nu2->knotsu) + *fp= offset+nu2->knotsu[a+1]; + else + *fp = offset; + } + } + } + freeNurb(nu2); nu2= NULL; + } + } + + set_actNurb(obedit, NULL); /* for selected */ + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + BIF_undo_push("Add segment"); + + } + else error("Can't make segment"); +} + +void mouse_nurb(bContext *C, short mval[2], int extend) +{ + Object *obedit= CTX_data_edit_object(C); // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Curve *cu= obedit->data; + ViewContext vc; + Nurb *nu; + BezTriple *bezt=0; + BPoint *bp=0; + short hand; + + view3d_set_viewcontext(C, &vc); + + hand= findnearestNurbvert(&vc, 1, mval, &nu, &bezt, &bp); + + if(bezt || bp) { + if(extend==0) { + + setflagsNurb(editnurb, 0); + + if(bezt) { + + if(hand==1) select_beztriple(bezt, SELECT, 1, HIDDEN); + else if(hand==0) bezt->f1|= SELECT; + else bezt->f3|= SELECT; + } + else { + cu->lastselbp= bp; + select_bpoint(bp, SELECT, 1, HIDDEN); + } + + } + else { + if(bezt) { + if(hand==1) { + if(bezt->f2 & SELECT) select_beztriple(bezt, DESELECT, 1, HIDDEN); + else select_beztriple(bezt, SELECT, 1, HIDDEN); + } else if(hand==0) { + bezt->f1 ^= SELECT; + } else { + bezt->f3 ^= SELECT; + } + } + else { + if(bp->f1 & SELECT) select_bpoint(bp, DESELECT, 1, HIDDEN); + else { + select_bpoint(bp, SELECT, 1, HIDDEN); + cu->lastselbp= bp; + } + } + + } + + } + +// rightmouse_transform(); + + if(nu!=get_actNurb(obedit)) { + set_actNurb(obedit, nu); + } + +} + +/* from what I can gather, the mode==0 magic number spins and bridges the nurbs based on the + * orientation of the global 3d view (yuck yuck!) mode==1 does the same, but doesn't bridge up + * up the new geometry, mode==2 now does the same as 0, but aligned to world axes, not the view. +*/ +static void spin_nurb(Scene *scene, Object *obedit, float *dvec, short mode) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu; + float *curs, si,phi,n[3],q[4],cmat[3][3],tmat[3][3],imat[3][3]; + float cent[3],bmat[3][3], rotmat[3][3], scalemat1[3][3], scalemat2[3][3]; + float persmat[3][3], persinv[3][3]; + short a,ok; + + if(v3d==0 || obedit==0 || obedit->type!=OB_SURF) return; + if( (v3d->lay & obedit->lay)==0 ) return; + + if (mode != 2) Mat3CpyMat4(persmat, v3d->viewmat); + else Mat3One(persmat); + Mat3Inv(persinv, persmat); + + /* imat and center and size */ + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + curs= give_cursor(scene, v3d); + VECCOPY(cent, curs); + VecSubf(cent, cent, obedit->obmat[3]); + Mat3MulVecfl(imat,cent); + + if(dvec || mode==2) { + n[0]=n[1]= 0.0; + n[2]= 1.0; + } else { + n[0]= v3d->viewinv[2][0]; + n[1]= v3d->viewinv[2][1]; + n[2]= v3d->viewinv[2][2]; + Normalize(n); + } + + 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; + QuatToMat3(q, cmat); + Mat3MulMat3(tmat, cmat, bmat); + Mat3MulMat3(rotmat, imat, tmat); + + Mat3One(scalemat1); + scalemat1[0][0]= sqrt(2.0); + scalemat1[1][1]= sqrt(2.0); + + Mat3MulMat3(tmat,persmat,bmat); + Mat3MulMat3(cmat,scalemat1,tmat); + Mat3MulMat3(tmat,persinv,cmat); + Mat3MulMat3(scalemat1,imat,tmat); + + Mat3One(scalemat2); + scalemat2[0][0]/= sqrt(2.0); + scalemat2[1][1]/= sqrt(2.0); + + Mat3MulMat3(tmat,persmat,bmat); + Mat3MulMat3(cmat,scalemat2,tmat); + Mat3MulMat3(tmat,persinv,cmat); + Mat3MulMat3(scalemat2,imat,tmat); + + ok= 1; + + for(a=0;a<7;a++) { + if(mode==0 || mode==2) ok= extrudeflagNurb(editnurb, 1); + else adduplicateflagNurb(scene, 1); + if(ok==0) { + error("Can't spin"); + break; + } + rotateflagNurb(editnurb, 1,cent,rotmat); + + if(mode==0 || mode==2) { + if( (a & 1)==0 ) { + rotateflagNurb(editnurb, 1,cent,scalemat1); + weightflagNurb(editnurb, 1, 0.25*sqrt(2.0), 1); + } + else { + rotateflagNurb(editnurb, 1,cent,scalemat2); + weightflagNurb(editnurb, 1, 4.0/sqrt(2.0), 1); + } + } + if(dvec) { + Mat3MulVecfl(bmat,dvec); + translateflagNurb(editnurb, 1,dvec); + } + } + + if(ok) { + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + nu->orderv= 4; + nu->flagv |= CU_CYCLIC; + makeknots(nu, 2, nu->flagv>>1); + } + } + } + +} + +/* external one, for undo */ +void spinNurb(Scene *scene, float *dvec, short mode) +{ + Object *obedit= scene->obedit; // XXX use context + + spin_nurb(scene, obedit, dvec, mode); + BIF_undo_push("Spin"); +} + +void addvert_Nurb(Scene *scene, int mode) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu; + BezTriple *bezt, *newbezt = NULL; + BPoint *bp, *newbp = NULL; + float *curs, mat[3][3],imat[3][3], temp[3]; + + if(obedit==0 || v3d == 0) return; + if( (v3d->lay & obedit->lay)==0 ) return; + + Mat3CpyMat4(mat, obedit->obmat); + Mat3Inv(imat,mat); + + findselectedNurbvert(editnurb, &nu, &bezt, &bp); + if(bezt==0 && bp==0) return; + + if((nu->type & 7)==CU_BEZIER) { + /* which bezpoint? */ + if(bezt== nu->bezt) { /* first */ + BEZ_DESEL(bezt); + newbezt = + (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); + memcpy(newbezt+1, bezt, nu->pntsu*sizeof(BezTriple)); + *newbezt= *bezt; + BEZ_SEL(newbezt); + newbezt->h2= newbezt->h1; + VECCOPY(temp, bezt->vec[1]); + MEM_freeN(nu->bezt); + nu->bezt= newbezt; + bezt= newbezt+1; + } + else if(bezt== (nu->bezt+nu->pntsu-1)) { /* last */ + BEZ_DESEL(bezt); + newbezt = + (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); + memcpy(newbezt, nu->bezt, nu->pntsu*sizeof(BezTriple)); + *(newbezt+nu->pntsu)= *bezt; + VECCOPY(temp, bezt->vec[1]); + MEM_freeN(nu->bezt); + nu->bezt= newbezt; + newbezt+= nu->pntsu; + BEZ_SEL(newbezt); + newbezt->h2= newbezt->h1; + bezt= nu->bezt+nu->pntsu-1; + } + else bezt= 0; + + if(bezt) { + nu->pntsu++; + + if(mode=='e') { + VECCOPY(newbezt->vec[0], bezt->vec[0]); + VECCOPY(newbezt->vec[1], bezt->vec[1]); + VECCOPY(newbezt->vec[2], bezt->vec[2]); + } + else { + curs= give_cursor(scene, v3d); + + VECCOPY(newbezt->vec[1], curs); + VecSubf(newbezt->vec[1],newbezt->vec[1], obedit->obmat[3]); + Mat3MulVecfl(imat,newbezt->vec[1]); + VecSubf(temp, newbezt->vec[1],temp); + VecAddf(newbezt->vec[0], bezt->vec[0],temp); + VecAddf(newbezt->vec[2], bezt->vec[2],temp); + calchandlesNurb(nu); + } + } + } + else if(nu->pntsv==1) { + /* which b-point? */ + if(bp== nu->bp) { /* first */ + bp->f1= 0; + newbp = + (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb3"); + memcpy(newbp+1, bp, nu->pntsu*sizeof(BPoint)); + *newbp= *bp; + newbp->f1= 1; + MEM_freeN(nu->bp); + nu->bp= newbp; + bp= newbp + 1; + } + else if(bp== (nu->bp+nu->pntsu-1)) { /* last */ + bp->f1= 0; + newbp = + (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb4"); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint)); + *(newbp+nu->pntsu)= *bp; + MEM_freeN(nu->bp); + nu->bp= newbp; + newbp+= nu->pntsu; + newbp->f1= 1; + bp= newbp - 1; + } + else bp= 0; + + if(bp) { + nu->pntsu++; + + makeknots(nu, 1, nu->flagu>>1); + + if(mode=='e') { + VECCOPY(newbp->vec, bp->vec); + } + else { + curs= give_cursor(scene, v3d); + + VECCOPY(newbp->vec, curs); + VecSubf(newbp->vec, newbp->vec, obedit->obmat[3]); + Mat3MulVecfl(imat,newbp->vec); + newbp->vec[3]= 1.0; + } + } + } + +// XXX retopo_do_all(); + + test2DNurb(nu); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + if(mode=='e') { +// XXX BIF_TransformSetUndo("Extrude"); +// initTransform(TFM_TRANSLATION, CTX_NO_PET); +// Transform(); + } +// else while(get_mbut()&R_MOUSE) BIF_wait_for_statechange(); + + if(mode!='e') { + /* dependencies with other objects, should become event */ + BIF_undo_push("Add vertex"); + + } +} + +void extrude_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + int ok= 0; + + if(obedit && obedit->type==OB_SURF) { + + /* first test: curve? */ + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->pntsv==1 && isNurbsel_count(nu)==1 ) break; + } + if(nu) { + addvert_Nurb(scene, 'e'); + } else { + ok= extrudeflagNurb(editnurb, 1); /* '1'= flag */ + + if(ok) { + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); +// XXX BIF_TransformSetUndo("Extrude"); +// initTransform(TFM_TRANSLATION, CTX_NO_PET); +// Transform(); + } + } + } +} + + + +void makecyclicNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float *fp; + int a, b, cyclmode=0; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( nu->pntsu>1 || nu->pntsv>1) { + if( (nu->type & 7)==0 ) { + a= nu->pntsu; + bp= nu->bp; + while(a--) { + if( bp->f1 & SELECT ) { + if(nu->flagu & CU_CYCLIC) nu->flagu &= ~CU_CYCLIC; + else nu->flagu |= CU_CYCLIC; + break; + } + bp++; + } + } + else if( (nu->type & 7)==CU_BEZIER ) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + if(nu->flagu & CU_CYCLIC) nu->flagu &= ~CU_CYCLIC; + else nu->flagu |= CU_CYCLIC; + break; + } + bezt++; + } + calchandlesNurb(nu); + } + else if(nu->pntsv==1 && (nu->type & 7)==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 ) { + if(nu->flagu & CU_CYCLIC) nu->flagu &= ~CU_CYCLIC; + else { + nu->flagu |= CU_CYCLIC; + nu->flagu &= ~2; /* endpoint flag, fixme */ + fp= MEM_mallocN(sizeof(float)*KNOTSU(nu), "makecyclicN"); + b= (nu->orderu+nu->pntsu); + memcpy(fp, nu->knotsu, sizeof(float)*b); + MEM_freeN(nu->knotsu); + nu->knotsu= fp; + + makeknots(nu, 1, 0); /* 1==u 0==uniform */ + + } + break; + } + bp++; + } + } + } + else if(nu->type==CU_NURBS) { + if(cyclmode==0) { + cyclmode= pupmenu("Toggle %t|cyclic U%x1|cyclic V%x2"); + if(cyclmode < 1) return; + } + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + + if( bp->f1 & SELECT) { + if(cyclmode==1 && nu->pntsu>1) { + if(nu->flagu & CU_CYCLIC) nu->flagu &= ~CU_CYCLIC; + else { + nu->flagu |= CU_CYCLIC; + if (check_valid_nurb_u(nu)) { + fp= MEM_mallocN(sizeof(float)*KNOTSU(nu), "makecyclicN"); + b= (nu->orderu+nu->pntsu); + if (nu->knotsu) { /* null if check_valid_nurb_u failed before but is valid now */ + memcpy(fp, nu->knotsu, sizeof(float)*b); + MEM_freeN(nu->knotsu); + } + nu->knotsu= fp; + + makeknots(nu, 1, 0); /* 1==u 0==uniform */ + } + } + } + if(cyclmode==2 && nu->pntsv>1) { + if(nu->flagv & 1) nu->flagv--; + else { + nu->flagv++; + if (check_valid_nurb_v(nu)) { + fp= MEM_mallocN(sizeof(float)*KNOTSV(nu), "makecyclicN"); + b= (nu->orderv+nu->pntsv); + if (nu->knotsv) { /* null if check_valid_nurb_v failed before but is valid now */ + memcpy(fp, nu->knotsv, sizeof(float)*b); + MEM_freeN(nu->knotsv); + } + nu->knotsv= fp; + + makeknots(nu, 2, 0); /* 2==v 0==uniform */ + } + } + } + break; + } + bp++; + } + + } + } + } + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Cyclic"); +} + +void selectconnected_nurb(Scene *scene) +{ + ViewContext vc; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + short mval[2], shift= 0; // XXX + + + findnearestNurbvert(&vc, 1, mval, &nu, &bezt, &bp); + if(bezt) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(shift) 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(shift) select_bpoint(bp, DESELECT, 1, VISIBLE); + else select_bpoint(bp, SELECT, 1, VISIBLE); + bp++; + } + } + + BIF_undo_push("Select connected"); + +} + +void selectrow_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Curve *cu= obedit->data; + static BPoint *last=0; + static int direction=0; + Nurb *nu; + BPoint *bp; + int u = 0, v = 0, a, b, ok=0; + + if(editnurb->first==0) return; + if(obedit==NULL || obedit->type!=OB_SURF) return; + if(cu->lastselbp==NULL) return; + + /* find the correct nurb and toggle with u of v */ + for(nu= editnurb->first; nu; nu= nu->next) { + bp= nu->bp; + for(v=0; v<nu->pntsv; v++) { + for(u=0; u<nu->pntsu; u++, bp++) { + if(bp==cu->lastselbp) { + if(bp->f1 & SELECT) { + ok= 1; + break; + } + } + } + if(ok) break; + } + if(ok) { + if(last==cu->lastselbp) { + direction= 1-direction; + setflagsNurb(editnurb, 0); + } + last= cu->lastselbp; + + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp++) { + if(direction) { + if(a==v) select_bpoint(bp, SELECT, 1, VISIBLE); + } + else { + if(b==u) select_bpoint(bp, SELECT, 1, VISIBLE); + } + } + } + return; + } + } + BIF_undo_push("Select Row"); + +} + +void select_next_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + + if(obedit==0) return; + + select_adjacent_cp(editnurb, 1, 0, SELECT); + + BIF_undo_push("Select Next"); +} + +void select_prev_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + + if(obedit==NULL) return; + + select_adjacent_cp(editnurb, -1, 0, SELECT); + + BIF_undo_push("Select Previous"); +} + +void select_more_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp, *tempbp; + int a; + short sel= 0; + short *selbpoints; + + if(obedit==0) return; + + /* note that NURBS surface is a special case because we mimic */ + /* the behaviour 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); + } + + BIF_undo_push("Select More"); +} + +/* basic method: deselect if control point doesn't have all neighbours selected */ +void select_less_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + short sel= 0, lastsel= 0; + short *selbpoints; + + if(obedit==0) return; + + 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 neighbours 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 & 7)==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 neighbours 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++; + } + } + } + } + + BIF_undo_push("Select Less"); +} + +/* this function could be moved elsewhere as it can be reused in other parts of the source needing randomized list */ +/* returns list containing -1 in indices that have been left out of the list. otherwise index contains reference */ +/* to next index. basically *list contains a linked list */ +static void generate_pickable_list(int *list, int size, int pickamount) +{ + int i, j, removable; + + BLI_srand( BLI_rand() ); /* random seed */ + + /* generate list in form 0->1, 1->2, 2->3, ... i-2->i-1, i->0 */ + for(i=0; i<size; i++) { + if(i == size-1) list[i]= 0; + else list[i]= i+1; + } + + for(i=0; i<size-pickamount; i++) { + removable= floor(BLI_frand()*(size-1)+0.5); /* with rounding. frand returns [0,1] */ + + /* seek proper item as the one randomly selected might not be appropriate */ + for(j=0; j<size; j++, removable++) { + if(list[removable] != -1) break; + if(removable == size-1) removable= -1; + } + + /* pick unwanted item out of the list */ + list[list[removable]]= -1; /* mark former last as invalid */ + + if(list[removable] == size-1) list[removable]= 0; + else list[removable]= list[removable]+1; + } +} + +void select_random_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + static short randfac= 50; + int amounttoselect, amountofcps, a, i, k= 0; + int *itemstobeselected; + + if(!obedit) return; + + if(!button(&randfac,0, 100,"Percentage:")) return; + + if(randfac == 0) return; + + amountofcps= count_curveverts_without_handles(editnurb); + itemstobeselected= MEM_callocN(sizeof(int) * amountofcps, "selectitems"); + amounttoselect= floor(randfac * amountofcps / 100 + 0.5); + generate_pickable_list(itemstobeselected, amountofcps, amounttoselect); + + /* select elements */ + for(i=1, nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(itemstobeselected[k] != -1) select_beztriple(bezt, SELECT, 1, VISIBLE); + k++; + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(itemstobeselected[k] != -1) select_bpoint(bp, SELECT, 1, VISIBLE); + k++; + bp++; + } + } + } + + MEM_freeN(itemstobeselected); + + BIF_undo_push("Select Random"); +} + +void select_every_nth_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + ListBase *editnurb= curve_get_editcurve(obedit); + static short nfac= 2; + + if(!obedit) return; + + if(!button(&nfac, 2, 25,"N:")) return; + + select_adjacent_cp(editnurb, nfac, 1, SELECT); + select_adjacent_cp(editnurb, -nfac, 1, SELECT); + + BIF_undo_push("Select Every Nth"); +} + +void adduplicate_nurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX use context + View3D *v3d= NULL; // XXX + + if(v3d==0 || (v3d->lay & obedit->lay)==0 ) return; + + adduplicateflagNurb(scene, 1); + +// XXX BIF_TransformSetUndo("Add Duplicate"); +// initTransform(TFM_TRANSLATION, CTX_NO_PET); +// Transform(); +} + +void delNurb(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu, *next, *nu1; + BezTriple *bezt, *bezt1, *bezt2; + BPoint *bp, *bp1, *bp2; + int a; + short event, cut = 0; + + if(obedit==0 ) return; + if(v3d==0 || (v3d->lay & obedit->lay)==0 ) return; + + if(obedit->type==OB_SURF) event= pupmenu("Erase %t|Selected%x0|All%x2"); + else event= pupmenu("Erase %t|Selected%x0|Segment%x1|All%x2"); + + if(event== -1) return; + + if(obedit->type==OB_SURF) { + if(event==0) deleteflagNurb(scene, 1); + else freeNurblist(editnurb); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Delete"); + + return; + } + + if(event==0) { + /* first loop, can we remove entire pieces? */ + nu= editnurb->first; + while(nu) { + next= nu->next; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + a= nu->pntsu; + if(a) { + while(a) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ); + else break; + a--; + bezt++; + } + if(a==0) { + BLI_remlink(editnurb, nu); + freeNurb(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) { + BLI_remlink(editnurb, nu); + freeNurb(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 ((nu!= NULL) && ((nu->type & 7)==CU_NURBS)) { + clamp_nurb_order_u(nu); + } + */ + nu= next; + } + /* 2nd loop, delete small pieces: just for curves */ + nu= editnurb->first; + while(nu) { + next= nu->next; + event= 0; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + for(a=0;a<nu->pntsu;a++) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + memmove(bezt, bezt+1, (nu->pntsu-a-1)*sizeof(BezTriple)); + nu->pntsu--; + a--; + event= 1; + } + else bezt++; + } + if(event) { + bezt1 = + (BezTriple*)MEM_mallocN((nu->pntsu) * sizeof(BezTriple), "delNurb"); + memcpy(bezt1, nu->bezt, (nu->pntsu)*sizeof(BezTriple) ); + MEM_freeN(nu->bezt); + nu->bezt= bezt1; + calchandlesNurb(nu); + } + } + else if(nu->pntsv==1) { + bp= nu->bp; + + for(a=0;a<nu->pntsu;a++) { + if( bp->f1 & SELECT ) { + memmove(bp, bp+1, (nu->pntsu-a-1)*sizeof(BPoint)); + nu->pntsu--; + a--; + event= 1; + } + else { + bp++; + } + } + if(event) { + bp1 = (BPoint*)MEM_mallocN(nu->pntsu * sizeof(BPoint), "delNurb2"); + memcpy(bp1, nu->bp, (nu->pntsu)*sizeof(BPoint) ); + 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 ((nu->type & 7)==CU_NURBS) { + clamp_nurb_order_u(nu); + }*/ + } + makeknots(nu, 1, nu->flagu>>1); + } + nu= next; + } + } + else if(event==1) { /* erase segment */ + /* find the 2 selected points */ + bezt1= bezt2= 0; + bp1= bp2= 0; + nu= editnurb->first; + nu1= 0; + while(nu) { + next= nu->next; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + for(a=0; a<nu->pntsu-1; a++) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + bezt1= bezt; + bezt2= bezt+1; + if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) ; + else { /* maybe do not make cyclic */ + if(a==0 && (nu->flagu & CU_CYCLIC) ) { + bezt2= bezt+(nu->pntsu-1); + if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) { + nu->flagu &= ~CU_CYCLIC; + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Delete"); + } + } + return; + } + cut= a; + nu1= nu; + break; + } + bezt++; + } + } + else if(nu->pntsv==1) { + bp= nu->bp; + for(a=0; a<nu->pntsu-1; a++) { + if( bp->f1 & SELECT ) { + bp1= bp; + bp2= bp+1; + if( bp2->f1 & 1 ) ; + else { /* maybe do not make cyclic */ + if(a==0 && (nu->flagu & CU_CYCLIC) ) { + bp2= bp+(nu->pntsu-1); + if( bp2->f1 & SELECT ) { + nu->flagu &= ~CU_CYCLIC; + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Delete"); + } + } + return; + } + cut= a; + nu1= nu; + break; + } + bp++; + } + } + if(nu1) break; + + nu= nu->next; + } + if(nu1) { + if(bezt1) { + if(nu1->pntsu==2) { /* remove completely */ + BLI_remlink(editnurb, nu); + freeNurb(nu); nu = NULL; + } + else if(nu1->flagu & CU_CYCLIC) { /* cyclic */ + bezt = + (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb1"); + memcpy(bezt, nu1->bezt,(cut+1)*sizeof(BezTriple)); + a= nu1->pntsu-cut-1; + memcpy(nu1->bezt, bezt2, a*sizeof(BezTriple)); + memcpy(nu1->bezt+a, bezt, (cut+1)*sizeof(BezTriple)); + nu1->flagu &= ~CU_CYCLIC; + MEM_freeN(bezt); + calchandlesNurb(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(editnurb, nu); + nu->bezt = + (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb3"); + memcpy(nu->bezt, nu1->bezt,(cut+1)*sizeof(BezTriple)); + a= nu1->pntsu-cut-1; + + bezt = + (BezTriple*)MEM_mallocN(a * sizeof(BezTriple), "delNurb4"); + memcpy(bezt, nu1->bezt+cut+1,a*sizeof(BezTriple)); + MEM_freeN(nu1->bezt); + nu1->bezt= bezt; + nu1->pntsu= a; + nu->pntsu= cut+1; + + + calchandlesNurb(nu); + calchandlesNurb(nu1); + } + } + else if(bp1) { + if(nu1->pntsu==2) { /* remove completely */ + BLI_remlink(editnurb, nu); + freeNurb(nu); nu= NULL; + } + else if(nu1->flagu & CU_CYCLIC) { /* cyclic */ + bp = + (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb5"); + memcpy(bp, nu1->bp,(cut+1)*sizeof(BPoint)); + a= nu1->pntsu-cut-1; + memcpy(nu1->bp, bp2, a*sizeof(BPoint)); + memcpy(nu1->bp+a, bp, (cut+1)*sizeof(BPoint)); + nu1->flagu &= ~CU_CYCLIC; + MEM_freeN(bp); + } + else { /* add new curve */ + nu = (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb6"); + memcpy(nu, nu1, sizeof(Nurb)); + BLI_addtail(editnurb, nu); + nu->bp = + (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb7"); + memcpy(nu->bp, nu1->bp,(cut+1)*sizeof(BPoint)); + a= nu1->pntsu-cut-1; + bp = + (BPoint*)MEM_mallocN(a * sizeof(BPoint), "delNurb8"); + memcpy(bp, nu1->bp+cut+1,a*sizeof(BPoint)); + MEM_freeN(nu1->bp); + nu1->bp= bp; + nu1->pntsu= a; + nu->pntsu= cut+1; + } + } + } + } + else if(event==2) { + freeNurblist(editnurb); + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + BIF_undo_push("Delete"); + +} + +void nurb_set_smooth(Scene *scene, short event) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + + if(obedit==0) return; + + if(obedit->type != OB_CURVE) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + if(event==1) nu->flag |= CU_SMOOTH; + else if(event==0) nu->flag &= ~CU_SMOOTH; + } + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + if(event==1) BIF_undo_push("Set Smooth"); + else if(event==0) BIF_undo_push("Set Solid"); +} + +int join_curve(Scene *scene, int type) +{ + View3D *v3d= NULL; // XXX + Base *base, *nextb; + Object *ob; + Curve *cu; + Nurb *nu, *newnu; + BezTriple *bezt; + BPoint *bp; + ListBase tempbase; + float imat[4][4], cmat[4][4]; + int a; + + ob= OBACT; + if (object_data_is_libdata(ob)) { + error_libdata(); + return 0; + } + + if(!v3d || ob->type!=type) return 0; + if(ob->lay & v3d->lay); else return 0; + tempbase.first= tempbase.last= 0; + + /* trasnform all selected curves inverse in obact */ + Mat4Invert(imat, ob->obmat); + + base= FIRSTBASE; + while(base) { + nextb= base->next; + if (TESTBASE(v3d, base)) { + if(base->object->type==type) { + if(base->object != ob) { + + cu= base->object->data; + + if(cu->nurb.first) { + /* watch it: switch order here really goes wrong */ + Mat4MulMat4(cmat, base->object->obmat, imat); + + nu= cu->nurb.first; + while(nu) { + newnu= duplicateNurb(nu); + BLI_addtail(&tempbase, newnu); + + if( (bezt= newnu->bezt) ) { + a= newnu->pntsu; + while(a--) { + Mat4MulVecfl(cmat, bezt->vec[0]); + Mat4MulVecfl(cmat, bezt->vec[1]); + Mat4MulVecfl(cmat, bezt->vec[2]); + bezt++; + } + } + if( (bp= newnu->bp) ) { + a= newnu->pntsu*nu->pntsv; + while(a--) { + Mat4MulVecfl(cmat, bp->vec); + bp++; + } + } + nu= nu->next; + } + } + + ED_base_object_free_and_unlink(scene, base); + } + } + } + base= nextb; + } + + cu= ob->data; + addlisttolist(&cu->nurb, &tempbase); + + DAG_scene_sort(scene); // because we removed object(s), call before editmode! + + // XXX Context + ED_object_enter_editmode(NULL, EM_WAITCURSOR); + ED_object_exit_editmode(NULL, EM_FREEDATA|EM_WAITCURSOR); + + BIF_undo_push("Join"); + return 1; +} + + + +Nurb *addNurbprim(Scene *scene, int type, int stype, int newname) +/* type: &8= 2D; 0=poly,1 bez, 4 nurb + * stype: 0: 2/4 points curve + * 1: 8 points circle + * 2: 4x4 patch Nurb + * 3: tube 4:sphere 5:donut + * 6: 5 points, 5th order straight line (for anim path) + */ +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + static int xzproj= 0; + Nurb *nu = NULL; + BezTriple *bezt; + BPoint *bp; + float *curs, cent[3],vec[3],imat[3][3],mat[3][3]; + float fac,cmat[3][3], grid; + int a, b; + + if (v3d) grid = v3d->grid; + else grid = 1.0; + + /* imat and center and size */ + if(obedit) { + + Mat3CpyMat4(mat, obedit->obmat); + curs= give_cursor(scene, v3d); + VECCOPY(cent, curs); + cent[0]-= obedit->obmat[3][0]; + cent[1]-= obedit->obmat[3][1]; + cent[2]-= obedit->obmat[3][2]; + + if (v3d) { + if ( !(newname) || U.flag & USER_ADD_VIEWALIGNED) Mat3CpyMat4(imat, v3d->viewmat); + else Mat3One(imat); + Mat3MulVecfl(imat, cent); + Mat3MulMat3(cmat, imat, mat); + Mat3Inv(imat, cmat); + } + setflagsNurb(editnurb, 0); + } + else { + Mat3One(imat); + cent[0]= cent[1]= cent[2]= 0.0; + } + + if (ELEM5(stype, 0, 1, 2, 4, 6)) { + nu = (Nurb*)MEM_callocN(sizeof(Nurb), "addNurbprim"); + nu->type= type; + nu->resolu= 4; + nu->resolv= 4; + } + + switch(stype) { + case 0: /* curve */ + nu->resolu= 12; /* set as 4 above */ + if(newname) { + rename_id((ID *)obedit, "Curve"); + rename_id((ID *)obedit->data, "Curve"); + } + if((type & 7)==CU_BEZIER) { + 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; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->vec[1][0]+= -grid; + bezt->vec[0][0]+= -1.5*grid; + bezt->vec[0][1]+= -0.5*grid; + bezt->vec[2][0]+= -0.5*grid; + bezt->vec[2][1]+= 0.5*grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]); + + bezt++; + bezt->h1= bezt->h2= HD_ALIGN; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->radius = bezt->weight = 1.0; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->vec[1][0]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]); + + calchandlesNurb(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++) { + VECCOPY(bp->vec, cent); + bp->vec[3]= 1.0; + bp->f1= SELECT; + bp->radius = bp->weight = 1.0; + } + + bp= nu->bp; + bp->vec[0]+= -1.5*grid; + bp++; + bp->vec[0]+= -grid; + bp->vec[1]+= grid; + bp++; + bp->vec[0]+= grid; + bp->vec[1]+= grid; + bp++; + bp->vec[0]+= 1.5*grid; + + bp= nu->bp; + for(a=0;a<4;a++, bp++) Mat3MulVecfl(imat,bp->vec); + + if((type & 7)==4) { + nu->knotsu= 0; /* makeknots allocates */ + makeknots(nu, 1, nu->flagu>>1); + } + + } + break; + case 6: /* 5 point path */ + nu->pntsu= 5; + nu->pntsv= 1; + nu->orderu= 5; + nu->flagu= 2; /* endpoint */ + nu->resolu= 8; + nu->bp= callocstructN(BPoint, 5, "addNurbprim3"); + + bp= nu->bp; + for(a=0;a<5;a++, bp++) { + VECCOPY(bp->vec, cent); + bp->vec[3]= 1.0; + bp->f1= SELECT; + bp->radius = bp->weight = 1.0; + } + + bp= nu->bp; + bp->vec[0]+= -2.0*grid; + bp++; + bp->vec[0]+= -grid; + bp++; bp++; + bp->vec[0]+= grid; + bp++; + bp->vec[0]+= 2.0*grid; + + bp= nu->bp; + for(a=0;a<5;a++, bp++) Mat3MulVecfl(imat,bp->vec); + + if((type & 7)==4) { + nu->knotsu= 0; /* makeknots allocates */ + makeknots(nu, 1, nu->flagu>>1); + } + + break; + case 1: /* circle */ + nu->resolu= 12; /* set as 4 above */ + if(newname) { + rename_id((ID *)obedit, "CurveCircle"); + rename_id((ID *)obedit->data, "CurveCircle"); + } + if((type & 7)==CU_BEZIER) { + nu->pntsu= 4; + nu->bezt= callocstructN(BezTriple, 4, "addNurbprim1"); + nu->flagu= CU_CYCLIC; + bezt= nu->bezt; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][0]+= -grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][1]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][0]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][1]+= -grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + calchandlesNurb(nu); + } + else if( (type & 7)==CU_NURBS ) { /* nurb */ + nu->pntsu= 8; + nu->pntsv= 1; + nu->orderu= 4; + nu->bp= callocstructN(BPoint, 8, "addNurbprim6"); + nu->flagu= CU_CYCLIC; + bp= nu->bp; + + for(a=0; a<8; a++) { + bp->f1= SELECT; + VECCOPY(bp->vec, cent); + + if(xzproj==0) { + bp->vec[0]+= nurbcircle[a][0]*grid; + bp->vec[1]+= nurbcircle[a][1]*grid; + } + else { + bp->vec[0]+= 0.25*nurbcircle[a][0]*grid-.75*grid; + bp->vec[2]+= 0.25*nurbcircle[a][1]*grid; + } + if(a & 1) bp->vec[3]= 0.25*sqrt(2.0); + else bp->vec[3]= 1.0; + Mat3MulVecfl(imat,bp->vec); + bp->radius = bp->weight = 1.0; + + bp++; + } + + makeknots(nu, 1, nu->flagu>>1); + } + break; + case 2: /* 4x4 patch */ + if( (type & 7)==CU_NURBS ) { /* nurb */ + if(newname) { + rename_id((ID *)obedit, "Surf"); + rename_id((ID *)obedit->data, "Surf"); + } + + 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++) { + VECCOPY(bp->vec, cent); + bp->f1= SELECT; + fac= (float)a -1.5; + bp->vec[0]+= fac*grid; + fac= (float)b -1.5; + bp->vec[1]+= fac*grid; + if(a==1 || a==2) if(b==1 || b==2) { + bp->vec[2]+= grid; + } + Mat3MulVecfl(imat,bp->vec); + bp->vec[3]= 1.0; + bp++; + } + } + + makeknots(nu, 1, nu->flagu>>1); + makeknots(nu, 2, nu->flagv>>1); + } + break; + case 3: /* tube */ + if( (type & 7)==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfTube"); + rename_id((ID *)obedit->data, "SurfTube"); + } + + nu= addNurbprim(scene, 4, 1, newname); /* circle */ + nu->resolu= 4; + nu->flag= CU_SMOOTH; + BLI_addtail(editnurb, nu); /* temporal for extrude and translate */ + vec[0]=vec[1]= 0.0; + vec[2]= -grid; + Mat3MulVecfl(imat, vec); + translateflagNurb(editnurb, 1, vec); + extrudeflagNurb(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 4: /* sphere */ + if( (type & 7)==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfSphere"); + rename_id((ID *)obedit->data, "SurfSphere"); + } + + nu->pntsu= 5; + nu->pntsv= 1; + nu->orderu= 3; + nu->resolu= 4; + nu->resolv= 4; + 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; + VECCOPY(bp->vec, cent); + bp->vec[0]+= nurbcircle[a][0]*grid; + bp->vec[2]+= nurbcircle[a][1]*grid; + if(a & 1) bp->vec[3]= 0.5*sqrt(2.0); + else bp->vec[3]= 1.0; + Mat3MulVecfl(imat,bp->vec); + bp++; + } + nu->flagu= 4; + makeknots(nu, 1, nu->flagu>>1); + + BLI_addtail(editnurb, nu); /* temporal for spin */ + if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0) + spin_nurb(scene, obedit, 0, 2); + else + spin_nurb(scene, obedit, 0, 0); + + makeknots(nu, 2, nu->flagv>>1); + + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a-- >0) { + bp->f1 |= SELECT; + bp++; + } + BLI_remlink(editnurb, nu); + } + break; + case 5: /* donut */ + if( (type & 7)==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfDonut"); + rename_id((ID *)obedit->data, "SurfDonut"); + } + + xzproj= 1; + nu= addNurbprim(scene, 4, 1, newname); /* circle */ + xzproj= 0; + nu->resolu= 4; + nu->resolv= 4; + nu->flag= CU_SMOOTH; + BLI_addtail(editnurb, nu); /* temporal for extrude and translate */ + if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0) + spin_nurb(scene, obedit, 0, 2); + else + spin_nurb(scene, obedit, 0, 0); + + BLI_remlink(editnurb, nu); + + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a-- >0) { + bp->f1 |= SELECT; + bp++; + } + + } + break; + } + + /* always do: */ + nu->flag= CU_SMOOTH; + + test2DNurb(nu); + + return nu; +} + +void default_curve_ipo(Scene *scene, Curve *cu) +{ + IpoCurve *icu; + BezTriple *bezt; + + if(cu->ipo) return; + + cu->ipo= add_ipo(scene, "CurveIpo", ID_CU); + + icu= MEM_callocN(sizeof(IpoCurve), "ipocurve"); + + icu->blocktype= ID_CU; + icu->adrcode= CU_SPEED; + icu->flag= IPO_VISIBLE|IPO_SELECT|IPO_AUTO_HORIZ; + set_icu_vars(icu); + + BLI_addtail( &(cu->ipo->curve), icu); + + icu->bezt= bezt= MEM_callocN(2*sizeof(BezTriple), "defaultipo"); + icu->totvert= 2; + + bezt->hide= IPO_BEZ; + bezt->f1=bezt->f2= bezt->f3= SELECT; + bezt->h1= bezt->h2= HD_AUTO; + bezt++; + bezt->vec[1][0]= 100.0; + bezt->vec[1][1]= 1.0; + bezt->hide= IPO_BEZ; + bezt->f1=bezt->f2= bezt->f3= SELECT; + bezt->h1= bezt->h2= HD_AUTO; + + calchandles_ipocurve(icu); +} + +void add_primitiveCurve(Scene *scene, int stype) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu; + Curve *cu; + int type, newname= 0; + + if(v3d==0) return; + if(scene->id.lib) return; + + if(stype>=10 && stype<20) type= CU_2D+1; + else if(stype>=20 && stype<30) type= CU_2D+2; + else if(stype>=30 && stype<40) type= CU_2D+3; + else if(stype>=40 && stype<50) { + if(stype==46) type= 4; + else type= CU_2D+4; + } + else type= CU_2D; + +// XXX check_editmode(OB_CURVE); + + /* if no obedit: new object and enter editmode */ + if(obedit==NULL) { +// XXX add_object_draw(OB_CURVE); + ED_object_base_init_from_view(scene, v3d, BASACT); + obedit= BASACT->object; + + where_is_object(scene, obedit); + + make_editNurb(obedit); + newname= 1; + + cu= obedit->data; + if(stype==46) { + cu->flag |= (CU_3D+CU_PATH); + + default_curve_ipo(scene, cu); + } + } + else { + cu= obedit->data; + } + + if(cu->flag & CU_3D) type &= ~CU_2D; + + stype= (stype % 10); + + nu= addNurbprim(scene, type, stype, newname); /* 2D */ + + BLI_addtail(editnurb, nu); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + /* if a new object was created, it stores it in Curve, for reload original data and undo */ + if ( !(newname) || U.flag & USER_ADD_EDITMODE) { + if(newname) load_editNurb(obedit); + } else { + // XXX + ED_object_exit_editmode(NULL, EM_FREEDATA|EM_WAITCURSOR); + } + + BIF_undo_push("Add Curve"); +} + +void add_primitiveNurb(Scene *scene, int type) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= NULL; // XXX + Nurb *nu; + int newname= 0; + + if(scene->id.lib) return; + +// XXX check_editmode(OB_SURF); + + /* if no obedit: new object and enter editmode */ + if(obedit==0) { +// XXX add_object_draw(OB_SURF); + ED_object_base_init_from_view(scene, v3d, BASACT); + obedit= BASACT->object; + + where_is_object(scene, obedit); + + make_editNurb(obedit); + newname= 1; + } + + nu= addNurbprim(scene, 4, type, newname); + BLI_addtail(editnurb,nu); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + /* if a new object was created, it stores it in Curve, for reload original data and undo */ + if ( !(newname) || U.flag & USER_ADD_EDITMODE) { + if(newname) load_editNurb(obedit); + } else { + // XXX + ED_object_exit_editmode(NULL, EM_FREEDATA|EM_WAITCURSOR); + } + + BIF_undo_push("Add Surface"); +} + + + +void clear_tilt(Scene *scene) +{ + Object *obedit= scene->obedit; // XXX + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + if(okee("Clear tilt")==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( nu->bezt ) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(BEZSELECTED_HIDDENHANDLES(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++; + } + } + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + BIF_undo_push("Clear tilt"); + +} + +int bezt_compare (const void *e1, const void *e2) +{ + BezTriple *b1 = *((BezTriple**)e1); + BezTriple *b2 = *((BezTriple**)e2); + + /* Check numerical values */ + float val = b1->vec[1][0] - b2->vec[1][0]; + + if (val<0) + return -1; + + if (val>0) + return 1; + + /* Check selected flags : Ensures that selected keys will be listed first */ + + if ((b1->f2 & SELECT) && !(b2->f2 & SELECT)) + return -1; + if (!(b1->f2 & SELECT) && (b2->f2 & SELECT)) + return 1; + + return 0; +} + + +/* **************** undo for curves ************** */ + +static void undoCurve_to_editCurve(void *lbu, void *lbe) +{ + ListBase *lb= lbu; + ListBase *editnurb= lbe; + Nurb *nu, *newnu; + + freeNurblist(editnurb); + + /* copy */ + for(nu= lb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(editnurb, newnu); + } +} + +static void *editCurve_to_undoCurve(void *lbe) +{ + ListBase *editnurb= lbe; + ListBase *lb; + Nurb *nu, *newnu; + + lb= MEM_callocN(sizeof(ListBase), "listbase undo"); + + /* copy */ + for(nu= editnurb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(lb, newnu); + } + return lb; +} + +static void free_undoCurve(void *lbv) +{ + ListBase *lb= lbv; + + freeNurblist(lb); + MEM_freeN(lb); +} + +static void *get_data(bContext *C) +{ + Object *obedit= CTX_data_edit_object(C); + return curve_get_editcurve(obedit); +} + +/* and this is all the undo system needs to know */ +void undo_push_curve(bContext *C, char *name) +{ + undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL); +} + + + +/***/ |