static void CVMouseMoveSpiroPoint(CharView *cv, PressedOn *p) { spiro_cp *active = cv->active_cp, *merge = p->spiro; SplineSet *activess = cv->active_spl; int active_index; if ( active==NULL ) return; if ( cv->info.x==active->x && cv->info.y==active->y ) return; if ( !cv->recentchange ) CVPreserveState(&cv->b); active->x = cv->info.x; active->y = cv->info.y; CVSetCharChanged(cv,true); active_index = active-activess->spiros; if ( active!=merge && merge!=NULL && p->spl!=NULL && SPIRO_SPL_OPEN(activess) && SPIRO_SPL_OPEN(p->spl) && (active_index==0 || active_index==activess->spiro_cnt-2) && ((merge-p->spl->spiros)==0 || (merge-p->spl->spiros)==p->spl->spiro_cnt-2) ) { SplinePoint *activesp = active_index==0 ? activess->first : activess->last; SplinePoint *mergesp = (merge-p->spl->spiros)==0 ? p->spl->first : p->spl->last; CVMergeSplineSets(cv,activesp,activess,mergesp,p->spl); } SSRegenerateFromSpiros(activess); SCUpdateAll(cv->b.sc); }
void CVAdjustPoint(CharView *cv, SplinePoint *sp) { if ( cv->info.x==sp->me.x && cv->info.y==sp->me.y ) return; sp->nextcp.x += (cv->info.x-sp->me.x); sp->nextcp.y += (cv->info.y-sp->me.y); sp->prevcp.x += (cv->info.x-sp->me.x); sp->prevcp.y += (cv->info.y-sp->me.y); sp->me.x = cv->info.x; sp->me.y = cv->info.y; AdjustControls(sp); CVSetCharChanged(cv,true); }
/* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "pfaeditui.h" #include <math.h> #if defined(KNIFE_CONTINUOUS) /* Use this code to do cuts as we move along. Probably a bad idea, let's wait till the end */ static void ProcessKnife(CharView *cv, PressedOn *p) { real dx, dy; SplinePoint *n; /* If we've already made a cut, don't make another cut too close to it */ /* ie. if the hand shakes over a cut let's not get about six tiny cut */ /* segments adjacent to one another */ if ( (dx=cv->info.x-cv->lastknife.x)<0 ) dx=-dx; if ( (dy=cv->info.y-cv->lastknife.y)<0 ) dy=-dy; if ( (dx+dy)/cv->scale <= 6 ) return; if ( p->sp==NULL && p->spline==NULL ) return; /* Nothing to cut */ if ( p->spline!=NULL ) p->sp = SplineBisect(p->spline,p->t); if ( p->spl==NULL ) /* Kanou says this can happen. It doesn't hurt to check for it */ return; if ( p->spl->first!=p->spl->last ) if ( p->sp==p->spl->first || p->sp==p->spl->last ) return; /* Already cut here */ n = chunkalloc(sizeof(SplinePoint)); p->sp->pointtype = pt_corner; *n = *p->sp; n->hintmask = NULL; p->sp->next = NULL; n->prev = NULL; n->next->from = n; if ( p->spl->first==p->spl->last ) { p->spl->first = n; p->spl->last = p->sp; } else { SplinePointList *nspl = chunkalloc(sizeof(SplinePointList)); nspl->next = p->spl->next; p->spl->next = nspl; nspl->first = n; nspl->last = p->spl->last; p->spl->last = p->sp; } cv->lastknife.x = cv->info.x; cv->lastknife.y = cv->info.y; CVSetCharChanged(cv,true); SCUpdateAll(cv->b.sc); }
/* When the user tries to add a point (by doing a mouse down with a point tool selected) there are several cases to be looked at: If there is a single point selected and it is at the begining/end of an open spline set if we clicked on another point which is the begining/end of an open splineset draw a spline connecting the two spline sets and merge them (or if it's the same spline set, then close it) else create a new point where we clicked draw a spline between the selected point and the new one deselect the old point select the new one endif else if they clicked on a spline split the spline into two bits at the point where they clicked else create a new point where they clicked put it on a new splineset select it endif and, if the old point is a tangent, we may need to adjust its control pt With the introduction of spiro mode (Raph Levien's clothoid splines) we've got to worry about all the above cases for spiro points too. */ void CVMouseDownPoint(CharView *cv, GEvent *event) { SplineSet *sel, *ss; SplinePoint *sp, *base = NULL; SplineChar *sc = cv->b.sc; enum pointtype ptype = (cv->active_tool==cvt_curve?pt_curve: cv->active_tool==cvt_hvcurve?pt_hvcurve: cv->active_tool==cvt_corner?pt_corner: cv->active_tool==cvt_tangent?pt_tangent: /*cv->active_tool==cvt_pen?*/pt_corner); int order2 = cv->b.layerheads[cv->b.drawmode]->order2; int order2_style = (order2 && !(event->u.mouse.state&ksm_meta)) || (!order2 && (event->u.mouse.state&ksm_meta)); cv->active_spl = NULL; cv->active_sp = NULL; if ( cv->b.sc->inspiro && hasspiro()) { CVMouseDownSpiroPoint(cv, event); return; } sel = CVAnySelPointList(cv); if ( sel!=NULL ) { if ( sel->first->selected ) base = sel->first; else base = sel->last; if ( base==cv->p.sp ) return; /* We clicked on the active point, that's a no-op */ } CVPreserveState(&cv->b); CVClearSel(cv); if ( sel!=NULL ) { sp = cv->p.sp; cv->lastselpt = base; ss = sel; if ( base->next!=NULL ) SplineSetReverse(sel); if ( base->next!=NULL ) IError("Base point not at end of splineset in CVMouseDownPoint"); if ( sp==NULL || (sp->next!=NULL && sp->prev!=NULL) || sp==base ) { /* Add a new point */ SplineSetSpirosClear(sel); sp = SplinePointCreate( cv->p.cx, cv->p.cy ); sp->noprevcp = sp->nonextcp = 1; sp->nextcpdef = sp->prevcpdef = 1; sp->pointtype = ptype; sp->selected = true; if ( !base->nonextcp && order2_style && cv->active_tool==cvt_pen ) { sp->prevcp = base->nextcp; sp->noprevcp = false; sp->me.x = ( sp->prevcp.x + sp->nextcp.x )/2; sp->me.y = ( sp->prevcp.y + sp->nextcp.y )/2; sp->nonextcp = false; sp->pointtype = pt_curve; } else if ( order2 && !base->nonextcp ) { sp->prevcp = base->nextcp; sp->noprevcp = false; if ( cv->active_tool==cvt_pen ) { sp->nextcp.x = sp->me.x - (sp->prevcp.x-sp->me.x); sp->nextcp.y = sp->me.y - (sp->prevcp.y-sp->me.y); sp->nonextcp = false; sp->pointtype = pt_curve; } } if ( base->nonextcp ) base->nextcpdef = true; SplineMake(base,sp,order2); if ( cv->active_tool!=cvt_pen ) { SplineCharDefaultNextCP(base); SplineCharDefaultPrevCP(sp); } ss->last = sp; } else if ( cv->p.spl==sel ) { /* Close the current spline set */ SplineSetSpirosClear(sel); cv->joinvalid = true; cv->joinpos = *sp; cv->joinpos.selected = false; if ( order2 ) { if ( base->nonextcp || sp->noprevcp ) { base->nonextcp = sp->noprevcp = true; base->nextcp = base->me; sp->prevcp = sp->me; } else { base->nextcp.x = sp->prevcp.x = (base->nextcp.x+sp->prevcp.x)/2; base->nextcp.y = sp->prevcp.y = (base->nextcp.y+sp->prevcp.y)/2; } base->nextcpdef = sp->prevcpdef = true; } SplineMake(base,sp,order2); if ( cv->active_tool!=cvt_pen ) SplineCharDefaultNextCP(base); SplineCharDefaultPrevCP(sp); ss->last = sp; if ( sp->pointtype==pt_tangent ) { SplineCharTangentNextCP(sp); if ( sp->next ) SplineRefigure(sp->next ); } } else { /* Merge two spline sets */ SplineSetSpirosClear(sel); CVMergeSPLS(cv,sel, base,sp); } sp->selected = true; if ( base->pointtype==pt_tangent ) { SplineCharTangentPrevCP(base); if ( base->prev!=NULL ) SplineRefigure(base->prev); } } else if ( cv->p.spline!=NULL ) { sp = SplineBisect(cv->p.spline,cv->p.t); cv->joinvalid = true; cv->joinpos = *sp; cv->joinpos.selected = false; if ( cv->active_tool==cvt_pen ) ptype = pt_curve; sp->pointtype = ptype; if ( ptype==pt_hvcurve ) { SPHVCurveForce(sp); } sp->selected = true; ss = cv->p.spl; } else { ss = chunkalloc(sizeof(SplineSet)); sp = SplinePointCreate( cv->p.cx, cv->p.cy ); ss->first = ss->last = sp; ss->next = cv->b.layerheads[cv->b.drawmode]->splines; cv->b.layerheads[cv->b.drawmode]->splines = ss; sp->nonextcp = sp->noprevcp = 1; sp->nextcpdef = sp->prevcpdef = 1; sp->pointtype = ptype; sp->selected = true; } cv->active_spl = ss; cv->active_sp = sp; CVSetCharChanged(cv,true); CVInfoDraw(cv,cv->gw); SCUpdateAll(sc); if ( cv->active_tool == cvt_pen ) cv->p.constrain = sp->me; }
static void CVMouseDownSpiroPoint(CharView *cv, GEvent *event) { SplineSet *sel, *ss; SplineChar *sc = cv->b.sc; spiro_cp *base, *cp; int base_index, cp_index, i; char ty = (cv->active_tool==cvt_curve?SPIRO_G4: cv->active_tool==cvt_hvcurve?SPIRO_G2: cv->active_tool==cvt_corner?SPIRO_CORNER: cv->active_tool==cvt_tangent?SPIRO_LEFT: /*cv->active_tool==cvt_pen?*/SPIRO_RIGHT); cv->active_spl = NULL; cv->active_sp = NULL; sel = CVAnySelPointList(cv); if ( sel!=NULL ) { if ( SPIRO_SELECTED(&sel->spiros[0]) ) base_index = 0; else base_index = sel->spiro_cnt-2; base = &sel->spiros[base_index]; if ( base==cv->p.spiro ) return; /* We clicked on the active point, that's a no-op */ } CVPreserveState(&cv->b); CVClearSel(cv); if ( sel!=NULL ) { if ( (cp = cv->p.spiro)!=NULL ) cp_index = cp-cv->p.spl->spiros; cv->lastselcp = base; ss = sel; if ( base_index!=sel->spiro_cnt-2 ) { SplineSetReverse(sel); base = &sel->spiros[sel->spiro_cnt-2]; if ( cv->p.spl==sel ) { cp_index = sel->spiro_cnt-2-cp_index; cp = &sel->spiros[cp_index]; } } if ( cp==NULL || (cp_index!=0 && cp_index!=cv->p.spl->spiro_cnt-2) || cp==base || !SPIRO_SPL_OPEN(cv->p.spl)) { /* Add a new point */ if ( sel->spiro_cnt>=sel->spiro_max ) sel->spiros = realloc(sel->spiros,(sel->spiro_max += 10)*sizeof(spiro_cp)); cp = &sel->spiros[sel->spiro_cnt-1]; cp[1] = cp[0]; /* Move the final 'z' */ cp->x = cv->p.cx; cp->y = cv->p.cy; cp->ty = ty; SPIRO_DESELECT(cp-1); ++sel->spiro_cnt; } else if ( cv->p.spl==sel ) { /* Close the current spline set */ sel->spiros[0].ty = ty; cv->joinvalid = true; cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp); } else { /* Merge two spline sets */ SplinePoint *sp = cp_index==0 ? cv->p.spl->first : cv->p.spl->last; SplinePoint *basesp = base_index==0 ? sel->first : sel->last; cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp); CVMergeSPLS(cv,ss,basesp,sp); } } else if ( cv->p.spline!=NULL ) { /* Add an intermediate point on an already existing spline */ ss = cv->p.spl; if ( ss->spiro_cnt>=ss->spiro_max ) ss->spiros = realloc(ss->spiros,(ss->spiro_max += 10)*sizeof(spiro_cp)); for ( i=ss->spiro_cnt-1; i>cv->p.spiro_index; --i ) ss->spiros[i+1] = ss->spiros[i]; ++ss->spiro_cnt; cp = &ss->spiros[cv->p.spiro_index+1]; cp->x = cv->p.cx; cp->y = cv->p.cy; cp->ty = ty; ss = cv->p.spl; cv->joinvalid = true; cv->joincp = *cp; SPIRO_DESELECT(&cv->joincp); } else { /* A new point on a new (open) contour */ ss = chunkalloc(sizeof(SplineSet)); ss->next = cv->b.layerheads[cv->b.drawmode]->splines; cv->b.layerheads[cv->b.drawmode]->splines = ss; ss->spiros = malloc((ss->spiro_max=10)*sizeof(spiro_cp)); cp = &ss->spiros[0]; cp->x = cv->p.cx; cp->y = cv->p.cy; cp->ty = SPIRO_OPEN_CONTOUR; cp[1].x = cp[1].y = 0; cp[1].ty = 'z'; ss->spiro_cnt = 2; } SPIRO_SELECT(cp); SSRegenerateFromSpiros(ss); cv->active_spl = ss; cv->active_cp = cp; CVSetCharChanged(cv,true); CVInfoDraw(cv,cv->gw); SCUpdateAll(sc); }
void CVMouseMoveTransform(CharView *cv) { real transform[6]; CVRestoreTOriginalState(cv); if ( cv->info.x != cv->p.cx || cv->info.y != cv->p.cy ) { transform[0] = transform[3] = 1; transform[1] = transform[2] = 0; switch ( cv->active_tool ) { case cvt_rotate: { real angle = atan2(cv->info.y-cv->p.cy,cv->info.x-cv->p.cx); transform[0] = transform[3] = cos(angle); transform[2] = -(transform[1] = sin(angle)); } break; case cvt_flip: { real dx,dy; if (( dx = cv->info.x-cv->p.cx)<0 ) dx=-dx; if (( dy = cv->info.y-cv->p.cy)<0 ) dy=-dy; if ( dy>2*dx ) transform[0] = -1; else if ( dx>2*dy ) transform[3] = -1; else if ( (cv->info.x-cv->p.cx)*(cv->info.y-cv->p.cy)>0 ) { transform[0] = transform[3] = 0; transform[1] = transform[2] = -1; } else { transform[0] = transform[3] = 0; transform[1] = transform[2] = 1; } } break; case cvt_scale: { transform[0] = 1.0+(cv->info.x-cv->p.cx)/(400*cv->scale); transform[3] = 1.0+(cv->info.y-cv->p.cy)/(400*cv->scale); } break; case cvt_skew: { real angle = atan2(cv->info.y-cv->p.cy,cv->info.x-cv->p.cx); transform[2] = sin(angle); } break; case cvt_3d_rotate: { real angle = atan2(cv->info.y-cv->p.cy,cv->info.x-cv->p.cx); /* Allow one pixel per degree */ real zangle = sqrt( (cv->info.x-cv->p.cx)*(cv->info.x-cv->p.cx) + (cv->info.y-cv->p.cy)*(cv->info.y-cv->p.cy) ) * cv->scale * 3.1415926535897932/180; real s = sin(angle), c = cos(angle); real cz = cos(zangle); transform[0] = c*c + s*s*cz; transform[3] = s*s + c*c*cz; transform[2] = transform[1] = c*s * (cz-1); } break; /* Perspective takes three points: origin, start point, cur point */ /* first rotate so that orig/start are the x axis */ /* then define perspective so that: */ /* y' = y */ /* x' = cur.x + (cur.y - y)/cur.y * (x - cur.x) */ /* then rotate back */ case cvt_perspective: { real angle = atan2(cv->p.cy,cv->p.cx); real s = sin(angle), c = cos(angle); transform[0] = transform[3] = c; transform[2] = -(transform[1] = -s); transform[4] = transform[5] = 0; CVTransFunc(cv,transform,false); CVYPerspective((CharViewBase *) cv, c*cv->info.x + s*cv->info.y, -s*cv->info.x + c*cv->info.y); transform[2] = -(transform[1] = s); } break; default: break; } /* Make the pressed point be the center of the transformation */ if ( cv->active_tool!=cvt_perspective ) { transform[4] = -cv->p.cx*transform[0] - cv->p.cy*transform[2] + cv->p.cx; transform[5] = -cv->p.cy*transform[3] - cv->p.cx*transform[1] + cv->p.cy; } CVSetCharChanged(cv,true); CVTransFunc(cv,transform,false); } SCUpdateAll(cv->b.sc); }