*/}}
Browse Source

2D shape Selection&G/S/R.

YimingWu 8 months ago
parent
commit
f4fa849cdc
7 changed files with 368 additions and 106 deletions
  1. 1 0
      la_interface.h
  2. 18 0
      la_tns.h
  3. 13 2
      la_tns_kernel.c
  4. 7 7
      la_tns_mesh.c
  5. 115 10
      la_tns_shape.c
  6. 210 83
      resources/la_modelling.c
  7. 4 4
      resources/la_widgets_viewers.c

+ 1 - 0
la_interface.h

@@ -894,6 +894,7 @@ STRUCTURE(la3DObjectDrawExtra){
     int MeshEditType;
     tnsMatrix44d mViewProj;
     int W,H,Is3D;
+    real PointScale;
 };
 
 typedef void(*laCanvasDrawFunc)(laBoxedTheme* bt, void* DataInstance, laCanvasExtra* Extra);

+ 18 - 0
la_tns.h

@@ -733,6 +733,7 @@ STRUCTURE(tnsSPoint){
     laListItem Item;
     tnsVector2d p,dl,dr;
     int flags;
+    u32bit i;
 };
 STRUCTURE(tnsShape){
     laListItem Item;
@@ -1115,7 +1116,9 @@ tnsShapeObject *tnsCreateShapeEmpty(tnsObject *under, char *Name, real AtX, real
 tnsShapeObject *tnsCreateShapeSquare(tnsObject *under, char *Name, real AtX, real AtY, real AtZ, real size);
 
 int tnsMergeMeshObjects(tnsMeshObject* into, tnsMeshObject* mo);
+tnsInstancer* tnsDuplicateInstancer(tnsInstancer* from);
 tnsMeshObject* tnsDuplicateMeshObject(tnsMeshObject* from);
+tnsShapeObject* tnsDuplicateShappeObject(tnsShapeObject* from);
 
 void tnsInitMeshPlane(tnsMeshObject* mo, real size);
 void tnsAddMMeshPlane(tnsMeshObject* mo, real size);
@@ -1180,6 +1183,21 @@ void tnsEnsureMeshBatch(tnsMeshObject* mo);
 void tnsEvaluateMeshObject(tnsMeshObject* mo, tnsEvaluateData* ed);
 void tnsEvaluateShapeObject(tnsShapeObject* so, tnsEvaluateData* ed);
 
+
+int tnsShapePointAnySelected(tnsShape* s);
+int tnsShapeAnySelected(tnsShapeObject* so);
+void tnsShapeDeselectAll(tnsShapeObject* so);
+void tnsShapeSelectAll(tnsShapeObject* so);
+void tnsShapeSelectPoint(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle);
+void tnsShapeEnterEditMode(tnsShapeObject* mo);
+void tnsShapeLeaveEditMode(tnsShapeObject* mo);
+void tnsShapeSelectRingFrom(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle);
+
+NEED_STRUCTURE(la3DObjectDrawExtra);
+
+void tnsDrawShapePointsSelectionID(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de);
+void tnsDrawShapeObject(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de);
+
 void la_RegisterModellingOperators();
 
 void tnsGetCameraProjection(tnsMatrix44d* mat, int w, int h, tnsCamera* Camera);

+ 13 - 2
la_tns_kernel.c

@@ -3531,8 +3531,11 @@ void tnsDestroyObject(tnsObject *o){
         if(mo->f){ for(int i=0;i<mo->totf;i++){ free(mo->f[i].loop); mo->f[i].loop=0; } arrFree(&mo->f, &mo->maxf); }
         mo->totv=mo->tote=mo->totf=0;
         tnsInvalidateMeshBatch(o);
-        tnsMaterialSlot* ms;
-        while(ms=lstPopItem(&mo->Materials)){ memLeave(ms); } memAssignRef(mo,&mo->CurrentMaterial,0);
+        tnsMaterialSlot* ms; while(ms=lstPopItem(&mo->Materials)){ memLeave(ms); } memAssignRef(mo,&mo->CurrentMaterial,0);
+    }
+    if(o->Type==TNS_OBJECT_SHAPE){ tnsShapeObject* so=o;
+        tnsShape* s; while(s=lstPopItem(&so->Shapes)){ tnsSPoint* sp; while(sp=lstPopItem(&s->Points)){ memLeave(sp); } memLeave(s); }
+        tnsMaterialSlot* ms; while(ms=lstPopItem(&so->Materials)){ memLeave(ms); } memAssignRef(so,&so->CurrentMaterial,0);
     }
 
     strSafeDestroy(&o->Name);
@@ -3580,6 +3583,14 @@ tnsLight *tnsCreateLight(tnsObject *under, char *Name, real AtX, real AtY, real
     return l;
 }
 
+tnsInstancer* tnsDuplicateInstancer(tnsInstancer* from){
+    if(from->Base.Type!=TNS_OBJECT_INSTANCER) return 0; tnsInstancer* to = memAcquireHyper(sizeof(tnsInstancer));
+    tnsInitObjectBase(to, from->Base.ParentObject?from->Base.ParentObject:from->Base.InRoot, from->Base.Name->Ptr, TNS_OBJECT_INSTANCER,0,0,0,0,0,0,0,0,1);
+    tnsCopyObjectTransformationsLocal(to,from);
+    to->Instance=from->Instance;
+    return to;
+}
+
 int tnsSizeOfObject(tnsObject* o){
     switch(o->Type){
     case TNS_OBJECT_ROOT: return sizeof(tnsRootObject);

+ 7 - 7
la_tns_mesh.c

@@ -119,6 +119,13 @@ tnsMeshObject* tnsDuplicateMeshObject(tnsMeshObject* from){
     tnsMeshObject* to = memAcquireHyper(sizeof(tnsMeshObject));
     tnsInitObjectBase(to, from->Base.ParentObject?from->Base.ParentObject:from->Base.InRoot, from->Base.Name->Ptr, TNS_OBJECT_MESH,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->totv){ return to; }
     to->totv=from->totv; to->tote=from->tote; to->totf=from->totf;
     arrInitLength(&to->v, to->totv, &to->maxv, sizeof(tnsVert)); if(from->totv) memcpy(to->v,from->v,sizeof(tnsVert)*from->totv);
@@ -127,13 +134,6 @@ tnsMeshObject* tnsDuplicateMeshObject(tnsMeshObject* from){
     for(int i=0;i<to->totf;i++){ to->f[i].loop=0; arrInitLength(&to->f[i].loop, to->f[i].looplen, &to->f[i].looplen, sizeof(int));
         for(int l=0;l<to->f[i].looplen;l++){ to->f[i].loop[l]=from->f[i].loop[l]; } to->f[i].mat=from->f[i].mat;
     }
-    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);
     return to;
 }
 

+ 115 - 10
la_tns_shape.c

@@ -32,25 +32,125 @@ tnsSPoint* tnsNewSPoint(tnsShape* s,real x, real y,real ldx,real ldy, real rdx,r
     lstAppendItem(&s->Points,sp); return sp;
 }
 
+void tnsShapeRefreshIndex(tnsShapeObject* so){
+    u32bit i; for(tnsShape* s=so->Shapes.pFirst;s;s=s->Item.pNext){
+        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_MESH_FLAG_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_MESH_FLAG_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_MESH_FLAG_SELECTED; }
+    }
+}
+void tnsShapeSelectPoint(tnsShapeObject* so, tnsSPoint* sp, int select, int toggle){
+    if(!so) return;
+    if(toggle) tnsShapeSelectPoint(so,sp,(sp->flags&TNS_MESH_FLAG_SELECTED?0:1),0);
+    elif(select) sp->flags|=TNS_MESH_FLAG_SELECTED; else sp->flags&=(~TNS_MESH_FLAG_SELECTED);
+    //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_MESH_FLAG_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_MESH_FLAG_SELECTED; }else{ isp->flags&=(~TNS_MESH_FLAG_SELECTED); }
+    }
+}
+
+void tnsShapeEnterEditMode(tnsShapeObject* so){
+    if(so->Mode==TNS_MESH_EDIT_MODE) return;
+    so->Mode = TNS_MESH_EDIT_MODE;
+    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;
+    tnsShapeRefreshIndex(so);
 }
 
-void tns_DrawShape(tnsShape* s, real* override_color){
-    if(!s->Points.pFirst) return;
-    NVGcontext* vg=MAIN.CurrentWindow->nvg; nvgBeginPath(vg);
-    tnsSPoint* sp1=s->Points.pFirst; nvgMoveTo(vg,sp1->p[0],sp1->p[1]);
+void tns_DrawShape(tnsShape* s, real* override_color, int DrawEdit, real PointScale){
+    if(!s->Points.pFirst) return; int HasSelection=0;
+    NVGcontext* vg=MAIN.CurrentWindow->nvg;
+    if(DrawEdit==2){
+        nvgShapeAntiAlias(vg,0);
+        for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*1);
+            real color[4]={0,0,0,1}; TNS_ID_TO_COLOR(color,sp->i+1);
+            nvgFillColor(vg,nvgRGBAf(LA_COLOR4(color))); nvgFill(vg);
+        }
+        nvgShapeAntiAlias(vg,1);
+        return;
+    }
+    nvgBeginPath(vg);
+    tnsSPoint* sp1=s->Points.pFirst; nvgMoveTo(vg,sp1->p[0],sp1->p[1]); if(sp1->flags&TNS_MESH_FLAG_SELECTED){HasSelection=1;}
     for(tnsSPoint* sp=sp1->Item.pNext;sp;sp=sp->Item.pNext){
-        nvgLineTo(vg,sp->p[0],sp->p[1]);
+        nvgLineTo(vg,sp->p[0],sp->p[1]); if(sp->flags&TNS_MESH_FLAG_SELECTED){HasSelection=1;}
     }
+    nvgClosePath(vg);
 	nvgFillColor(vg, override_color?nvgRGBAf(LA_COLOR4(override_color)):nvgRGBAf(0.8,0.8,0.8,1));
 	nvgFill(vg);
+    if(DrawEdit){
+        tnsVector4d color; tnsVectorCopy4d(laAccentColor(LA_BT_VERTEX),color);
+        real* ActiveColor=laAccentColor(LA_BT_SVERTEX);
+        if(!HasSelection) color[3]*=0.4;
+        nvgStrokeColor(vg,nvgRGBAf(LA_COLOR4(color)));
+        nvgStrokeWidth(vg,PointScale*4); nvgStroke(vg);
+        for(tnsSPoint* sp=s->Points.pFirst;sp;sp=sp->Item.pNext){ nvgBeginPath(vg); nvgCircle(vg,sp->p[0],sp->p[1],PointScale*5);
+            nvgFillColor(vg,sp->flags&TNS_MESH_FLAG_SELECTED?nvgRGBAf(LA_COLOR4(ActiveColor)):nvgRGBAf(LA_COLOR4(color)));
+            nvgFill(vg);
+        }
+    }
 }
-void tnsDrawShapeObjectShapes(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de, real* override_color){
+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);
@@ -64,20 +164,25 @@ void tnsDrawShapeObjectShapes(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de,
         nvgTranslate(vg,ei->Mat[12],-ei->Mat[13]);
         nvgScale(vg,sca[0],-sca[1]); nvgRotate(vg,rot[2]);
     }
-    for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){ tns_DrawShape(s,override_color); }
+    for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){
+        tns_DrawShape(s,override_color,DrawEdit?DrawEdit:(so->Mode==TNS_MESH_EDIT_MODE),de->PointScale/sca[0]);
+    }
     nvgRestore(vg);
 }
 void tnsDrawShapeObject(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){
-    tnsDrawShapeObjectShapes(ei,de,0);
+    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);
+    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);
+    tnsDrawShapeObjectShapes(ei,de,color,0);
 }
 
 void tnsEvaluateShapeObject(tnsShapeObject* so, tnsEvaluateData* ed){

+ 210 - 83
resources/la_modelling.c

@@ -76,10 +76,16 @@ int OPINV_ToggleEdit(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-    tnsMeshObject* mo=root->Active; if(!mo) return 0;
-    if(mo->Base.Type!=TNS_OBJECT_MESH) return LA_CANCELED;
-    if(mo->Mode==TNS_MESH_EDIT_MODE) tnsMeshLeaveEditMode(mo); else tnsMeshEnterEditMode(mo);
-    laRecordInstanceDifferences(mo, "tns_mesh_object"); laPushDifferences("Toggle edit mode", TNS_HINT_GEOMETRY);
+    tnsObject* o=root->Active; if(!o) return 0;
+    char* ObType=0;
+    if(o->Type==TNS_OBJECT_MESH){ tnsMeshObject* mo=o; ObType="tns_mesh_object";
+        if(mo->Mode==TNS_MESH_EDIT_MODE) tnsMeshLeaveEditMode(mo); else tnsMeshEnterEditMode(mo);
+    }elif(o->Type==TNS_OBJECT_SHAPE){ tnsShapeObject* so=o; ObType="tns_shape_object";
+        if(so->Mode==TNS_MESH_EDIT_MODE) tnsShapeLeaveEditMode(so); else tnsShapeEnterEditMode(so);
+    }else{
+        return LA_CANCELED;
+    }
+    laRecordInstanceDifferences(o, ObType); laPushDifferences("Toggle edit mode", TNS_HINT_GEOMETRY);
     laNotifyUsers("tns.world");
     return LA_FINISHED_PASS;
 }
@@ -119,9 +125,9 @@ void la_PopulateSelectDataObjects(MSelectData* sd, tnsObject* root, laCanvasExtr
     tnsViewportWithScissor(0,0,w,h);tnsResetViewMatrix();tnsResetModelMatrix();tnsResetProjectionMatrix();
     
     NVGcontext* vg=MAIN.CurrentWindow->nvg;
-    la3DObjectDrawExtra de={0}; de.W=w; de.H=h;
+    la3DObjectDrawExtra de={0}; de.W=w; de.H=h; de.PointScale=1.0f/LA_RH;
     tnsMatrix44d mview,mproj; tnsRootObject* ro=root;
-    if(ro && ro->Is2D){ la_SetCanvasOrtho(e,w,h); }
+    if(ro && ro->Is2D){ la_SetCanvasOrtho(e,w,h); de.PointScale=e->ZoomX; }
     else{ tnsApplyCameraView(w, h, camera, mview,mproj); tnsMultiply44d(de.mViewProj,mproj,mview); de.Is3D=1; }
     la_BeginNVG(vg,e,w,h,ro->Is2D);
 
@@ -136,7 +142,7 @@ void la_PopulateSelectVerts(MSelectData* sd, tnsMeshObject* mo){
     arrEnsureLength(&sd->RefsV,0,&sd->maxV,sizeof(tnsMVert*));
     if(mo->Base.Type!=TNS_OBJECT_MESH||!mo->mv.pFirst){ return; }
     for(tnsMVert* v=mo->mv.pFirst;v;v=v->Item.pNext){
-        arrEnsureLength(&sd->RefsV, v->i, &sd->maxV, sizeof(tnsObject*));
+        arrEnsureLength(&sd->RefsV, v->i, &sd->maxV, sizeof(tnsMVert*));
         sd->RefsV[v->i]=v; sd->nextV=TNS_MAX2(v->i, sd->nextV);
     }
     sd->nextV++;
@@ -150,32 +156,63 @@ void la_PopulateSelectEdges(MSelectData* sd, tnsMeshObject* mo){
     }
     sd->nextE++;
 }
-void la_PopulateSelectDataPrimitives(MSelectData* sd, tnsMeshObject* mo, tnsCamera* camera, int WhatPrim, int SelectThrough){
+void la_PopulateSelectPoints(MSelectData* sd, tnsShapeObject* so){
+    sd->nextV=0;
+    arrEnsureLength(&sd->RefsV,sd->nextV,&sd->maxV,sizeof(tnsSPoint*));
+    if(so->Base.Type!=TNS_OBJECT_SHAPE||!so->Shapes.pFirst){ return; }
+    for(tnsShape*s=so->Shapes.pFirst;s;s=s->Item.pNext){
+        for(tnsSPoint*sp=s->Points.pFirst;sp;sp=sp->Item.pNext){
+            arrEnsureLength(&sd->RefsV, sd->nextV, &sd->maxV, sizeof(tnsSPoint*));
+            sd->RefsV[sd->nextV]=sp; sd->nextV++;
+        }
+    }
+    sd->nextV++;
+}
+void la_PopulateSelectDataPrimitives(MSelectData* sd, tnsObject* o, tnsCamera* camera, int WhatPrim, tnsObject* root, laCanvasExtra* e){
     int DoVerts=(WhatPrim==LA_CANVAS_SELECT_MODE_VERTS),DoEdges=(WhatPrim==LA_CANVAS_SELECT_MODE_EDGES);
     int Knife=(WhatPrim==LA_CANVAS_SELECT_MODE_KNIFE);
-    if(DoVerts || Knife){ la_PopulateSelectVerts(sd,mo); }
-    if(DoEdges || Knife){ la_PopulateSelectEdges(sd,mo); }
+    tnsMeshObject* mo=o; tnsShapeObject* so=o;
+    if(o->Type==TNS_OBJECT_MESH){
+        if(DoVerts || Knife){ la_PopulateSelectVerts(sd,mo); }
+        if(DoEdges || Knife){ la_PopulateSelectEdges(sd,mo); }
+    }elif(o->Type==TNS_OBJECT_SHAPE){
+        la_PopulateSelectPoints(sd,so);
+    }
     int w=sd->Color->Width, h=sd->Color->Height;
     tnsUnbindTexture(); tnsUniformUseTexture(T->immShader,0,0); tnsUseMultiplyColor(0);
     tnsEnableShaderv(T->SelectionShader);
     tnsViewportWithScissor(0,0,w,h);tnsResetViewMatrix();tnsResetModelMatrix();tnsResetProjectionMatrix();
-    tnsApplyCameraView(w,h,camera,0,0);
+
+    NVGcontext* vg=MAIN.CurrentWindow->nvg;
+    la3DObjectDrawExtra de={0}; de.W=w; de.H=h; de.PointScale=1.0f/LA_RH;
+    tnsMatrix44d mview,mproj; tnsRootObject* ro=root;
+    if(ro && ro->Is2D){ la_SetCanvasOrtho(e,w,h); de.PointScale=e->ZoomX; }
+    else{ tnsApplyCameraView(w, h, camera, mview,mproj); tnsMultiply44d(de.mViewProj,mproj,mview); de.Is3D=1; }
+    la_BeginNVG(vg,e,w,h,ro->Is2D);
+    
     glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-    tnsPushMatrix(); tnsApplyModelMatrix(mo->Base.GlobalTransform);glEnable(GL_DEPTH_TEST);
-    if(!SelectThrough){
-        glDepthMask(GL_TRUE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-        tnsUniformUseOffset(T->SelectionShader,0);
-        tnsDrawBatch(mo->Batch,"body",0,0);
-        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+    tnsPushMatrix(); tnsApplyModelMatrix(o->GlobalTransform);glEnable(GL_DEPTH_TEST);
+    if(o->Type==TNS_OBJECT_MESH){
+        if(!e->SelectThrough){
+            glDepthMask(GL_TRUE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+            tnsUniformUseOffset(T->SelectionShader,0);
+            tnsDrawBatch(mo->Batch,"body",0,0);
+            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+        }
     }
     glDepthMask(GL_FALSE);
     tnsUniformUseOffset(T->SelectionShader,1);
-    if(DoEdges || Knife){ tnsDrawBatch(mo->Batch, "edges_select",0,0); }
-    if(DoVerts || Knife){ tnsDrawBatch(mo->Batch, "verts_select",0,0); }
+    if(o->Type==TNS_OBJECT_MESH){
+        if(DoEdges || Knife){ tnsDrawBatch(mo->Batch, "edges_select",0,0); }
+        if(DoVerts || Knife){ tnsDrawBatch(mo->Batch, "verts_select",0,0); }
+    }elif(o->Type==TNS_OBJECT_SHAPE){
+        tnsEvaluatedInstance ei; memcpy(&ei.Mat,o->GlobalTransform,sizeof(tnsMatrix44d)); ei.Object=o;
+        tnsDrawShapePointsSelectionID(&ei,&de);
+    }
     tnsUniformUseOffset(T->SelectionShader,0);
     tnsPopMatrix();
     glDisable(GL_DEPTH_TEST);
-    tnsEnableShaderv(T->immShader);
+    nvgEndFrame(vg); tnsRestoreFromNanoVG(); tnsEnableShaderv(T->immShader);
 }
 void la_PadSelectionBuffer(uint8_t* buf, int w, int h, int sx, int sy, int ex, int ey, int real_endx){
     if(!sx&&!sy&&!ex&&!ey) return;
@@ -289,6 +326,9 @@ void la_DoMeshSelect(tnsMeshObject* mo, void* p, int WhatPrim, int DeselectAll,
     if(p){ if(WhatPrim==LA_CANVAS_SELECT_MODE_VERTS) tnsMMeshSelectVert(mo,p,Select,Toggle);
         elif(WhatPrim==LA_CANVAS_SELECT_MODE_EDGES) tnsMMeshSelectEdge(mo,p,Select,Toggle);  }
 }
+void la_DoShapeSelect(tnsShapeObject* so, void* p, int DeselectAll, int Select, int Toggle){
+    if(DeselectAll){ tnsShapeDeselectAll(so); } if(p) tnsShapeSelectPoint(so,p,Select,Toggle);
+}
 
 #define LA_SELECT_MODE_BOX 1
 STRUCTURE(MSelectExtra){
@@ -308,13 +348,16 @@ int OPINV_Select(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-    tnsMeshObject* mo=root->Active;
+    tnsObject* o=root->Active; tnsMeshObject* mo=o; tnsShapeObject* so=o;
 
     int is_geo=0, SelectMode=ex->SelectMode, ring_band=0;
     if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "toggle")){
-        if(mo && mo->Base.Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
+        if(o && o->Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
             if(tnsMMeshAnySelected(mo)) tnsMMeshDeselectAll(mo); else tnsMMeshSelectAll(mo);
             tnsInvalidateMeshBatch(mo); is_geo=1;
+        }elif(o && o->Type==TNS_OBJECT_SHAPE && so->Mode==TNS_MESH_EDIT_MODE){ is_geo=1;
+            if(tnsShapeAnySelected(so)) tnsShapeDeselectAll(so); else tnsShapeSelectAll(so);
+            //tnsInvalidateMeshBatch(mo);
         }else{
             if(tnsAnyObjectsSelected(root)) tnsDeselectAllObjects(root); else tnsSelectAllObjects(root);
         }
@@ -330,8 +373,8 @@ int OPINV_Select(laOperator *a, laEvent *e){
     int DeselectAll=1;
     int Append=e->SpecialKeyBit&LA_KEY_SHIFT; if(Append) DeselectAll=0;
     
-    if(mo && mo->Base.Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
-        la_PopulateSelectDataPrimitives(sd, mo, ex->ViewingCamera, SelectMode, ex->SelectThrough);
+    if(o&&o->Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
+        la_PopulateSelectDataPrimitives(sd, mo, ex->ViewingCamera, SelectMode, root, ex);
         if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "box")){
             MSelectExtra* se=memAcquire(sizeof(MSelectExtra));
             ex->OnX=e->x; ex->OnX=e->y;
@@ -345,6 +388,18 @@ int OPINV_Select(laOperator *a, laEvent *e){
         }
         tnsInvalidateMeshBatch(mo);
         laNotifyUsers("tns.world"); laRecordAndPush(0,"tns.world","Mesh selection",TNS_HINT_GEOMETRY);
+    }elif(o&&o->Type==TNS_OBJECT_SHAPE && so->Mode==TNS_MESH_EDIT_MODE){
+        la_PopulateSelectDataPrimitives(sd, mo, ex->ViewingCamera, SelectMode, root, ex);
+        if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "box")){
+            MSelectExtra* se=memAcquire(sizeof(MSelectExtra));
+            ex->OnX=e->x; ex->OnX=e->y;
+            a->CustomData=se; se->sd=sd; se->Mode=LA_SELECT_MODE_BOX; se->mo=mo; se->cam=c; ex->DrawCursor=1; se->root=root; return LA_RUNNING;
+        }
+        int elemtype,id=la_SelectGetClosest(sd, e->x-ui->L, e->y-ui->U, LA_RH,&elemtype)-1;
+        void* p=la_SelectGetRef(sd,id,elemtype);
+        la_DoShapeSelect(mo, p, DeselectAll, 1, 1);
+        if(ring_band && p){ tnsShapeSelectRingFrom(mo,p,1,Append); }
+        laNotifyUsers("tns.world"); laRecordAndPush(0,"tns.world","Shape selection",TNS_HINT_GEOMETRY);
     }else{
         la_PopulateSelectDataObjects(sd,root,ex);
         if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "box")){
@@ -366,7 +421,7 @@ int OPMOD_Select(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-    tnsMeshObject* mo=root->Active;
+    tnsObject* o=root->Active; tnsMeshObject* mo=o; tnsShapeObject* so=o;
     MSelectExtra* se=a->CustomData;
 
     if(e->Type==LA_L_MOUSE_DOWN){ se->InSelect=1; ex->DrawCursor=2; ex->ClickedX=e->x; ex->ClickedY=e->y; laRedrawCurrentPanel(); }
@@ -381,7 +436,7 @@ int OPMOD_Select(laOperator *a, laEvent *e){
 
     int is_geo=0;
     if(se->InSelect && e->Type==LA_L_MOUSE_UP){
-        if(mo && mo->Base.Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
+        if(o && o->Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){ is_geo=1;
             la_DoMeshSelect(mo, 0, ex->SelectMode, DeselectAll, 0, 0);
             int len; int* ids=la_SelectGetBox(se->sd, ex->ClickedX-ui->L, ex->ClickedY-ui->U, e->x-ui->L, e->y-ui->U, &len);
             for(int i=0;i<len;i++){
@@ -389,7 +444,14 @@ int OPMOD_Select(laOperator *a, laEvent *e){
                 la_DoMeshSelect(mo, p, ex->SelectMode, 0, !Remove, 0);
             }
             tnsMMeshEnsureSelection(mo,ex->SelectMode);
-            tnsInvalidateMeshBatch(mo); is_geo=1;
+            tnsInvalidateMeshBatch(mo); 
+        }elif(o && o->Type==TNS_OBJECT_SHAPE && so->Mode==TNS_MESH_EDIT_MODE){ is_geo=1;
+            la_DoShapeSelect(so, 0, DeselectAll, 0, 0);
+            int len; int* ids=la_SelectGetBox(se->sd, ex->ClickedX-ui->L, ex->ClickedY-ui->U, e->x-ui->L, e->y-ui->U, &len);
+            for(int i=0;i<len;i++){
+                int id=ids[i]-1; void* p=la_SelectGetRef(se->sd,id,ex->SelectMode==LA_CANVAS_SELECT_MODE_EDGES?TNS_MMESH_EDGE_BIT:0);
+                la_DoShapeSelect(so, p, 0, !Remove, 0);
+            }
         }else{
             la_DoObjectSelect(se->root, 0, ex, DeselectAll, 0, 0);
             int len; int* ids=la_SelectGetBox(se->sd, ex->ClickedX, ex->ClickedY, e->x-ui->L, e->y-ui->U, &len);
@@ -428,13 +490,19 @@ STRUCTURE(MTOrigMVert){
     tnsVector3d origp;
     tnsMVert* mv;
 };
+STRUCTURE(MTOrigSPoint){
+    tnsVector2d p;
+    tnsVector2d origp;
+    tnsSPoint* sp;
+};
 STRUCTURE(MTransformData){
     tnsMatrix44d Delta;
     tnsMatrix44d ViewProjection;
     tnsVector4d Up,Right,Foward;
     tnsVector4d TCenter,TLCenter;
     int CenterX,CenterY; real Initial;
-    tnsObject* mo; tnsMatrix44d obmatinv,deltainv;
+    tnsObject* mo; tnsShapeObject* so;
+    tnsMatrix44d obmatinv,deltainv;
     tnsCamera* c; tnsObject* root;
     int w,h;
     void* Originals; int next,max;
@@ -511,10 +579,32 @@ int la_PopulateTransformVerticies(MTransformData* td, tnsMeshObject* mo){
     la_GetTransformCenter2D(td);
     return any;
 }
+int la_PopulateTransformPoints(MTransformData* td, tnsShapeObject* so){
+    int any=0; td->so=so; td->next=0;
+    arrEnsureLength(&td->Originals, 0, &td->max, sizeof(MTOrigSPoint));
+    tnsInverse44d(td->obmatinv, so->Base.GlobalTransform);
+    tnsInverse44d(td->deltainv, so->Base.DeltaTransform);
+    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;
+            arrEnsureLength(&td->Originals, td->next, &td->max, sizeof(MTOrigSPoint));
+            MTOrigSPoint* to=arrElement(td->Originals, td->next, sizeof(MTOrigSPoint)); td->next++; to->sp=sp;
+            tnsVector3d tp; tnsVector3d fp={sp->p[0],sp->p[1],0};
+            tnsApplyTransform43d(tp, so->Base.GlobalTransform, fp);
+            tnsVectorSet2v(to->p,tp); tnsVectorSet2v(to->origp,sp->p); any++;
+            tnsVectorAccum2d(td->TCenter,to->p);
+            tnsVectorAccum2d(td->TLCenter,sp->p);
+        }
+    }
+    tnsVectorMultiSelf2d(td->TCenter, 1.0f/any);
+    tnsVectorMultiSelf2d(td->TLCenter, 1.0f/any);
+    la_GetTransformCenter2D(td);
+    return any;
+}
 void la_ApplyTranslation(MTransformData* td, int x, int y){
     tnsMatrix44d trans; tnsVector3d deltay,delta; tnsVector3d gp; tnsVector3d use_delta;
-    if(td->Is2D){
-        use_delta[0]=x*td->zoomx; use_delta[1]=-y*td->zoomy; use_delta[2]=0;
+    if(td->Is2D || td->so){
+        use_delta[0]=x*(td->so?(1.0f/LA_RH):td->zoomx); use_delta[1]=-y*(td->so?(1.0f/LA_RH):td->zoomy); use_delta[2]=0;
         if(td->LockAxis[0]){ use_delta[1]=0; } if(td->LockAxis[1]){ use_delta[0]=0; }
     }else{
         tnsVectorMulti3d(delta, td->Right, x); tnsVectorMulti3d(deltay, td->Up, y); tnsVectorAccum3d(delta, deltay);
@@ -534,7 +624,23 @@ void la_ApplyTranslation(MTransformData* td, int x, int y){
         real dir=tnsDot3d(use_delta, lock, 0); tnsVectorMultiSelf3d(use_delta,(td->UserDeltaVal*dir<=0)?-1:1);
         td->DeltaVal=td->UserDeltaVal; 
     }
-    if(!td->mo){
+    if(td->mo){
+        tnsMakeTranslationMatrix44d(trans, LA_COLOR3(use_delta)); tnsMatrix44d final; 
+        if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,trans);
+        else tnsMultiply44d(final,trans,td->obmatinv);
+        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert));
+            tnsApplyTransform43d(to->mv->p, final, to->p);
+        }
+        tnsInvalidateMeshBatch(td->mo); tnsMMeshCalculateNormal(td->mo);
+    }elif(td->so){
+        tnsMakeTranslationMatrix44d(trans, LA_COLOR3(use_delta)); tnsMatrix44d final; 
+        if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,trans);
+        else tnsMultiply44d(final,trans,td->obmatinv);
+        for(int i=0;i<td->next;i++){ MTOrigSPoint* to=arrElement(td->Originals, i, sizeof(MTOrigSPoint));
+            tnsVector3d fp={to->p[0],to->p[1],0}, tp;
+            tnsApplyTransform43d(tp, final, fp); tnsVectorSet3v(to->sp->p,tp);
+        }
+    }else{
         for(int i=0;i<td->next;i++){
             MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject)); memcpy(to->o->GlobalTransform, to->Global,sizeof(tnsMatrix44d));
             if(to->Discard){ tnsSelfMatrixChanged(to->o,1); continue; }
@@ -547,30 +653,15 @@ void la_ApplyTranslation(MTransformData* td, int x, int y){
                 else tnsMoveObjectGlobal(to->o, LA_COLOR3(use_delta));
             }
         }
-    }else{
-        tnsMakeTranslationMatrix44d(trans, LA_COLOR3(use_delta)); tnsMatrix44d final; 
-        if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,trans);
-        else tnsMultiply44d(final,trans,td->obmatinv);
-        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert));
-            tnsApplyTransform43d(to->mv->p, final, to->p);
-        }
-        tnsInvalidateMeshBatch(td->mo); tnsMMeshCalculateNormal(td->mo);
     }
 }
 void la_ApplyScale(MTransformData* td, int uix, int uiy){
     tnsMatrix44d trans; real d=tnsDistIdv2(uix,uiy,td->CenterX,td->CenterY); if(!td->Initial){ td->Initial=100; }
     real s=d/td->Initial; tnsVector3d gp;
     td->DeltaVal=s; if(td->UseUserDelta) td->DeltaVal=s=td->UserDeltaVal;
-    if(!td->mo){
-        for(int i=0;i<td->next;i++){
-            MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject)); memcpy(to->o->GlobalTransform, to->Global,sizeof(tnsMatrix44d));
-            if(to->Discard){ tnsSelfMatrixChanged(to->o,1); continue; }
-            if(td->CanvasDeltaMode) tnsGlobalMatrixChangedForDelta(to->o, 0); else tnsGlobalMatrixChanged(to->o, 0);
-            if(td->CanvasDeltaMode) tnsScaleObjectDelta(to->o,s,s,s,LA_COLOR3(td->TCenter));
-            else tnsScaleObject(to->o, s,s,s, LA_COLOR3(td->TCenter));
-        }
-    }else{
-        tnsVector3d use_delta={s,s,s};
+    tnsMatrix44d t1,t2,t3,final; 
+    if(td->mo || td->so){
+        tnsVector3d use_delta={s,s,s};\
         if(td->LockAxis[0]>0){ use_delta[1]=use_delta[2]=1;}
         if(td->LockAxis[1]>0){ use_delta[0]=use_delta[2]=1;}
         if(td->LockAxis[2]>0){ use_delta[0]=use_delta[1]=1;}
@@ -578,26 +669,59 @@ void la_ApplyScale(MTransformData* td, int uix, int uiy){
         if(td->LockAxis[1]<0){ use_delta[1]=1; }
         if(td->LockAxis[2]<0){ use_delta[2]=1; }
         tnsMakeScaleMatrix44d(trans,LA_COLOR3(use_delta));
-        tnsMatrix44d t1,t2,t3; tnsMakeTranslationMatrix44d(t1,LA_COLOR3(td->TCenter)); tnsInverse44d(t2,t1);
-        tnsMatrix44d final; tnsMultiply44d(t3,t1,trans); tnsMultiply44d(t1,t3,t2);
+        tnsMakeTranslationMatrix44d(t1,LA_COLOR3(td->TCenter)); tnsInverse44d(t2,t1);
+        tnsMultiply44d(t3,t1,trans); tnsMultiply44d(t1,t3,t2);
         if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,t1);
         else tnsMultiply44d(final,t1,td->obmatinv);
+    }
+    if(td->mo){
         for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert));
             tnsApplyTransform43d(to->mv->p, final, to->p);
         }
         tnsInvalidateMeshBatch(td->mo); tnsMMeshCalculateNormal(td->mo);
+    }elif(td->so){
+        for(int i=0;i<td->next;i++){ MTOrigSPoint* to=arrElement(td->Originals, i, sizeof(MTOrigSPoint));
+            tnsVector3d fp={to->p[0],to->p[1],0}, tp;
+            tnsApplyTransform43d(tp, final, fp); tnsVectorSet3v(to->sp->p,tp);
+        }
+    }else{
+        for(int i=0;i<td->next;i++){
+            MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject)); memcpy(to->o->GlobalTransform, to->Global,sizeof(tnsMatrix44d));
+            if(to->Discard){ tnsSelfMatrixChanged(to->o,1); continue; }
+            if(td->CanvasDeltaMode) tnsGlobalMatrixChangedForDelta(to->o, 0); else tnsGlobalMatrixChanged(to->o, 0);
+            if(td->CanvasDeltaMode) tnsScaleObjectDelta(to->o,s,s,s,LA_COLOR3(td->TCenter));
+            else tnsScaleObject(to->o, s,s,s, LA_COLOR3(td->TCenter));
+        }
     }
 }
 void la_ApplyRotation(MTransformData* td, int uix, int uiy){
     tnsMatrix44d trans; real a=atan2(uiy-td->CenterY,uix-td->CenterX);
     real angle=a-td->Initial; tnsVector3d gp; tnsVector3d LimFoward={0}; real* use_forward=td->Foward;
-    if(td->Is2D){ use_forward=LimFoward; LimFoward[2]=1; }
+    tnsMatrix44d t1,t2,t3,final;
+    if(td->Is2D || td->so){ use_forward=LimFoward; LimFoward[2]=1; }
     if(td->LockAxis[0]||td->LockAxis[1]||td->LockAxis[2]){ use_forward=LimFoward; }
     if(td->LockAxis[0]){ LimFoward[0]=1; LimFoward[2]=0; }
     if(td->LockAxis[1]){ LimFoward[1]=1; LimFoward[2]=0; }
     if(td->LockAxis[2]){ LimFoward[2]=1; }
     if(td->UseUserDelta) angle=rad(td->UserDeltaVal); td->DeltaVal=deg(angle);
-    if(!td->mo){
+    if(td->mo||td->so){
+        tnsMakeRotationMatrix44d(trans, angle, LA_COLOR3(use_forward));
+        tnsMakeTranslationMatrix44d(t1,LA_COLOR3(td->UseLocal?td->TLCenter:td->TCenter)); tnsInverse44d(t2,t1);
+        tnsMultiply44d(t3,t1,trans); tnsMultiply44d(t1,t3,t2);
+        if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,t1);
+        else tnsMultiply44d(final,t1,td->obmatinv);
+    }
+    if(td->mo){
+        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert));
+            tnsApplyTransform43d(to->mv->p, final, to->p);
+        }
+        tnsInvalidateMeshBatch(td->mo); tnsMMeshCalculateNormal(td->mo);
+    }elif(td->so){
+        for(int i=0;i<td->next;i++){ MTOrigSPoint* to=arrElement(td->Originals, i, sizeof(MTOrigSPoint));
+            tnsVector3d fp={to->p[0],to->p[1],0}, tp;
+            tnsApplyTransform43d(tp, final, fp); tnsVectorSet3v(to->sp->p,tp);
+        }
+    }else{
         for(int i=0;i<td->next;i++){
             MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject)); memcpy(to->o->GlobalTransform, to->Global,sizeof(tnsMatrix44d));
             if(to->Discard){ tnsSelfMatrixChanged(to->o,1); continue; }
@@ -610,40 +734,36 @@ void la_ApplyRotation(MTransformData* td, int uix, int uiy){
                 else tnsRotateObjectGlobal(to->o,LA_COLOR3(use_forward),angle,LA_COLOR3(td->TCenter));
             }
         }
-    }else{
-        tnsMakeRotationMatrix44d(trans, angle, LA_COLOR3(use_forward));
-        tnsMatrix44d t1,t2,t3; tnsMakeTranslationMatrix44d(t1,LA_COLOR3(td->UseLocal?td->TLCenter:td->TCenter)); tnsInverse44d(t2,t1);
-        tnsMatrix44d final; tnsMultiply44d(t3,t1,trans); tnsMultiply44d(t1,t3,t2);
-        if(!td->UseLocal) tnsMultiply44d(final,td->obmatinv,t1);
-        else tnsMultiply44d(final,t1,td->obmatinv);
-        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert));
-            tnsApplyTransform43d(to->mv->p, final, to->p);
-        }
-        tnsInvalidateMeshBatch(td->mo); tnsMMeshCalculateNormal(td->mo);
     }
 }
 void la_CancelTransformObjects(MTransformData* td){
-    if(!td->mo){
+    if(td->mo){
+        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert)); tnsVectorCopy3d(to->origp,to->mv->p); }
+        tnsInvalidateMeshBatch(td->mo);
+    }elif(td->so){
+        for(int i=0;i<td->next;i++){ MTOrigSPoint* to=arrElement(td->Originals, i, sizeof(MTOrigSPoint)); tnsVectorCopy2d(to->origp,to->sp->p); }
+    }else{
         for(int i=0;i<td->next;i++){ MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject));
             if(to->Discard){ tnsSelfMatrixChanged(to->o,1); continue; }
             memcpy(to->o->GlobalTransform, to->Global,sizeof(tnsMatrix44d));
             if(td->CanvasDeltaMode) tnsGlobalMatrixChangedForDelta(to->o, 1);
             else tnsGlobalMatrixChanged(to->o, 1);
         }
-    }else{
-        for(int i=0;i<td->next;i++){ MTOrigMVert* to=arrElement(td->Originals, i, sizeof(MTOrigMVert)); tnsVectorCopy3d(to->origp,to->mv->p); }
-        tnsInvalidateMeshBatch(td->mo);
+    
     }
 }
 void la_RecordTransformDifferences(MTransformData* td){
-    if(!td->mo){
-        for(int i=0;i<td->next;i++){ MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject));
-            laRecordInstanceDifferences(to->o, "tns_object");
-        } laPushDifferences(td->mode==LA_TRANSFORM_MODE_GRAB?"Moved objects":td->mode==LA_TRANSFORM_MODE_ROTATE?"Rotated objects":"Scaled objects", TNS_HINT_TRANSFORM);
-    }else{
+    if(td->mo){
         laRecordInstanceDifferences(td->mo, "tns_mesh_object");
         laPushDifferences(td->mode==LA_TRANSFORM_MODE_GRAB?"Moved primitives":td->mode==LA_TRANSFORM_MODE_ROTATE?"Rotated primitives":"Scaled primitives", TNS_HINT_GEOMETRY);
         tnsInvalidateMeshBatch(td->mo);
+    }elif(td->so){
+        laRecordInstanceDifferences(td->so, "tns_shape_object");
+        laPushDifferences(td->mode==LA_TRANSFORM_MODE_GRAB?"Moved points":td->mode==LA_TRANSFORM_MODE_ROTATE?"Rotated points":"Scaled points", TNS_HINT_GEOMETRY);
+    }else{
+        for(int i=0;i<td->next;i++){ MTOrigObject* to=arrElement(td->Originals, i, sizeof(MTOrigObject));
+            laRecordInstanceDifferences(to->o, "tns_object");
+        } laPushDifferences(td->mode==LA_TRANSFORM_MODE_GRAB?"Moved objects":td->mode==LA_TRANSFORM_MODE_ROTATE?"Rotated objects":"Scaled objects", TNS_HINT_TRANSFORM);
     }
 }
 void la_FreeTransformData(MTransformData* td){
@@ -683,7 +803,8 @@ int la_InitTransform(laOperator* a, laEvent* e, int mode, int restore_type, int
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-    tnsMeshObject* mo=root->Active; tnsRootObject*ro=root;
+    tnsObject* o=root->Active; tnsRootObject*ro=root;
+    tnsMeshObject* mo=o; tnsShapeObject* so=o;
 
     MTransformData* td=la_InitTransformData(
         ex->OffScr->pColor[0]->Width, ex->OffScr->pColor[0]->Height, c, ex->DeltaMode,
@@ -693,8 +814,10 @@ int la_InitTransform(laOperator* a, laEvent* e, int mode, int restore_type, int
     td->root=root;
 
     int ret=0;
-    if(mo && mo->Base.Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
+    if(o && o->Type==TNS_OBJECT_MESH && mo->Mode==TNS_MESH_EDIT_MODE){
         if(la_PopulateTransformVerticies(td, mo)){ ex->ClickedX=e->x; ex->ClickedY=e->y; ret=1; }
+    }if(o && o->Type==TNS_OBJECT_SHAPE && so->Mode==TNS_MESH_EDIT_MODE){
+        if(la_PopulateTransformPoints(td, so)){ ex->ClickedX=e->x; ex->ClickedY=e->y; ret=1; }
     }else{
         if(la_PopulateTransformObjects(td,root)){ ex->ClickedX=e->x; ex->ClickedY=e->y; ret=1; }
         if(ret && restore_type){
@@ -1353,10 +1476,10 @@ int OPINV_Separate(laOperator *a, laEvent *e){
     return LA_FINISHED;
 }
 
-void la_PopulateSelectedMeshObjects(tnsObject* root, laListHandle* l){
-    if(root->Type==TNS_OBJECT_MESH && root->Flags&TNS_OBJECT_FLAGS_SELECTED){ lstAppendPointer(l,root); }
+void la_PopulateSelectedObjects(tnsObject* root, laListHandle* l, int FilterType){
+    if(root->Flags&TNS_OBJECT_FLAGS_SELECTED){ if(root->Type && ((!FilterType) || root->Type==FilterType)) lstAppendPointer(l,root); }
     for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
-        la_PopulateSelectedMeshObjects(lip->p, l);
+        la_PopulateSelectedObjects(lip->p, l, FilterType);
     }
 }
 int OPINV_Combine(laOperator *a, laEvent *e){
@@ -1367,7 +1490,7 @@ int OPINV_Combine(laOperator *a, laEvent *e){
     
     if(!mo || mo->Base.Type!=TNS_OBJECT_MESH || mo->Mode==TNS_MESH_EDIT_MODE){ return LA_CANCELED; }
 
-    laListHandle pending={0}; la_PopulateSelectedMeshObjects(root,&pending);
+    laListHandle pending={0}; la_PopulateSelectedObjects(root,&pending,TNS_OBJECT_MESH);
     tnsMeshObject* o; while(o=lstPopPointer(&pending)){ if(o==mo || o->Mode==TNS_MESH_EDIT_MODE) continue;
         if(tnsMergeMeshObjects(mo, o)) ran++;
     }
@@ -1386,12 +1509,16 @@ int OPINV_Duplicate(laOperator *a, laEvent *e){
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return LA_CANCELED;
     tnsMeshObject* mo=root->Active; int ran=0;
     
-    if(!mo || mo->Base.Type!=TNS_OBJECT_MESH || mo->Mode==TNS_MESH_EDIT_MODE){ return LA_CANCELED; }
+    if(!mo || mo->Mode==TNS_MESH_EDIT_MODE){ return LA_CANCELED; }
 
-    laListHandle pending={0}; la_PopulateSelectedMeshObjects(root,&pending);
+    laListHandle pending={0}; la_PopulateSelectedObjects(root,&pending,0);
     tnsMeshObject* o; tnsMeshObject* no; laListItemPointer* lip;
     for(lip=pending.pFirst;lip;lip=lip->pNext){ o=lip->p; o->Base.EditDuplicateTemp=0; if (o->Mode == TNS_MESH_EDIT_MODE) continue;
-        if (no = tnsDuplicateMeshObject(o)){ o->Base.EditDuplicateTemp=no;
+        if(o->Base.Type==TNS_OBJECT_MESH) no = tnsDuplicateMeshObject(o);
+        elif(o->Base.Type==TNS_OBJECT_SHAPE) no = tnsDuplicateShapeObject(o);
+        elif(o->Base.Type==TNS_OBJECT_INSTANCER) no = tnsDuplicateShapeObject(o);
+        
+        if(no){ o->Base.EditDuplicateTemp=no;
             no->Base.Flags |= TNS_OBJECT_FLAGS_SELECTED; o->Base.Flags &= (~TNS_OBJECT_FLAGS_SELECTED);
             if (mo == o) { memAssignRef(root, &root->Active, no); } ran++;
         }
@@ -1403,7 +1530,7 @@ int OPINV_Duplicate(laOperator *a, laEvent *e){
     while(o=lstPopPointer(&pending)){ o->Base.EditDuplicateTemp=0; }
     
     if(ran){
-        laRecordAndPush(0,"tns.world","Merge mesh objects",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world");
+        laRecordAndPush(0,"tns.world","Duplicated objects",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world");
         if(la_InitTransform(a,e,LA_TRANSFORM_MODE_GRAB,0,0)) return LA_RUNNING; return LA_FINISHED;
     }
     
@@ -1536,7 +1663,7 @@ int OPINV_Knife(laOperator *a, laEvent *e){
     int SelectMode=LA_CANVAS_SELECT_MODE_KNIFE;
     if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "loop_cut")){
         se->IsLoop=1; SelectMode=LA_CANVAS_SELECT_MODE_EDGES; }
-    la_PopulateSelectDataPrimitives(sd,mo,c,SelectMode,ex->SelectThrough);
+    la_PopulateSelectDataPrimitives(sd,mo,c,SelectMode,root,ex);
 
     if(se->IsLoop){ strSafePrint(&a->RuntimeHint,"◧ Cut  ◨ Cancel"); }
     else{ strSafePrint(&a->RuntimeHint,"◧ Place Cut  ◨ Cancel  ⮨ Confirm"); }

+ 4 - 4
resources/la_widgets_viewers.c

@@ -80,8 +80,8 @@ void la_RootObjectDraw(laBoxedTheme *bt, tnsObject *root, laUiItem* ui){
     tnsFlush();
 
     if (!e->OffScr){
-        e->OffScr = tnsCreateDeferredOffscreen(W,H,MAIN.PanelMultisample);
-        //e->OffScr = tnsCreate2DOffscreen(GL_RGBA, W, H, MAIN.PanelMultisample ,1, 0);
+        //e->OffScr = tnsCreateDeferredOffscreen(W,H,MAIN.PanelMultisample);
+        e->OffScr = tnsCreate2DOffscreen(GL_RGBA, W, H, MAIN.PanelMultisample ,1, 1);
     }else{ tnsEnsureOffscreenStatus(e->OffScr, ui->R - ui->L, ui->B - ui->U); }
 
     if (0){//(e->CurrentScene && e->CurrentScene->ActiveSun){
@@ -156,12 +156,12 @@ void la_RootObjectDraw(laBoxedTheme *bt, tnsObject *root, laUiItem* ui){
 
     la3DObjectDrawExtra de={0};
     de.MeshEditType=e->SelectMode; de.DisplayMode=e->AsPlayer?LA_CANVAS_DISPLAY_MATERIAL:e->DisplayMode;
-    de.W=W; de.H=H;
+    de.W=W; de.H=H; de.PointScale=1.0f/LA_RH;
 
     tnsRootObject* ro=root;
     NVGcontext* vg=MAIN.CurrentWindow->nvg;
     tnsMatrix44d mview,mproj;
-    if(ro && ro->Is2D){ la_SetCanvasOrtho(e,W,H); }
+    if(ro && ro->Is2D){ la_SetCanvasOrtho(e,W,H); de.PointScale=e->ZoomX; }
     else{ tnsApplyCameraView(W, H, c, mview,mproj); tnsMultiply44d(de.mViewProj,mproj,mview); de.Is3D=1; }
 
     tnsPushMatrix(); tnsPopMatrix(); //those are necessary when ui is the first in list;