/* * LaGUI: A graphical application framework. * Copyright (C) 2022-2023 Wu Yiming * * 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 3 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, see . */ #include "la_5.h" #include extern LA MAIN; extern tnsMain *T; tnsShape* tnsNewShape(tnsShapeObject* so){ tnsShape* s=memAcquireSimple(sizeof(tnsShape)); lstAppendItem(&so->Shapes,s); return s; } tnsSPoint* tnsNewSPoint(tnsShape* s,real x, real y,real ldx,real ldy, real rdx,real rdy){ tnsSPoint* sp=memAcquireSimple(sizeof(tnsSPoint)); tnsVectorSet2(sp->p,x,y); tnsVectorSet2(sp->dl,ldx,ldy); tnsVectorSet2(sp->dr,rdx,rdy); lstAppendItem(&s->Points,sp); return sp; } tnsSPoint* tnsShapeInsertSPointAt(tnsShape* s, tnsSPoint* pivot, int before){ tnsSPoint* sp=memAcquireSimple(sizeof(tnsSPoint)); tnsVectorSet2v(sp->p,pivot->p); tnsVectorSet2v(sp->dl,pivot->dl); tnsVectorSet2v(sp->dr,pivot->dr); if(before){ lstInsertItemBefore(&s->Points,sp,pivot); } else{ lstInsertItemAfter(&s->Points,sp,pivot); } } void tnsShapeSetHole(tnsShape* s, int closed, int toggle){ if(toggle) tnsShapeSetHole(s,!(s->flags&TNS_SHAPE_HOLE),0); else{ if(closed) s->flags|=TNS_SHAPE_HOLE; else s->flags&=(~TNS_SHAPE_HOLE); } } void tnsShapeSetClosed(tnsShape* s, int closed, int toggle){ if(toggle) tnsShapeSetClosed(s,!(s->flags&TNS_SHAPE_CLOSED),0); else{ if(closed) s->flags|=TNS_SHAPE_CLOSED; else s->flags&=(~TNS_SHAPE_CLOSED); } } void tnsShapeSetEndPoint(tnsShape* s, tnsSPoint* p){ tnsSPoint* ep=s->Points.pLast, *sp=s->Points.pFirst, *np=p->Item.pNext; if(!np || ep==sp || sp->Item.pNext==ep) return; sp->Item.pPrev=ep; ep->Item.pNext=sp; np->Item.pPrev=0; p->Item.pNext=0; s->Points.pFirst=np; s->Points.pLast=p; } void tnsShapeClearExtraFlags(tnsShapeObject* so){ for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ s->flags&=(~TNS_MESH_FLAG_PICKED); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags&=(~TNS_MESH_FLAG_PICKED); } } } void tnsShapeRefreshIndex(tnsShapeObject* so){ u32bit i=0; tnsShape* NextS; for(tnsShape* s=so->Shapes.pFirst;s;s=NextS){ NextS=s->Item.pNext; if(!s->Points.pFirst){ lstRemoveItem(&so->Shapes,s); memLeave(s); continue; } for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->i=i; i++; } } } int tnsShapePointAnySelected(tnsShape* s){ for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(sp->flags&TNS_SPOINT_SELECTED) return 1; } return 0; } int tnsShapeAnySelected(tnsShapeObject* so){ for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ if(tnsShapePointAnySelected(s)) return 1; } return 0; } void tnsShapeDeselectAll(tnsShapeObject* so){ for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags&=(~TNS_SPOINT_SELECTED); } } } void tnsShapeSelectAll(tnsShapeObject* so){ for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags|=TNS_SPOINT_SELECTED; } } } void tnsShapeSelectPoint(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle, int lr){ if(!so) return; int flag=(lr==1)?TNS_SPOINT_SELECTED_L:(lr==2)?TNS_SPOINT_SELECTED_R:(TNS_SPOINT_SELECTED); if(!(sp->flags&TNS_SPOINT_BEZIER)){ flag=TNS_SPOINT_SELECTED; } if(toggle) tnsShapeSelectPoint(so,sp,((sp->flags&flag)==flag?0:1),0,lr); elif(select) sp->flags|=flag; else sp->flags&=(~flag); //if(!so->FirstSelectV) so->FirstSelectV=sp; so->LastSelectV=sp; } void tnsShapeSelectRingFrom(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle){ int has_idle=0; laListHandle spl={sp->Item.pPrev,sp->Item.pNext}; while(spl.pFirst && ((laListItem*)spl.pFirst)->pPrev){ spl.pFirst=((laListItem*)spl.pFirst)->pPrev; } while(spl.pLast && ((laListItem*)spl.pLast)->pNext){ spl.pLast=((laListItem*)spl.pLast)->pNext; } if(toggle){ for(tnsSPoint* isp=spl.pFirst;isp;isp=isp->Item.pNext){ if(isp!=sp && (!(isp->flags&TNS_SPOINT_SELECTED))) has_idle=1; if(has_idle){ break; } } if(has_idle){ select=1; }else{ select=0; } } for(tnsSPoint* isp=spl.pFirst;isp;isp=isp->Item.pNext){ if(select){ isp->flags|=TNS_SPOINT_SELECTED; }else{ isp->flags&=(~TNS_SPOINT_SELECTED); } } } void tnsShapeSelectLinked(tnsShapeObject* so){ for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ if(!tnsShapePointAnySelected(s)){ continue; } for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags|=TNS_SPOINT_SELECTED; } } } tnsShape* tnsShapeRemovePoint(tnsShapeObject* so, tnsShape* s, tnsSPoint* sp, int SplitShape){ if(SplitShape && sp->Item.pNext && sp->Item.pPrev){ if(s->flags&TNS_SHAPE_CLOSED){ tnsShapeSetEndPoint(s,sp); } tnsShape* ns=tnsNewShape(so); ns->mat=s->mat; tnsSPoint* NextIP; for(tnsSPoint* ip=sp->Item.pNext;ip;ip=NextIP){ NextIP=ip->Item.pNext; tnsSPoint* nsp=tnsNewSPoint(ns,ip->p[0],ip->p[1],ip->dl[0],ip->dl[1],ip->dr[0],ip->dr[1]); nsp->flags=ip->flags; lstRemoveItem(&s->Points,ip); } s->flags&=(~TNS_SHAPE_CLOSED); ns->flags&=(~TNS_SHAPE_CLOSED); lstRemoveItem(&s->Points,sp); memLeave(sp); return ns; }else{ lstRemoveItem(&s->Points,sp); memLeave(sp); return 0; } } int tnsShapeConnect(tnsShapeObject* so, tnsShape* s1, tnsShape* s2, tnsSPoint* sp1, tnsSPoint* sp2){ if((sp1!=s1->Points.pFirst && sp1!=s1->Points.pLast) || (sp2!=s2->Points.pFirst && sp2!=s2->Points.pLast)) return 0; if(sp1==s1->Points.pFirst){ lstReverse(&s1->Points);for(tnsSPoint*sp=s1->Points.pFirst;sp;sp=sp->Item.pNext){ LA_SWAP(real,sp->dl[0],sp->dr[0]); LA_SWAP(real,sp->dl[1],sp->dr[1]); } } if(sp2==s2->Points.pLast){ lstReverse(&s2->Points);for(tnsSPoint*sp=s2->Points.pFirst;sp;sp=sp->Item.pNext){ LA_SWAP(real,sp->dl[0],sp->dr[0]); LA_SWAP(real,sp->dl[1],sp->dr[1]); } } tnsSPoint* ip;while(ip=lstPopItem(&s2->Points)){ tnsSPoint* nsp=tnsNewSPoint(s1,ip->p[0],ip->p[1],ip->dl[0],ip->dl[1],ip->dr[0],ip->dr[1]); nsp->flags=ip->flags; memLeave(ip); } lstRemoveItem(&so->Shapes.pFirst,s2); memLeave(s2); tnsShapeRefreshIndex(so); return 1; } int tnsShapeConnectSelected(tnsShapeObject* so){ tnsShape* s1=0,*s2=0; tnsSPoint* sp1=0,*sp2=0; for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ if(s->flags&TNS_SHAPE_CLOSED) continue; if(!s1){ if(((tnsSPoint*)s->Points.pFirst)->flags&TNS_MESH_FLAG_SELECTED){ s1=s;sp1=s->Points.pFirst; } elif(((tnsSPoint*)s->Points.pLast)->flags&TNS_MESH_FLAG_SELECTED){ s1=s;sp1=s->Points.pLast; } }else{ if(((tnsSPoint*)s->Points.pFirst)->flags&TNS_MESH_FLAG_SELECTED){ s2=s;sp2=s->Points.pFirst; break; } elif(((tnsSPoint*)s->Points.pLast)->flags&TNS_MESH_FLAG_SELECTED){ s2=s;sp2=s->Points.pLast; break; } } } if(s1&&s2&&sp1&&sp2){ return tnsShapeConnect(so,s1,s2,sp1,sp2); } return 0; } int tnsShapeExtrudeSelected(tnsShapeObject* so, int DupliOnly){ laListHandle* pairs; tnsShape* NextS; int any=0; for(tnsShape* s=so->Shapes.pFirst;s;s=NextS){ NextS=s->Item.pNext; if(s->flags&TNS_MESH_FLAG_PICKED){ continue; } tnsSPoint* PrevSP=0,*NextSP=0,*nsp=0; tnsShape* ns=0; int broken=0; for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_MESH_FLAG_SELECTED)){ broken=1; break; } } if(!broken){ any=1; tnsShape* ns=tnsNewShape(so); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ tnsSPoint* nsp=tnsNewSPoint(ns,sp->p[0],sp->p[1],sp->dl[0],sp->dl[1],sp->dr[0],sp->dr[1]); nsp->flags=sp->flags; } ns->flags=(s->flags|TNS_MESH_FLAG_PICKED); ns->mat=s->mat; for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags&=(~TNS_SPOINT_SELECTED); } continue; } for(tnsSPoint* sp=s->Points.pFirst;sp;sp=NextSP){ NextSP=sp->Item.pNext; if(!(sp->flags&TNS_MESH_FLAG_SELECTED)){ PrevSP=sp; continue; } int doprev=0,donext=0; if((!PrevSP) || (PrevSP->flags^sp->flags)&TNS_MESH_FLAG_SELECTED){ doprev=1; } if((!NextSP) || (NextSP->flags^sp->flags)&TNS_MESH_FLAG_SELECTED){ donext=1; } if(DupliOnly){ if(doprev){ ns=tnsNewShape(so); ns->flags=(s->flags&(~TNS_SHAPE_CLOSED)); ns->flags|=TNS_MESH_FLAG_PICKED; ns->mat=s->mat; any=1; } if(ns){ nsp=tnsNewSPoint(ns,sp->p[0],sp->p[1],sp->dl[0],sp->dl[1],sp->dr[0],sp->dr[1]); nsp->flags=sp->flags; } if(donext){ ns=0; } }else{ if(!(doprev&&donext)){ any=1; if(doprev) tnsShapeInsertSPointAt(s,sp,1); elif(donext) tnsShapeInsertSPointAt(s,sp,0); }else{ if((!NextSP) && donext){ tnsShapeInsertSPointAt(s,sp,1); } else{ tnsShapeInsertSPointAt(s,sp,0); } } } PrevSP=sp; } } if(DupliOnly){ for(tnsShape* s=so->Shapes.pFirst;s;s=NextS){ NextS=s->Item.pNext; if(s->flags&TNS_MESH_FLAG_PICKED){ continue; } for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags&=(~TNS_SPOINT_SELECTED); } } } tnsShapeClearExtraFlags(so); tnsShapeRefreshIndex(so); return any; } void tnsShapeEnterEditMode(tnsShapeObject* so){ if(so->Mode==TNS_MESH_EDIT_MODE) return; so->Mode = TNS_MESH_EDIT_MODE; tnsShapeRefreshIndex(so); tnsInvalidateEvaluation(so); } void tnsShapeLeaveEditMode(tnsShapeObject* so){ if(so->Mode==TNS_MESH_OBJECT_MODE) return; so->Mode = TNS_MESH_OBJECT_MODE; tnsInvalidateEvaluation(so); } tnsShapeObject* tnsDuplicateShapeObject(tnsShapeObject* from){ if(from->Base.Type!=TNS_OBJECT_SHAPE) return 0; tnsShapeObject* to = memAcquireHyper(sizeof(tnsShapeObject)); tnsInitObjectBase(to, from->Base.ParentObject?from->Base.ParentObject:from->Base.InRoot, from->Base.Name->Ptr, TNS_OBJECT_SHAPE,0,0,0,0,0,0,0,0,1); tnsCopyObjectTransformationsLocal(to,from); tnsMaterialSlot* new_current=0; for(tnsMaterialSlot* ms=from->Materials.pFirst;ms;ms=ms->Item.pNext){ tnsMaterialSlot* nms=tnsNewMaterialSlot(to); nms->Index=ms->Index; memAssignRef(nms,&nms->Material,ms->Material); if(ms==from->CurrentMaterial) new_current=nms; } memAssignRef(to,&to->CurrentMaterial,new_current); if(!from->Shapes.pFirst){ return to; } for(tnsShape* s=from->Shapes.pFirst;s;s=s->Item.pNext){ tnsShape* ns=tnsNewShape(to); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ tnsSPoint* nsp=tnsNewSPoint(ns,sp->p[0],sp->p[1],sp->dl[0],sp->dl[1],sp->dr[0],sp->dr[1]); nsp->flags=sp->flags; } ns->flags=s->flags;ns->mat=s->mat; } return to; } void tnsInitShapeSquare(tnsShapeObject* so, real size){ tnsShape* s=tnsNewShape(so); tnsNewSPoint(s,-size,-size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED; tnsNewSPoint(s,-size,size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED; tnsNewSPoint(s,size,size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED; tnsNewSPoint(s,size,-size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED; tnsShapeSetClosed(s,1,0); tnsShapeRefreshIndex(so); } NVGpaint tns_GetMaterial2D(tnsMaterial* m){ NVGcontext* vg=MAIN.CurrentWindow->nvg; if(m->GradientMode==TNS_GRADIENT_MODE_LINEAR){ return nvgLinearGradient(vg,m->GradientCenter[0],m->GradientCenter[1],m->GradientSize[0],m->GradientSize[1], nvgRGBAf(LA_COLOR4(m->Color)),nvgRGBAf(LA_COLOR4(m->Color2))); }elif(m->GradientMode==TNS_GRADIENT_MODE_BOX){ return nvgBoxGradient(vg,m->GradientCenter[0],m->GradientCenter[1],m->GradientSize[0],m->GradientSize[1], m->GradientBoxR,m->GradientBoxF,nvgRGBAf(LA_COLOR4(m->Color)),nvgRGBAf(LA_COLOR4(m->Color2))); }elif(m->GradientMode==TNS_GRADIENT_MODE_RADIAL){ return nvgRadialGradient(vg,m->GradientCenter[0],m->GradientCenter[1],m->GradientSize[0],m->GradientSize[1], nvgRGBAf(LA_COLOR4(m->Color)),nvgRGBAf(LA_COLOR4(m->Color2))); } NVGpaint p={0}; return p; } void tns_DrawShape(tnsShape* s, tnsMaterial* mat, real* override_color, int DrawEdit, real PointScale){ if(!s->Points.pFirst) return; int HasSelection=0; int closed=(s->flags&TNS_SHAPE_CLOSED); NVGcontext* vg=MAIN.CurrentWindow->nvg; if(DrawEdit==2){ nvgShapeAntiAlias(vg,0); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ real color[4]={0,0,0,1}; nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*1); TNS_ID_TO_COLOR(color,sp->i*3+1); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg); nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1],PointScale*1); TNS_ID_TO_COLOR(color,sp->i*3+2); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg); nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1],PointScale*1); TNS_ID_TO_COLOR(color,sp->i*3+3); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg); } nvgShapeAntiAlias(vg,1); return; } if(DrawEdit){ nvgBeginPath(vg); } tnsSPoint* sp1=s->Points.pFirst; nvgMoveTo(vg,sp1->p[0],sp1->p[1]); if(sp1->flags&TNS_SPOINT_SELECTED){HasSelection=1;} for(tnsSPoint* sp=sp1->Item.pNext;sp;sp=sp->Item.pNext){ if(sp->flags&TNS_SPOINT_SELECTED){HasSelection=1;} if(sp->Item.pPrev){ tnsSPoint* np=sp->Item.pPrev; real c1[2],c2[2]; tnsVectorSet2v(c1,np->p); tnsVectorSet2v(c2,sp->p); if(np->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c1,np->dr); } if(sp->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c2,sp->dl); } nvgBezierTo(vg,c1[0],c1[1],c2[0],c2[1],sp->p[0],sp->p[1]); }else{ nvgLineTo(vg,sp->p[0],sp->p[1]); } } if(closed && (sp1!=s->Points.pLast)){ tnsSPoint* np=s->Points.pLast; tnsSPoint* sp=sp1; real c1[2],c2[2]; tnsVectorSet2v(c1,np->p); tnsVectorSet2v(c2,sp->p); if(np->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c1,np->dr); } if(sp->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c2,sp->dl); } nvgBezierTo(vg,c1[0],c1[1],c2[0],c2[1],sp->p[0],sp->p[1]); nvgClosePath(vg); nvgPathWinding(vg,(s->flags&TNS_SHAPE_HOLE)?NVG_HOLE:NVG_SOLID); if(!(s->flags&TNS_SHAPE_HOLE)){ if((!override_color) && mat && mat->GradientMode){ nvgFillPaint(vg,tns_GetMaterial2D(mat)); }else{ nvgFillColor(vg, override_color?nvgRGBAf(LA_COLOR4(override_color)):(mat?nvgRGBAf(LA_COLOR4(mat->Color)):nvgRGBAf(0.8,0.8,0.8,1))); } } } if(DrawEdit || (!closed)){ tnsVector4d color; tnsVectorCopy4d(laAccentColor(LA_BT_VERTEX),color); real* ActiveColor=laAccentColor(LA_BT_SVERTEX); if(DrawEdit && (!HasSelection)) color[3]*=0.4; nvgStrokeColor(vg,override_color?nvgRGBAf(LA_COLOR4(override_color)):nvgRGBAf(LA_COLOR4(color))); nvgStrokeWidth(vg,PointScale*4); if(!DrawEdit){nvgShapeAntiAlias(vg,0);nvgStroke(vg);nvgShapeAntiAlias(vg,1);return;} nvgStroke(vg); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ int mainsel=sp->flags&TNS_MESH_FLAG_SELECTED; if(sp->flags&(TNS_SPOINT_FREE|TNS_SPOINT_ALIGNED)){ nvgStrokeWidth(vg,PointScale*2); nvgStrokeColor(vg,(mainsel||(sp->flags&TNS_SPOINT_SELECTED_L))?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color))); nvgBeginPath(vg); nvgMoveTo(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1]); nvgLineTo(vg,sp->p[0],sp->p[1]); nvgStroke(vg); nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1],PointScale*5); nvgStroke(vg); nvgStrokeColor(vg,(mainsel||(sp->flags&TNS_SPOINT_SELECTED_R))?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color))); nvgBeginPath(vg); nvgMoveTo(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1]); nvgLineTo(vg,sp->p[0],sp->p[1]); nvgStroke(vg); nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1],PointScale*5); nvgStroke(vg); } nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*5); nvgFillColor(vg,mainsel?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color))); nvgFill(vg); } } } void tnsDrawShapeObjectShapes(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de, real* override_color, int DrawEdit){ tnsShapeObject* so=ei->Object; NVGcontext* vg=MAIN.CurrentWindow->nvg; real sca[3]; tnsExtractScale44d(ei->Mat,sca); real rot[3]; tnsExtractXYZEuler44d(ei->Mat,rot); nvgSave(vg); if(de->Is3D){ tnsVector4d pos; tnsApplyTransform44d(pos,de->mViewProj,&ei->Mat[12]); pos[0]/=pos[3]; pos[1]/=pos[3]; nvgTranslate(vg,pos[0]*de->W/2,pos[1]*de->H/2); nvgScale(vg,LA_RH*sca[0],LA_RH*sca[1]); nvgRotate(vg,rot[2]); }else{ nvgTranslate(vg,ei->Mat[12],-ei->Mat[13]); nvgScale(vg,sca[0],-sca[1]); nvgRotate(vg,rot[2]); } if(DrawEdit!=2){ int begun=0; int ishole=0,nexthole=0; for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ tnsMaterial* mat=0; if(de->DisplayMode==LA_CANVAS_DISPLAY_MATERIAL){ for(tnsMaterialSlot* ms=so->Materials.pFirst;ms;ms=ms->Item.pNext){ if(s->mat==ms->Index){ mat=ms->Material; break; } } } if(!begun){ begun=1; nvgBeginPath(vg); } ishole=s->flags&TNS_SHAPE_HOLE; tnsShape* ns=s->Item.pNext; nexthole=ns?(ns->flags&TNS_SHAPE_HOLE):0; tns_DrawShape(s,mat,override_color,0,de->PointScale/sca[0]); if(!nexthole /*(!ns) || (ishole &&(!nexthole))*/){ nvgFill(vg); begun=0; } } } if(DrawEdit||so->Mode==TNS_MESH_EDIT_MODE){ for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ tns_DrawShape(s,0,override_color,DrawEdit?DrawEdit:so->Mode==TNS_MESH_EDIT_MODE,de->PointScale/sca[0]); } } nvgRestore(vg); } void tnsDrawShapeObject(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){ tnsShapeObject* so=ei->Object; if(!so->Shapes.pFirst){ tnsDrawSingleObjectOrigin(so); } tnsDrawShapeObjectShapes(ei,de,0,0); } void tnsDrawShapeObjectSelectionID(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){ int i=ei->InstanceSelectionID; real color[4]={0,0,0,1}; TNS_ID_TO_COLOR(color,i); tnsDrawShapeObjectShapes(ei,de,color,0); } void tnsDrawShapePointsSelectionID(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){ tnsDrawShapeObjectShapes(ei,de,0,2); } void tnsDrawShapeObjectOverlay(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){ int i=ei->InstanceSelectionID; real color[4]; tnsVectorCopy4d(laAccentColor(LA_BT_ACTIVE),color); if(ei->Object!=ei->Object->InRoot->Active){ color[3]=0.4; }else{ color[3]=0.7; } tnsDrawShapeObjectShapes(ei,de,color,0); } void tnsEvaluateShapeObject(tnsShapeObject* so, tnsEvaluateData* ed){ int DrawTo=TNS_EVAL_LAYER_SOLID; if(so->Backdrop){ DrawTo=TNS_EVAL_LAYER_BACKDROP; } tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObject,DrawTo,0,0,0); if(ed->FillSelectionID){ tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObjectSelectionID,TNS_EVAL_LAYER_SELECTION,0,1,so->Base.SelectID); } if(ed->FillOutline && (!ed->OverrideID)){ if((so->Base.Flags&TNS_OBJECT_FLAGS_SELECTED) && (so->Mode!=TNS_MESH_EDIT_MODE)){ tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObjectOverlay,TNS_EVAL_LAYER_OVERLAY,0,0,0); } } } tnsShapeObject *tnsCreateShapeEmpty(tnsObject *under, char *Name, real AtX, real AtY, real AtZ){ tnsShapeObject *so = memAcquireHyper(sizeof(tnsShapeObject)); tnsInitObjectBase(&so->Base, under, Name, TNS_OBJECT_SHAPE, AtX, AtY, AtZ, 0, 0, 0, 1.0f, TNS_ROTATION_XYZ_EULER, 1.0f); return so; } tnsShapeObject *tnsCreateShapeSquare(tnsObject *under, char *Name, real AtX, real AtY, real AtZ, real size){ tnsShapeObject *so=tnsCreateShapeEmpty(under, Name, AtX, AtY, AtZ); tnsInitShapeSquare(so, size); return so; } int OPCHK_IsAnyPointSelected(laPropPack *This, laStringSplitor *ss){ if(This && This->EndInstance){ laCanvasExtra* ex=This->EndInstance; laUiItem* ui=ex->ParentUi; tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0; if(root->Active && root->Active->Type!=TNS_OBJECT_SHAPE) return 0; tnsShapeObject* so=root->Active; return tnsShapeAnySelected(so); } return 0; } int OPINV_SetPointHandle(laOperator *a, laEvent *e){ laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi; tnsObject*root=ui?ui->PP.EndInstance:0; if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED; tnsShapeObject* so=root->Active; int ran=0; char* mode=strGetArgumentString(a->ExtraInstructionsP,"mode"); if(!mode){ laEnableOperatorPanel(a,a->This,e->x,e->y,200,200,0,0,0,0,0,0,0,0,e); return LA_RUNNING; } int flags=0,HandleFlags=TNS_SPOINT_ALIGNED|TNS_SPOINT_FREE; if(strSame(mode,"ALIGNED")){ flags=TNS_SPOINT_ALIGNED; } elif(strSame(mode,"FREE")){ flags=TNS_SPOINT_FREE; } tnsVector2d pl={0,0},pr={0,0}; for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_MESH_FLAG_SELECTED)) continue; int oldf=sp->flags&HandleFlags, hasl=0,hasr=0; sp->flags&=(~HandleFlags); sp->flags|=flags; ran=1; if(!flags){ continue; } if(sp->Item.pPrev){ tnsVectorSet2v(pl,((tnsSPoint*)sp->Item.pPrev)->p); hasl=1;} if(sp->Item.pNext){ tnsVectorSet2v(pr,((tnsSPoint*)sp->Item.pNext)->p); hasr=1;} if(!hasl){ if(!hasr){ tnsVectorSet2(pl,sp->p[0]-0.5,sp->p[1]); tnsVectorSet2(pr,sp->p[0]+0.5,sp->p[1]); } else{ tnsVectorSet2(pl,-pr[0]+2*sp->p[0],-pr[1]+2*sp->p[1]); } }else{ if(!hasr){ tnsVectorSet2(pr,-pl[0]+2*sp->p[0],-pl[1]+2*sp->p[1]); } } if(flags==TNS_SPOINT_ALIGNED && flags!=oldf){ tnsVector2d diff; tnsVectorMinus2d(diff,pr,pl); tnsVectorMultiSelf2d(diff,0.25); tnsVectorSet2v(sp->dr,diff); tnsVectorMultiSelf2d(diff,-1); tnsVectorSet2v(sp->dl,diff); }elif(flags==TNS_SPOINT_FREE && oldf!=TNS_SPOINT_ALIGNED){ tnsVectorMinus2d(sp->dl,pl,sp->p); tnsVectorMultiSelf2d(sp->dl,0.3); tnsVectorMinus2d(sp->dr,pr,sp->p); tnsVectorMultiSelf2d(sp->dr,0.3); } } } if(ran){ laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Set point handle",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world"); } return LA_FINISHED; } void laui_SetPointHandle(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){ laColumn* c=laFirstColumn(uil); laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=NONE;text=Normal",0,0); laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=ALIGNED;text=Aligned",0,0); laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=FREE;text=Free",0,0); } int OPINV_SetShapeClosed(laOperator *a, laEvent *e){ laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi; tnsObject*root=ui?ui->PP.EndInstance:0; if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED; tnsShapeObject* so=root->Active; int ran=0; char* mode=strGetArgumentString(a->ExtraInstructionsP,"mode"); char* res=strGetArgumentString(a->ExtraInstructionsP,"reset"); int set=2; if(strSame(mode,"CLOSE")){ set=1; }elif(strSame(mode,"OPEN")){ set=0; } int reset=0; if(strSame(res,"TRUE")){ reset=1; } for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_SPOINT_SELECTED)) continue; if(reset){ tnsShapeSetEndPoint(s,sp); tnsShapeRefreshIndex(so); tnsShapeSetClosed(s,0,0); } else{ tnsShapeSetClosed(s,set,set==2); } ran=1; break; } } if(ran){ laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Open/Close shape",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world"); } return LA_FINISHED; } int OPINV_SetShapeHole(laOperator *a, laEvent *e){ laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi; tnsObject*root=ui?ui->PP.EndInstance:0; if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED; tnsShapeObject* so=root->Active; int ran=0; char* mode=strGetArgumentString(a->ExtraInstructionsP,"mode"); int set=2; if(strSame(mode,"HOLE")){ set=1; }elif(strSame(mode,"FILL")){ set=0; } for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_SPOINT_SELECTED)) continue; tnsShapeSetHole(s,set,set==2); ran=1; break; } } if(ran){ laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Open/Close shape",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world"); } return LA_FINISHED; } int OPINV_ReorderShape(laOperator *a, laEvent *e){ laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi; tnsObject*root=ui?ui->PP.EndInstance:0; if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED; tnsShapeObject* so=root->Active; int ran=0; char* mode=strGetArgumentString(a->ExtraInstructionsP,"direction"); int set=0; if(strSame(mode,"UP")){ set=1; }elif(strSame(mode,"DOWN")){ set=0; } tnsShape* ns; for(tnsShape*s=so->Shapes.pFirst;s;s=ns){ ns=s->Item.pNext; if((!tnsShapePointAnySelected(s))||s->flags&TNS_MESH_FLAG_PICKED) continue; if(set) lstMoveUp(&so->Shapes,s); else lstMoveDown(&so->Shapes,s); s->flags|=TNS_MESH_FLAG_PICKED; ran=1; } if(ran){ tnsShapeClearExtraFlags(so); laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Reorder shapes",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world"); } return LA_FINISHED; } void la_RegisterShapeOperators(){ laPropContainer *pc; laProp *p; laOperatorType *at; laEnumProp *ep; at=laCreateOperatorType("M_set_point_handle", "Set Point Handle", "Set handle type of selected points",OPCHK_IsAnyPointSelected,0,0,OPINV_SetPointHandle,OPMOD_FinishOnData,0,0); at->UiDefine=laui_SetPointHandle; at=laCreateOperatorType("M_set_shape_closed", "Set Shape Closed", "Set shape closed or open",OPCHK_IsAnyPointSelected,0,0,OPINV_SetShapeClosed,0,0,0); at=laCreateOperatorType("M_set_shape_hole", "Set Shape Hole", "Set shape as hole or fill",OPCHK_IsAnyPointSelected,0,0,OPINV_SetShapeHole,0,0,0); at=laCreateOperatorType("M_reorder_shape", "Reorder shape", "Reorder shapes (move up/down in the stack)",OPCHK_IsAnyPointSelected,0,0,OPINV_ReorderShape,0,0,0); }