*/}}

la_tns_shape.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * LaGUI: A graphical application framework.
  3. * Copyright (C) 2022-2023 Wu Yiming
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "la_5.h"
  19. #include <math.h>
  20. extern LA MAIN;
  21. extern tnsMain *T;
  22. tnsShape* tnsNewShape(tnsShapeObject* so){
  23. tnsShape* s=memAcquireSimple(sizeof(tnsShape));
  24. lstAppendItem(&so->Shapes,s); return s;
  25. }
  26. tnsSPoint* tnsNewSPoint(tnsShape* s,real x, real y,real ldx,real ldy, real rdx,real rdy){
  27. tnsSPoint* sp=memAcquireSimple(sizeof(tnsSPoint));
  28. tnsVectorSet2(sp->p,x,y); tnsVectorSet2(sp->dl,ldx,ldy); tnsVectorSet2(sp->dr,rdx,rdy);
  29. lstAppendItem(&s->Points,sp); return sp;
  30. }
  31. void tnsShapeSetClosed(tnsShape* s, int closed, int toggle){
  32. if(toggle) tnsShapeSetClosed(s,!(s->flags&TNS_SHAPE_CLOSED),0);
  33. else{ if(closed) s->flags|=TNS_SHAPE_CLOSED; else s->flags&=(~TNS_SHAPE_CLOSED); }
  34. }
  35. void tnsShapeRefreshIndex(tnsShapeObject* so){
  36. u32bit i; for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){
  37. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->i=i; i++; }
  38. }
  39. }
  40. int tnsShapePointAnySelected(tnsShape* s){
  41. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(sp->flags&TNS_SPOINT_SELECTED) return 1; } return 0;
  42. }
  43. int tnsShapeAnySelected(tnsShapeObject* so){
  44. for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){ if(tnsShapePointAnySelected(s)) return 1; } return 0;
  45. }
  46. void tnsShapeDeselectAll(tnsShapeObject* so){
  47. for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){
  48. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags&=(~TNS_SPOINT_SELECTED); }
  49. }
  50. }
  51. void tnsShapeSelectAll(tnsShapeObject* so){
  52. for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){
  53. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ sp->flags|=TNS_SPOINT_SELECTED; }
  54. }
  55. }
  56. void tnsShapeSelectPoint(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle, int lr){
  57. if(!so) return; int flag=(lr==1)?TNS_SPOINT_SELECTED_L:(lr==2)?TNS_SPOINT_SELECTED_R:(TNS_SPOINT_SELECTED);
  58. if(!(sp->flags&TNS_SPOINT_BEZIER)){ flag=TNS_SPOINT_SELECTED; }
  59. if(toggle) tnsShapeSelectPoint(so,sp,((sp->flags&flag)==flag?0:1),0,lr);
  60. elif(select) sp->flags|=flag; else sp->flags&=(~flag);
  61. //if(!so->FirstSelectV) so->FirstSelectV=sp; so->LastSelectV=sp;
  62. }
  63. void tnsShapeSelectRingFrom(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle){
  64. int has_idle=0; laListHandle spl={sp->Item.pPrev,sp->Item.pNext};
  65. while(spl.pFirst && ((laListItem*)spl.pFirst)->pPrev){ spl.pFirst=((laListItem*)spl.pFirst)->pPrev; }
  66. while(spl.pLast && ((laListItem*)spl.pLast)->pNext){ spl.pLast=((laListItem*)spl.pLast)->pNext; }
  67. if(toggle){
  68. for(tnsSPoint* isp=spl.pFirst;isp;isp=isp->Item.pNext){
  69. if(isp!=sp && (!(isp->flags&TNS_SPOINT_SELECTED))) has_idle=1;
  70. if(has_idle){ break; }
  71. }
  72. if(has_idle){ select=1; }else{ select=0; }
  73. }
  74. for(tnsSPoint* isp=spl.pFirst;isp;isp=isp->Item.pNext){
  75. if(select){ isp->flags|=TNS_SPOINT_SELECTED; }else{ isp->flags&=(~TNS_SPOINT_SELECTED); }
  76. }
  77. }
  78. void tnsShapeEnterEditMode(tnsShapeObject* so){
  79. if(so->Mode==TNS_MESH_EDIT_MODE) return;
  80. so->Mode = TNS_MESH_EDIT_MODE;
  81. tnsInvalidateEvaluation(so);
  82. }
  83. void tnsShapeLeaveEditMode(tnsShapeObject* so){
  84. if(so->Mode==TNS_MESH_OBJECT_MODE) return;
  85. so->Mode = TNS_MESH_OBJECT_MODE;
  86. tnsInvalidateEvaluation(so);
  87. }
  88. tnsShapeObject* tnsDuplicateShapeObject(tnsShapeObject* from){
  89. if(from->Base.Type!=TNS_OBJECT_SHAPE) return 0;
  90. tnsShapeObject* to = memAcquireHyper(sizeof(tnsShapeObject));
  91. 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);
  92. tnsCopyObjectTransformationsLocal(to,from);
  93. tnsMaterialSlot* new_current=0;
  94. for(tnsMaterialSlot* ms=from->Materials.pFirst;ms;ms=ms->Item.pNext){
  95. tnsMaterialSlot* nms=tnsNewMaterialSlot(to); nms->Index=ms->Index;
  96. memAssignRef(nms,&nms->Material,ms->Material);
  97. if(ms==from->CurrentMaterial) new_current=nms;
  98. }
  99. memAssignRef(to,&to->CurrentMaterial,new_current);
  100. if(!from->Shapes.pFirst){ return to; }
  101. for(tnsShape* s=from->Shapes.pFirst;s;s=s->Item.pNext){
  102. tnsShape* ns=tnsNewShape(to); for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){
  103. 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;
  104. }
  105. ns->flags=s->flags;ns->mat=s->mat;
  106. }
  107. return to;
  108. }
  109. void tnsInitShapeSquare(tnsShapeObject* so, real size){
  110. tnsShape* s=tnsNewShape(so);
  111. tnsNewSPoint(s,-size,-size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED;
  112. tnsNewSPoint(s,-size,size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED;
  113. tnsNewSPoint(s,size,size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED;
  114. tnsNewSPoint(s,size,-size,0,0,0,0)->flags|=TNS_MESH_FLAG_SELECTED;
  115. tnsShapeSetClosed(s,1,0); tnsShapeRefreshIndex(so);
  116. }
  117. void tns_DrawShape(tnsShape* s, real* override_color, int DrawEdit, real PointScale){
  118. if(!s->Points.pFirst) return; int HasSelection=0; int closed=(s->flags&TNS_SHAPE_CLOSED);
  119. NVGcontext* vg=MAIN.CurrentWindow->nvg;
  120. if(DrawEdit==2){
  121. nvgShapeAntiAlias(vg,0);
  122. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ real color[4]={0,0,0,1};
  123. nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*1);
  124. TNS_ID_TO_COLOR(color,sp->i*3+1); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg);
  125. nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1],PointScale*1);
  126. TNS_ID_TO_COLOR(color,sp->i*3+2); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg);
  127. nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1],PointScale*1);
  128. TNS_ID_TO_COLOR(color,sp->i*3+3); nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg);
  129. }
  130. nvgShapeAntiAlias(vg,1);
  131. return;
  132. }
  133. nvgBeginPath(vg);
  134. tnsSPoint* sp1=s->Points.pFirst; nvgMoveTo(vg,sp1->p[0],sp1->p[1]); if(sp1->flags&TNS_SPOINT_SELECTED){HasSelection=1;}
  135. for(tnsSPoint* sp=sp1->Item.pNext;sp;sp=sp->Item.pNext){
  136. if(sp->flags&TNS_SPOINT_SELECTED){HasSelection=1;}
  137. if(sp->Item.pPrev){ tnsSPoint* np=sp->Item.pPrev;
  138. real c1[2],c2[2]; tnsVectorSet2v(c1,np->p); tnsVectorSet2v(c2,sp->p);
  139. if(np->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c1,np->dr); }
  140. if(sp->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c2,sp->dl); }
  141. nvgBezierTo(vg,c1[0],c1[1],c2[0],c2[1],sp->p[0],sp->p[1]);
  142. }else{
  143. nvgLineTo(vg,sp->p[0],sp->p[1]);
  144. }
  145. }
  146. if(closed && (sp1!=s->Points.pLast)){
  147. tnsSPoint* np=s->Points.pLast; tnsSPoint* sp=sp1;
  148. real c1[2],c2[2]; tnsVectorSet2v(c1,np->p); tnsVectorSet2v(c2,sp->p);
  149. if(np->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c1,np->dr); }
  150. if(sp->flags&TNS_SPOINT_BEZIER){ tnsVectorAccum2d(c2,sp->dl); }
  151. nvgBezierTo(vg,c1[0],c1[1],c2[0],c2[1],sp->p[0],sp->p[1]);
  152. nvgClosePath(vg);
  153. nvgFillColor(vg, override_color?nvgRGBAf(LA_COLOR4(override_color)):nvgRGBAf(0.8,0.8,0.8,1));
  154. nvgFill(vg);
  155. }
  156. if(DrawEdit || (!closed)){
  157. tnsVector4d color; tnsVectorCopy4d(laAccentColor(LA_BT_VERTEX),color);
  158. real* ActiveColor=laAccentColor(LA_BT_SVERTEX);
  159. if(!HasSelection) color[3]*=0.4;
  160. nvgStrokeColor(vg,override_color?nvgRGBAf(LA_COLOR4(override_color)):nvgRGBAf(LA_COLOR4(color)));
  161. nvgStrokeWidth(vg,PointScale*4);
  162. if(!DrawEdit){nvgShapeAntiAlias(vg,0);nvgStroke(vg);nvgShapeAntiAlias(vg,1);return;}
  163. nvgStroke(vg);
  164. for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ int mainsel=sp->flags&TNS_MESH_FLAG_SELECTED;
  165. if(sp->flags&(TNS_SPOINT_FREE|TNS_SPOINT_ALIGNED)){
  166. nvgStrokeWidth(vg,PointScale*2);
  167. nvgStrokeColor(vg,(mainsel||(sp->flags&TNS_SPOINT_SELECTED_L))?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color)));
  168. nvgBeginPath(vg); nvgMoveTo(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1]); nvgLineTo(vg,sp->p[0],sp->p[1]);
  169. nvgStroke(vg);
  170. nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dl[0],sp->p[1]+sp->dl[1],PointScale*5);
  171. nvgStroke(vg);
  172. nvgStrokeColor(vg,(mainsel||(sp->flags&TNS_SPOINT_SELECTED_R))?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color)));
  173. nvgBeginPath(vg); nvgMoveTo(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1]); nvgLineTo(vg,sp->p[0],sp->p[1]);
  174. nvgStroke(vg);
  175. nvgBeginPath(vg); nvgCircle(vg,sp->p[0]+sp->dr[0],sp->p[1]+sp->dr[1],PointScale*5);
  176. nvgStroke(vg);
  177. }
  178. nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*5);
  179. nvgFillColor(vg,mainsel?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color)));
  180. nvgFill(vg);
  181. }
  182. }
  183. }
  184. void tnsDrawShapeObjectShapes(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de, real* override_color, int DrawEdit){
  185. tnsShapeObject* so=ei->Object; NVGcontext* vg=MAIN.CurrentWindow->nvg;
  186. real sca[3]; tnsExtractScale44d(ei->Mat,sca);
  187. real rot[3]; tnsExtractXYZEuler44d(ei->Mat,rot);
  188. nvgSave(vg);
  189. if(de->Is3D){
  190. tnsVector4d pos; tnsApplyTransform44d(pos,de->mViewProj,&ei->Mat[12]);
  191. pos[0]/=pos[3]; pos[1]/=pos[3];
  192. nvgTranslate(vg,pos[0]*de->W/2,pos[1]*de->H/2);
  193. nvgScale(vg,LA_RH*sca[0],LA_RH*sca[1]); nvgRotate(vg,rot[2]);
  194. }else{
  195. nvgTranslate(vg,ei->Mat[12],-ei->Mat[13]);
  196. nvgScale(vg,sca[0],-sca[1]); nvgRotate(vg,rot[2]);
  197. }
  198. for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){
  199. tns_DrawShape(s,override_color,DrawEdit?DrawEdit:(so->Mode==TNS_MESH_EDIT_MODE),de->PointScale/sca[0]);
  200. }
  201. nvgRestore(vg);
  202. }
  203. void tnsDrawShapeObject(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){
  204. tnsDrawShapeObjectShapes(ei,de,0,0);
  205. }
  206. void tnsDrawShapeObjectSelectionID(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){
  207. int i=ei->InstanceSelectionID; real color[4]={0,0,0,1}; TNS_ID_TO_COLOR(color,i);
  208. tnsDrawShapeObjectShapes(ei,de,color,0);
  209. }
  210. void tnsDrawShapePointsSelectionID(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){
  211. tnsDrawShapeObjectShapes(ei,de,0,2);
  212. }
  213. void tnsDrawShapeObjectOverlay(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){
  214. int i=ei->InstanceSelectionID; real color[4]; tnsVectorCopy4d(laAccentColor(LA_BT_ACTIVE),color);
  215. if(ei->Object!=ei->Object->InRoot->Active){ color[3]=0.4; }else{ color[3]=0.7; }
  216. tnsDrawShapeObjectShapes(ei,de,color,0);
  217. }
  218. void tnsEvaluateShapeObject(tnsShapeObject* so, tnsEvaluateData* ed){
  219. int DrawTo=TNS_EVAL_LAYER_SOLID;
  220. if(so->Backdrop){ DrawTo=TNS_EVAL_LAYER_BACKDROP; }
  221. tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObject,DrawTo,0,0,0);
  222. if(ed->FillSelectionID){
  223. tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObjectSelectionID,TNS_EVAL_LAYER_SELECTION,0,1,so->Base.SelectID);
  224. }
  225. if(ed->FillOutline && (!ed->OverrideID)){
  226. if((so->Base.Flags&TNS_OBJECT_FLAGS_SELECTED) && (so->Mode!=TNS_MESH_EDIT_MODE)){
  227. tnsAddEvaluatedInstance(ed,so,tnsDrawShapeObjectOverlay,TNS_EVAL_LAYER_OVERLAY,0,0,0);
  228. }
  229. }
  230. }
  231. tnsShapeObject *tnsCreateShapeEmpty(tnsObject *under, char *Name, real AtX, real AtY, real AtZ){
  232. tnsShapeObject *so = memAcquireHyper(sizeof(tnsShapeObject));
  233. tnsInitObjectBase(&so->Base, under, Name, TNS_OBJECT_SHAPE, AtX, AtY, AtZ, 0, 0, 0, 1.0f, TNS_ROTATION_XYZ_EULER, 1.0f);
  234. return so;
  235. }
  236. tnsShapeObject *tnsCreateShapeSquare(tnsObject *under, char *Name, real AtX, real AtY, real AtZ, real size){
  237. tnsShapeObject *so=tnsCreateShapeEmpty(under, Name, AtX, AtY, AtZ);
  238. tnsInitShapeSquare(so, size);
  239. return so;
  240. }
  241. int OPCHK_IsAnyPointSelected(laPropPack *This, laStringSplitor *ss){
  242. if(This && This->EndInstance){
  243. laCanvasExtra* ex=This->EndInstance; laUiItem* ui=ex->ParentUi;
  244. tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
  245. if(root->Active->Type!=TNS_OBJECT_SHAPE) return 0;
  246. tnsShapeObject* so=root->Active; return tnsShapeAnySelected(so);
  247. }
  248. return 0;
  249. }
  250. int OPINV_SetPointHandle(laOperator *a, laEvent *e){
  251. laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi;
  252. tnsObject*root=ui?ui->PP.EndInstance:0;
  253. if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED;
  254. tnsShapeObject* so=root->Active; int ran=0;
  255. char* mode=strGetArgumentString(a->ExtraInstructionsP,"mode");
  256. if(!mode){
  257. laEnableOperatorPanel(a,a->This,e->x,e->y,200,200,0,0,0,0,0,0,0,0,e); return LA_RUNNING;
  258. }
  259. int flags=0,HandleFlags=TNS_SPOINT_ALIGNED|TNS_SPOINT_FREE;
  260. if(strSame(mode,"ALIGNED")){ flags=TNS_SPOINT_ALIGNED; }
  261. elif(strSame(mode,"FREE")){ flags=TNS_SPOINT_FREE; }
  262. tnsVector2d pl={0,0},pr={0,0};
  263. for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){
  264. for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_MESH_FLAG_SELECTED)) continue;
  265. int oldf=sp->flags&HandleFlags, hasl=0,hasr=0;
  266. sp->flags&=(~HandleFlags); sp->flags|=flags; ran=1; if(!flags){ continue; }
  267. if(sp->Item.pPrev){ tnsVectorSet2v(pl,((tnsSPoint*)sp->Item.pPrev)->p); hasl=1;}
  268. if(sp->Item.pNext){ tnsVectorSet2v(pr,((tnsSPoint*)sp->Item.pNext)->p); hasr=1;}
  269. if(!hasl){
  270. if(!hasr){ tnsVectorSet2(pl,sp->p[0]-0.5,sp->p[1]); tnsVectorSet2(pr,sp->p[0]+0.5,sp->p[1]); }
  271. else{ tnsVectorSet2(pl,-pr[0]+2*sp->p[0],-pr[1]+2*sp->p[1]); }
  272. }else{
  273. if(!hasr){ tnsVectorSet2(pr,-pl[0]+2*sp->p[0],-pl[1]+2*sp->p[1]); }
  274. }
  275. if(flags==TNS_SPOINT_ALIGNED && flags!=oldf){
  276. tnsVector2d diff; tnsVectorMinus2d(diff,pr,pl); tnsVectorMultiSelf2d(diff,0.25);
  277. tnsVectorSet2v(sp->dr,diff); tnsVectorMultiSelf2d(diff,-1); tnsVectorSet2v(sp->dl,diff);
  278. }elif(flags==TNS_SPOINT_FREE && oldf!=TNS_SPOINT_ALIGNED){
  279. tnsVectorMinus2d(sp->dl,pl,sp->p); tnsVectorMultiSelf2d(sp->dl,0.3);
  280. tnsVectorMinus2d(sp->dr,pr,sp->p); tnsVectorMultiSelf2d(sp->dr,0.3);
  281. }
  282. }
  283. }
  284. if(ran){
  285. laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Set point handle",TNS_HINT_GEOMETRY);
  286. laNotifyUsers("tns.world");
  287. }
  288. return LA_FINISHED;
  289. }
  290. void laui_SetPointHandle(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
  291. laColumn* c=laFirstColumn(uil);
  292. laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=NONE;text=Normal",0,0);
  293. laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=ALIGNED;text=Aligned",0,0);
  294. laShowItemFull(uil,c,pp,"_this_M_set_point_handle",0,"mode=FREE;text=Free",0,0);
  295. }
  296. int OPINV_SetShapeClosed(laOperator *a, laEvent *e){
  297. laCanvasExtra* ex=a->This->EndInstance; laUiItem* ui=ex->ParentUi;
  298. tnsObject*root=ui?ui->PP.EndInstance:0;
  299. if(!root || !root->Active || root->Active->Type!=TNS_OBJECT_SHAPE) return LA_FINISHED;
  300. tnsShapeObject* so=root->Active; int ran=0;
  301. char* mode=strGetArgumentString(a->ExtraInstructionsP,"mode");
  302. int set=2; if(strSame(mode,"CLOSE")){ set=1; }elif(strSame(mode,"OPEN")){ set=0; }
  303. for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){
  304. for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ if(!(sp->flags&TNS_SPOINT_SELECTED)) continue;
  305. tnsShapeSetClosed(s,set,set==2); ran=1; break;
  306. }
  307. }
  308. if(ran){
  309. laRecordInstanceDifferences(so,"tns_shape_object"); laPushDifferences("Open/Close shape",TNS_HINT_GEOMETRY);
  310. laNotifyUsers("tns.world");
  311. }
  312. return LA_FINISHED;
  313. }
  314. void la_RegisterShapeOperators(){
  315. laPropContainer *pc; laProp *p;
  316. laOperatorType *at; laEnumProp *ep;
  317. 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);
  318. at->UiDefine=laui_SetPointHandle;
  319. at=laCreateOperatorType("M_set_shape_closed", "Set Shape Closed", "Set shape closed or open",OPCHK_IsAnyPointSelected,0,0,OPINV_SetShapeClosed,0,0,0);
  320. }