*/}}
Browse Source

Rack copy

YimingWu 1 year ago
parent
commit
2ec2f551cb
6 changed files with 172 additions and 29 deletions
  1. 14 3
      la_interface.h
  2. 15 0
      la_tns_kernel.c
  3. 3 0
      la_util.h
  4. 122 24
      resources/la_nodes_basic.c
  5. 11 2
      resources/la_tns_drivers.c
  6. 7 0
      resources/la_widgets.c

+ 14 - 3
la_interface.h

@@ -239,6 +239,7 @@ STRUCTURE(laNodeOutSocket){
     laSafeString* Label;
     int DataType, ArrLen;
     int RuntimeX, RuntimeY, RuntimePX, RuntimePY;
+    void* Duplicated;
 };
 STRUCTURE(laNodeInSocket){
     laNodeOutSocket* Source;
@@ -1368,7 +1369,7 @@ laBaseNode* la_CreateInputMapperNode(laNodeRack* ir, laBaseNodeType* NodeType);
 
 laPropContainer* laget_BaseNodeType(laBaseNode* bn);
 void laRegisterNode(laBaseNodeType* type, laPropContainer* pc,
-                    laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval,
+                    laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, laBaseNodeCopyF copy,
                     int nodesize, char* udf_string, char* type_string, char* UiText, int icon);
 
 void latouched_NodeInSocket(void* unused, int hint);
@@ -1377,8 +1378,8 @@ void latouched_NodeInSocket(void* unused, int hint);
     {laSplitColumn(uil,c,0.2);laColumn*c1=laLeftColumn(c,1);c=laRightColumn(c,0);\
     laShowHeightAdjuster(uil,c1,This,"base.__gap",0);}
 
-#define LA_IDN_REGISTER(uitext,icon,a,pc,init,destroy,visit,eval,type) \
-    laRegisterNode(&a,pc,init,destroy,visit,eval,sizeof(type),"UDFRES_" #type, #type, uitext,icon);
+#define LA_IDN_REGISTER(uitext,icon,a,pc,funcname,type) \
+    laRegisterNode(&a,pc,funcname##Init,funcname##Destroy,funcname##Visit,funcname##Eval,funcname##Copy,sizeof(type),"UDFRES_" #type, #type, uitext,icon);
 
 #define LA_SRC_AND_PARENT(socket) \
     ((socket)->Source && (socket)->Source->Parent)
@@ -1392,6 +1393,12 @@ void latouched_NodeInSocket(void* unused, int hint);
 #define LA_ADD_THIS_NODE(n,vi) \
     {uint64_t br=vi->Branch; if(!n->Base.Branch){ lstAppendPointer(vi->l,n); } n->Base.Branch|=br; n->Base.BranchTemp&=(~br); }
 
+#define LA_IDN_OLD_DUPL(socket) \
+    old->socket->Duplicated=new->socket;
+#define LA_IDN_NEW_LINK(socket) \
+    { new->socket->Source=old->socket->Source?old->socket->Source->Duplicated:0; new->socket->ColorId=old->socket->ColorId; }
+
+
 extern laBaseNodeType LA_IDN_KEYBOARD;
 extern laBaseNodeType LA_IDN_MOUSE;
 extern laBaseNodeType LA_IDN_CONTROLLER;
@@ -1470,6 +1477,7 @@ STRUCTURE(laNodeRack){
     laListHandle Nodes;
     laRackPage* ParentPage;
     int RackType;
+    laNodeRack* Duplicated;
 };
 NEED_STRUCTURE(laInputControllerNode);
 STRUCTURE(laInputControllerNodeSocket){
@@ -1624,6 +1632,7 @@ STRUCTURE(laNodeCategory){
 
 laValueMapper* laValueMapperInit();
 laValueMapper* laValueMapperDestroy(laValueMapper* vm);
+laValueMapper* laValueMapperCopy(laValueMapper* new_optional, laValueMapper* vm);
 real laValueMapperEvaluate(laValueMapper* vm, real x, real* force_InMin, real* force_InMax, real* force_OutMin, real* force_OutMax, int ClampIn, int ClampOut);
 
 void laShowProgress(real p1, real p2);
@@ -2054,6 +2063,8 @@ void laDestroyInSocket(laNodeInSocket* s);
 void laDestroyOutSocket(laNodeOutSocket* s);
 laNodeCategory* laAddNodeCategory(char* Name,laUiDefineFunc* Ui,int ForRackTypes);
 void laNodeCategoryAddNodeTypes(laNodeCategory* nc, ...);
+laRackPage* laDuplicateRackPage(laRackPage* new_optional, laRackPage* from);
+
 
 void laFreeKeyMapItem(laKeyMapItem* kmi);
 laKeyMapItem *laAssignNewKey(laKeyMapper *km, char *Path, char *Operation, char SelectBase, int SpecialKeyBits, int EventType, int Key, char *ExtraInstructions);

+ 15 - 0
la_tns_kernel.c

@@ -3542,6 +3542,21 @@ tnsLight *tnsCreateLight(tnsObject *under, char *Name, real AtX, real AtY, real
     return l;
 }
 
+int tnsSizeOfObject(tnsObject* o){
+    switch(o->Type){
+    case TNS_OBJECT_ROOT: default: return sizeof(tnsObject);
+    case TNS_OBJECT_INSTANCER: return sizeof(tnsInstancer);
+    case TNS_OBJECT_MESH: return sizeof(tnsMeshObject);
+    case TNS_OBJECT_CAMERA: return sizeof(tnsCamera);
+    case TNS_OBJECT_LIGHT: return sizeof(tnsLight);
+    }
+}
+tnsObject* tnsDuplicateObject(tnsObject* o){
+    tnsMeshObject* to = memAcquireHyper(tnsSizeOfObject(o));
+    tnsInitObjectBase(to, o->ParentObject?o->ParentObject:o->InRoot, o->Name->Ptr, TNS_OBJECT_MESH,0,0,0,0,0,0,0,0,1);
+    tnsCopyObjectTransformationsLocal(to,o);
+}
+
 int tnsAnyObjectsSelected(tnsObject *root){
     for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){ tnsObject* o=lip->p; if(o->Flags&TNS_OBJECT_FLAGS_SELECTED) return 1; 
         if(o->ChildObjects.pFirst && tnsAnyObjectsSelected(o)) return 1;

+ 3 - 0
la_util.h

@@ -409,12 +409,14 @@ typedef void (*laBaseNodeInitF)(laBaseNode*, int NoCreate);
 typedef void (*laBaseNodeDestroyF)(laBaseNode*);
 typedef int (*laBaseNodeVisitF)(laBaseNode*, laNodeVisitInfo*);
 typedef int (*laBaseNodeEvalF)(laBaseNode*);
+typedef void (*laBaseNodeCopyF)(laBaseNode* _new, laBaseNode* _old, int DoRematch);
 
 STRUCTURE(laBaseNodeType){
     laBaseNodeInitF    Init;
     laBaseNodeDestroyF Destroy;
     laBaseNodeVisitF   Visit;
     laBaseNodeEvalF    Eval;
+	laBaseNodeCopyF    Copy;
     laPropContainer*   pc;
 	char* TypeName;
 	char* Name;
@@ -431,6 +433,7 @@ STRUCTURE(laBaseNode){
 	uint64_t EvalMagic;
 	uint64_t Branch;
 	uint64_t BranchTemp;
+	laBaseNode* Duplicated;
 };
 
 

+ 122 - 24
resources/la_nodes_basic.c

@@ -118,6 +118,13 @@ int IDN_ControllerEval(laInputControllerNode* n){
     }
     return 1;
 }
+void IDN_ControllerCopy(laInputControllerNode* new, laInputControllerNode* old, int DoRematch){
+    if(DoRematch) return;
+    for(int i=0;i<8;i++){ old->Sockets[i].Out->Duplicated=new->Sockets[i].Out;
+        strSafeSet(&new->Sockets[i].Which,old->Sockets[i].Which?old->Sockets[i].Which->Ptr:0);
+    }
+    new->Mode=old->Mode;new->UserID=old->UserID;
+}
 void laui_ControllerNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laInputControllerNode*n=This->EndInstance;
     laColumn* cl,*cr; 
@@ -148,16 +155,16 @@ void laui_ControllerNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
     }
 }
 
-void IDN_InputVisualizeInit(laInputVisualizerNode* n, int NoCreate){
+void IDN_InputVisualizerInit(laInputVisualizerNode* n, int NoCreate){
     if(NoCreate){return;}
     n->In=laCreateInSocket("IN", 0);
     strSafeSet(&n->Base.Name,"Input Visualizer");
 }
-void IDN_InputVisualizeDestroy(laInputVisualizerNode* n){
+void IDN_InputVisualizerDestroy(laInputVisualizerNode* n){
     laDestroyInSocket(n->In);
     strSafeDestroy(&n->Base.Name);
 }
-int IDN_InputVisualizeVisit(laInputVisualizerNode* n, laNodeVisitInfo* vi){
+int IDN_InputVisualizerVisit(laInputVisualizerNode* n, laNodeVisitInfo* vi){
     LA_GUARD_THIS_NODE(n,vi);
     if(LA_SRC_AND_PARENT(n->In)){ laBaseNode* sn=n->In->Source->Parent;LA_VISIT_NODE(sn,vi); }
     LA_ADD_THIS_NODE(n,vi);
@@ -177,6 +184,9 @@ int IDN_InputVisualizerEval(laInputVisualizerNode* n){
     laNotifyInstanceUsers(n);
     return 1;
 }
+void IDN_InputVisualizerCopy(laInputVisualizerNode* new, laInputVisualizerNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(In) return; }
+}
 void laui_InputVisualizeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laInputVisualizerNode*n=This->EndInstance;
     LA_BASE_NODE_HEADER(uil,c,This);
@@ -228,6 +238,10 @@ int IDN_SplitEval(laSplitNode* n){
     n->In->DataType=os->DataType;
     return 1;
 }
+void IDN_SplitCopy(laSplitNode* new, laSplitNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(In) return; }
+    for(int i=0;i<8;i++){ old->Out[i].Out->Duplicated=new->Out[i].Out; }
+}
 void laui_SplitNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSplitNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
@@ -293,6 +307,10 @@ int IDN_SwitchEval(laSwitchNode* n){
     else{ n->Out->Data=&n->TempVal; n->Out->DataType=LA_PROP_FLOAT; n->Out->ArrLen=1; }
     return 1;
 }
+void IDN_SwitchCopy(laSwitchNode* new, laSwitchNode* old, int DoRematch){
+    if(DoRematch){ for(int i=0;i<8;i++){ LA_IDN_NEW_LINK(In[i].In) } LA_IDN_NEW_LINK(SwitchIn) return; }
+    old->Out->Duplicated=new->Out; new->Switch=old->Switch; 
+}
 void laui_SwitchNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSwitchNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
@@ -349,6 +367,13 @@ int IDN_CombineEval(laCombineNode* n){
     n->Out->ArrLen=n->OutInt->ArrLen=n->OutEnum->ArrLen=maxlen;
     return 1;
 }
+void IDN_CombineCopy(laCombineNode* new, laCombineNode* old, int DoRematch){
+    if(DoRematch){
+        for(int i=0;i<8;i++){ LA_IDN_NEW_LINK(In[i].In) }
+        return;
+    }
+    old->Out->Duplicated=new->Out; old->OutInt->Duplicated=new->OutInt; old->OutEnum->Duplicated=new->OutEnum;
+}
 void laui_CombineNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laCombineNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
@@ -391,6 +416,13 @@ int IDN_ValuesEval(laValuesNode* n){
     }
     return 1;
 }
+void IDN_ValuesCopy(laValuesNode* new, laValuesNode* old, int DoRematch){
+    if(DoRematch){ return; }
+    for(int i=0;i<8;i++){
+        old->Out[i].Out->Duplicated=new->Out[i].Out; new->Modes[i]=old->Modes[i];
+        new->Values[i]=old->Values[i]; new->ValuesE[i]=old->ValuesE[i]; new->ValuesI[i]=old->ValuesI[i];
+    }
+}
 void laui_ValuesNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laValuesNode*n=This->EndInstance;
     laUiItem*b,*b2;
@@ -449,6 +481,10 @@ int IDN_MatrixEval(laMatrixNode* n){
     }
     return 1;
 }
+void IDN_MatrixCopy(laMatrixNode* new, laMatrixNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(InL) LA_IDN_NEW_LINK(InR) return; }
+    old->Out->Duplicated=new->Out; new->Operation=old->Operation;
+}
 void laui_MatrixNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laMatrixNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
@@ -505,6 +541,11 @@ int IDN_MathEval(laMathNode* n){
     n->ValueI=(int)n->Value;
     return 1;
 }
+void IDN_MathCopy(laMathNode* new, laMathNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(InL) LA_IDN_NEW_LINK(InR) return; }
+    old->Out->Duplicated=new->Out; old->OutInt->Duplicated=new->OutInt; new->Operation=old->Operation;
+    new->ValueL=old->ValueL; new->ValueR=old->ValueR;
+}
 void laui_MathNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laMathNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
@@ -572,6 +613,13 @@ int IDN_MapperEval(laMapperNode* n){
     n->rOut=result;
     return 1;
 }
+void IDN_MapperCopy(laMapperNode* new, laMapperNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(In) LA_IDN_NEW_LINK(InMax)
+        LA_IDN_NEW_LINK(InMin) LA_IDN_NEW_LINK(OutMax) LA_IDN_NEW_LINK(OutMin)  return;
+    }
+    old->Out->Duplicated=new->Out; new->ClampInput=old->ClampInput; new->ClampOutput=old->ClampOutput;
+    laValueMapperCopy(new->Mapper,old->Mapper);
+}
 void laui_MapperNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laMapperNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
@@ -615,6 +663,10 @@ int IDN_RandomEval(laRandomNode* n){
     n->rOut=Min+(real)rand()/(real)(RAND_MAX/(Max-Min));
     return 1;
 }
+void IDN_RandomCopy(laRandomNode* new, laRandomNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(InMax) LA_IDN_NEW_LINK(InMin) return; }
+    old->Out->Duplicated=new->Out; new->Max=old->Max; new->Min=old->Min;
+}
 void laui_RandomNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laRandomNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
@@ -671,6 +723,10 @@ int IDN_VectorMathEval(laVectorMathNode* n){
     }
     return 1;
 }
+void IDN_VectorMathCopy(laVectorMathNode* new, laVectorMathNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(InL) LA_IDN_NEW_LINK(InR) return; }
+    old->Out->Duplicated=new->Out; new->Operation=old->Operation;
+}
 void laui_VectorMathNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laVectorMathNode*n=This->EndInstance;
     laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
@@ -699,6 +755,10 @@ int IDN_CommentVisit(laCommentNode* n, laNodeVisitInfo* vi){
     return LA_DAG_FLAG_PERM;
 }
 int IDN_CommentEval(laCommentNode* n){ return 1; }
+void IDN_CommentCopy(laCommentNode* new, laCommentNode* old, int DoRematch){
+    if(DoRematch){ return; }
+    strSafeSet(new->Content,old->Content?old->Content->Ptr:0);
+}
 void laui_CommentNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laCommentNode*n=This->EndInstance;
     LA_BASE_NODE_HEADER(uil,c,This);
@@ -735,6 +795,10 @@ int IDN_RGB2OKHSLEval(laRGB2OKHSLNode* n){
     tnsRGB2HCYLinear(in, n->rOut);
     return 1;
 }
+void IDN_RGB2OKHSLCopy(laRGB2OKHSLNode* new, laRGB2OKHSLNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(In) return; }
+    old->OutH->Duplicated=new->OutH;old->OutS->Duplicated=new->OutS;old->OutL->Duplicated=new->OutL;
+}
 void laui_RGB2OKHSLNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laRGB2OKHSLNode*n=This->EndInstance;
     laUiItem*b,*b2;
@@ -782,6 +846,10 @@ int IDN_OKHSL2RGBEval(laOKHSL2RGBNode* n){
     tnsHCY2RGBLinear(in, n->rOut);
     return 1;
 }
+void IDN_OKHSL2RGBCopy(laOKHSL2RGBNode* new, laOKHSL2RGBNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(InH) LA_IDN_NEW_LINK(InS) LA_IDN_NEW_LINK(InL) return; }
+    old->Out->Duplicated=new->Out;
+}
 void laui_OKHSL2RGBNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laOKHSL2RGBNode*n=This->EndInstance;
     laUiItem*b,*b2;
@@ -840,6 +908,10 @@ int IDN_LoopEval(laLoopNode* n){
     }
     return 1;
 }
+void IDN_LoopCopy(laLoopNode* new, laLoopNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(Branch) LA_IDN_NEW_LINK(InIndex) LA_IDN_NEW_LINK(InIterations) LA_IDN_NEW_LINK(Prev) return; }
+    old->Next->Duplicated=new->Next;
+}
 void laui_LoopNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil), *cl,*cr; 
     laLoopNode*n=This->EndInstance;
@@ -883,6 +955,9 @@ int IDN_LoopIndexVisit(laLoopIndexNode* n, laNodeVisitInfo* vi){
 int IDN_LoopIndexEval(laLoopIndexNode* n){
     return 1;
 }
+void IDN_LoopIndexCopy(laLoopIndexNode* new, laLoopIndexNode* old, int DoRematch){
+    if(DoRematch){ return; } old->Out->Duplicated=new->Out;
+}
 void laui_LoopIndexNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil);
     laLoopIndexNode*n=This->EndInstance;
@@ -1073,11 +1148,11 @@ laBoxedTheme* laget_NodeGetTheme(laNodeRack* rack_unused, laBaseNode* n){
 }
 
 void laRegisterNode(laBaseNodeType* type, laPropContainer* pc,
-                    laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval,
+                    laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, laBaseNodeCopyF copy,
                     int nodesize, char* udf_string, char* type_string, char* UiText, int icon){
     arrEnsureLength(&MAIN.NodeTypes, MAIN.NodeTypeNext, &MAIN.NodeTypeMax, sizeof(laBaseNode*));
-    type->Init = init; type->Destroy = destroy; type->Visit=visit; type->Eval=eval; type->NodeSize=nodesize; type->pc=pc;
-    type->TypeName=type_string;type->Name=UiText;type->Icon=icon;
+    type->Init = init; type->Destroy = destroy; type->Visit=visit; type->Eval=eval; type->NodeSize=nodesize; type->Copy=copy;
+    type->pc=pc; type->TypeName=type_string;type->Name=UiText;type->Icon=icon;
     MAIN.NodeTypes[MAIN.NodeTypeNext]=type; MAIN.NodeTypeNext++;
     la_UDFAppendSharedTypePointer(udf_string, type);
 }
@@ -1377,23 +1452,23 @@ void la_RegisterInputMapperOperators(){
     laAddSubGroup(pc,"index", "Index","Index output","la_out_socket",0,0,0,offsetof(laLoopIndexNode, Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     
 typedef laMathNode laSmallMathNode;
-    LA_IDN_REGISTER("Controller",U'🕹',LA_IDN_CONTROLLER,LA_PC_IDN_CONTROLLER, IDN_ControllerInit, IDN_ControllerDestroy, IDN_ControllerVisit, IDN_ControllerEval, laInputControllerNode);
-    LA_IDN_REGISTER("Visualizer",U'🔍',LA_IDN_VISUALIZER,LA_PC_IDN_VISUALIZER, IDN_InputVisualizeInit, IDN_InputVisualizeDestroy, IDN_InputVisualizeVisit, IDN_InputVisualizerEval, laInputVisualizerNode);
-    LA_IDN_REGISTER("Split",U'⚟',LA_IDN_SPLIT,LA_PC_IDN_SPLIT, IDN_SplitInit, IDN_SplitDestroy, IDN_SplitVisit, IDN_SplitEval, laSplitNode);
-    LA_IDN_REGISTER("Switch",U'🚦',LA_IDN_SWITCH,LA_PC_IDN_SWITCH, IDN_SwitchInit, IDN_SwitchDestroy, IDN_SwitchVisit, IDN_SwitchEval, laSwitchNode);
-    LA_IDN_REGISTER("Combine",U'⚞',LA_IDN_COMBINE,LA_PC_IDN_COMBINE, IDN_CombineInit, IDN_CombineDestroy, IDN_CombineVisit, IDN_CombineEval, laCombineNode);
-    LA_IDN_REGISTER("Values",0,LA_IDN_VALUES,LA_PC_IDN_VALUES, IDN_ValuesInit, IDN_ValuesDestroy, IDN_ValuesVisit, IDN_ValuesEval, laValuesNode);
-    LA_IDN_REGISTER("Matrix",0,LA_IDN_MATRIX,LA_PC_IDN_MATRIX, IDN_MatrixInit, IDN_MatrixDestroy, IDN_MatrixVisit, IDN_MatrixEval, laMatrixNode);
-    LA_IDN_REGISTER("Math",0,LA_IDN_MATH,LA_PC_IDN_MATH, IDN_MathInit, IDN_MathDestroy, IDN_MathVisit, IDN_MathEval, laMathNode);
-    LA_IDN_REGISTER("Small Math",0,LA_IDN_SMALL_MATH,LA_PC_IDN_SMALL_MATH, IDN_MathInit, IDN_MathDestroy, IDN_MathVisit, IDN_MathEval, laSmallMathNode);
-    LA_IDN_REGISTER("Mapper",0,LA_IDN_MAPPER,LA_PC_IDN_MAPPER, IDN_MapperInit, IDN_MapperDestroy, IDN_MapperVisit, IDN_MapperEval, laMapperNode);
-    LA_IDN_REGISTER("Random",0,LA_IDN_RANDOM,LA_PC_IDN_RANDOM, IDN_RandomInit, IDN_RandomDestroy, IDN_RandomVisit, IDN_RandomEval, laRandomNode);
-    LA_IDN_REGISTER("Vector Math",0,LA_IDN_VECTOR_MATH,LA_PC_IDN_VECTOR_MATH, IDN_VectorMathInit, IDN_VectorMathDestroy, IDN_VectorMathVisit, IDN_VectorMathEval, laVectorMathNode);
-    LA_IDN_REGISTER("Comment",0,LA_IDN_COMMENT,LA_PC_IDN_COMMENT, IDN_CommentInit, IDN_CommentDestroy, IDN_CommentVisit, IDN_CommentEval, laCommentNode);
-    LA_IDN_REGISTER("RGB to OKHSL",0,LA_IDN_RGB2OKHSL,LA_PC_IDN_RGB2OKHSL, IDN_RGB2OKHSLInit, IDN_RGB2OKHSLDestroy, IDN_RGB2OKHSLVisit, IDN_RGB2OKHSLEval, laRGB2OKHSLNode);
-    LA_IDN_REGISTER("OKHSL to RGB",0,LA_IDN_OKHSL2RGB,LA_PC_IDN_OKHSL2RGB, IDN_OKHSL2RGBInit, IDN_OKHSL2RGBDestroy, IDN_OKHSL2RGBVisit, IDN_OKHSL2RGBEval, laOKHSL2RGBNode);
-    LA_IDN_REGISTER("Loop",0,LA_IDN_LOOP,LA_PC_IDN_LOOP, IDN_LoopInit, IDN_LoopDestroy, IDN_LoopVisit, IDN_LoopEval, laLoopNode);
-    LA_IDN_REGISTER("Loop Index",0,LA_IDN_LOOP_INDEX,LA_PC_IDN_LOOP_INDEX, IDN_LoopIndexInit, IDN_LoopIndexDestroy, IDN_LoopIndexVisit, IDN_LoopIndexEval, laLoopIndexNode);
+    LA_IDN_REGISTER("Controller",U'🕹',LA_IDN_CONTROLLER,LA_PC_IDN_CONTROLLER, IDN_Controller, laInputControllerNode);
+    LA_IDN_REGISTER("Visualizer",U'🔍',LA_IDN_VISUALIZER,LA_PC_IDN_VISUALIZER, IDN_InputVisualizer, laInputVisualizerNode);
+    LA_IDN_REGISTER("Split",U'⚟',LA_IDN_SPLIT,LA_PC_IDN_SPLIT, IDN_Split, laSplitNode);
+    LA_IDN_REGISTER("Switch",U'🚦',LA_IDN_SWITCH,LA_PC_IDN_SWITCH, IDN_Switch, laSwitchNode);
+    LA_IDN_REGISTER("Combine",U'⚞',LA_IDN_COMBINE,LA_PC_IDN_COMBINE, IDN_Combine, laCombineNode);
+    LA_IDN_REGISTER("Values",0,LA_IDN_VALUES,LA_PC_IDN_VALUES, IDN_Values, laValuesNode);
+    LA_IDN_REGISTER("Matrix",0,LA_IDN_MATRIX,LA_PC_IDN_MATRIX, IDN_Matrix, laMatrixNode);
+    LA_IDN_REGISTER("Math",0,LA_IDN_MATH,LA_PC_IDN_MATH, IDN_Math, laMathNode);
+    LA_IDN_REGISTER("Small Math",0,LA_IDN_SMALL_MATH,LA_PC_IDN_SMALL_MATH, IDN_Math, laSmallMathNode);
+    LA_IDN_REGISTER("Mapper",0,LA_IDN_MAPPER,LA_PC_IDN_MAPPER, IDN_Mapper, laMapperNode);
+    LA_IDN_REGISTER("Random",0,LA_IDN_RANDOM,LA_PC_IDN_RANDOM, IDN_Random, laRandomNode);
+    LA_IDN_REGISTER("Vector Math",0,LA_IDN_VECTOR_MATH,LA_PC_IDN_VECTOR_MATH, IDN_VectorMath, laVectorMathNode);
+    LA_IDN_REGISTER("Comment",0,LA_IDN_COMMENT,LA_PC_IDN_COMMENT, IDN_Comment, laCommentNode);
+    LA_IDN_REGISTER("RGB to OKHSL",0,LA_IDN_RGB2OKHSL,LA_PC_IDN_RGB2OKHSL, IDN_RGB2OKHSL, laRGB2OKHSLNode);
+    LA_IDN_REGISTER("OKHSL to RGB",0,LA_IDN_OKHSL2RGB,LA_PC_IDN_OKHSL2RGB, IDN_OKHSL2RGB, laOKHSL2RGBNode);
+    LA_IDN_REGISTER("Loop",0,LA_IDN_LOOP,LA_PC_IDN_LOOP, IDN_Loop, laLoopNode);
+    LA_IDN_REGISTER("Loop Index",0,LA_IDN_LOOP_INDEX,LA_PC_IDN_LOOP_INDEX, IDN_LoopIndex, laLoopIndexNode);
    
     LA_NODE_CATEGORY_INPUT=laAddNodeCategory("Input",0,LA_RACK_TYPE_INPUT);
     LA_NODE_CATEGORY_MATH=laAddNodeCategory("Math",0,LA_RACK_TYPE_ALL);
@@ -1414,7 +1489,7 @@ typedef laMathNode laSmallMathNode;
 void laGraphRequestRebuild(){ MAIN.GraphNeedsRebuild=1; }
 void laMappingRequestEval(){ MAIN.InputNeedsEval=1; }
 
-int __DEBUG_PAGE_EVAL__=1;
+int __DEBUG_PAGE_EVAL__=0;
 
 void la_PageClearBranch(laRackPage* rp, int mask){
     for(laListItemPointer*lip=rp->Eval.pFirst;lip;lip=lip->pNext){
@@ -1500,3 +1575,26 @@ void laNodeCategoryAddNodeTypes(laNodeCategory* nc, ...){
     }
     va_end(list);
 }
+
+laRackPage* laDuplicateRackPage(laRackPage* new_optional, laRackPage* from){
+    laRackPage* nr=new_optional?new_optional:memAcquire(sizeof(laRackPage));
+    strSafeSet(&nr->Name,from->Name?from->Name->Ptr:0);
+    strSafeSet(&nr->Script,nr->Script?from->Script->Ptr:0);
+    nr->TriggerMode=from->TriggerMode; nr->UseScript=from->UseScript; nr->RackType=from->RackType;
+    for(laNodeRack* r=from->Racks.pFirst;r;r=r->Item.pNext){
+        laNodeRack* nnr=memAcquire(sizeof(laNodeRack)); r->Duplicated=nnr; lstAppendItem(&nr->Racks, nnr);
+        strSafeSet(&nnr->Name,r->Name?r->Name->Ptr:0);
+        nnr->ParentPage=nr; nnr->RackType=r->RackType;
+        for(laBaseNode* n=r->Nodes.pFirst;n;n=n->Item.pNext){
+            laBaseNode* nn=memAcquire(n->Type->NodeSize);
+            nn->Type=n->Type; nn->InitDone=1; lstAppendItem(&nnr->Nodes, nn); nn->InRack=nnr; n->Duplicated=nn;
+            nn->Type->Init(nn, 0); nn->Type->Copy(nn,n,0);
+        }
+    }
+    for(laNodeRack* r=from->Racks.pFirst;r;r=r->Item.pNext){
+        for(laBaseNode* n=r->Nodes.pFirst;n;n=n->Item.pNext){
+            n->Type->Copy(n->Duplicated,n,1);
+        }
+    }
+    return nr;
+}

+ 11 - 2
resources/la_tns_drivers.c

@@ -49,6 +49,10 @@ int IDN_TransformEval(tnsTransformNode* n){
     laNotifyInstanceUsers(n->Target);
     return 1;
 }
+void IDN_TransformCopy(tnsTransformNode* new, tnsTransformNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(Mat) return; }
+    memAssignRef(new,&new->Target,old->Target);
+}
 void tnsui_TransformNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); tnsTransformNode*n=This->EndInstance;
     LA_BASE_NODE_HEADER(uil,c,This);
@@ -96,6 +100,11 @@ int IDN_MakeTransformEval(tnsMakeTransformNode* n){
     n->Out->ArrLen=16; n->Out->Data=n->Mat; n->Out->DataType=LA_PROP_FLOAT|LA_PROP_ARRAY;
     return 1;
 }
+void IDN_MakeTransformCopy(tnsMakeTransformNode* new, tnsMakeTransformNode* old, int DoRematch){
+    if(DoRematch){ LA_IDN_NEW_LINK(Loc) LA_IDN_NEW_LINK(Rot) LA_IDN_NEW_LINK(Sca) LA_IDN_NEW_LINK(Angle) return; }
+    old->Out->Duplicated=new->Out; new->UseSca=old->UseSca; new->UseAngle=old->UseAngle;
+    tnsVectorSet3v(new->UseLoc,old->UseLoc); tnsVectorSet3v(new->UseRot,old->UseRot);
+}
 void tnsui_MakeTransformNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); tnsMakeTransformNode*n=This->EndInstance;
     laUiItem* b2, *rui;
@@ -180,8 +189,8 @@ void tns_RegisterNodes(){
     laAddFloatProperty(pc,"use_sca", "Scale", "Use Scale",0,0,0,0,0,0.05,0,0,offsetof(tnsMakeTransformNode, UseSca),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"use_angle", "Angle", "Use Angle",0,0,0,0,0,0.05,0,0,offsetof(tnsMakeTransformNode, UseAngle),0,0,0,0,0,0,0,0,0,0,0);
     
-    LA_IDN_REGISTER("Transform",0,TNS_IDN_TRANSFORM,TNS_PC_IDN_TRANSFORM, IDN_TransformInit, IDN_TransformDestroy, IDN_TransformVisit, IDN_TransformEval, tnsTransformNode);
-    LA_IDN_REGISTER("Make Transform",0,TNS_IDN_MAKE_TRANSFORM,TNS_PC_IDN_MAKE_TRANSFORM, IDN_MakeTransformInit, IDN_MakeTransformDestroy, IDN_MakeTransformVisit, IDN_MakeTransformEval, tnsMakeTransformNode);
+    LA_IDN_REGISTER("Transform",0,TNS_IDN_TRANSFORM,TNS_PC_IDN_TRANSFORM, IDN_Transform, tnsTransformNode);
+    LA_IDN_REGISTER("Make Transform",0,TNS_IDN_MAKE_TRANSFORM,TNS_PC_IDN_MAKE_TRANSFORM, IDN_MakeTransform, tnsMakeTransformNode);
 
     LA_NODE_CATEGORY_DRIVER=laAddNodeCategory("Driver",0,LA_RACK_TYPE_DRIVER);
 

+ 7 - 0
resources/la_widgets.c

@@ -2967,6 +2967,13 @@ laValueMapper* laValueMapperDestroy(laValueMapper* vm){
     while(vmp=lstPopItem(&vm->Points)){ memLeave(vmp); }
     memLeave(vm);
 }
+laValueMapper* laValueMapperCopy(laValueMapper* new_optional, laValueMapper* vm){
+    laValueMapper* into=new_optional?new_optional:memAcquire(sizeof(laValueMapper)); laValueMapperPoint* vmp;
+    while(vmp=lstPopItem(&into->Points)){ memLeave(vmp); }
+    for(vmp=vm->Points.pFirst;vmp;vmp=vmp->Item.pNext){ la_ValueMapperCreatePoint(into, vmp->x, vmp->y); }
+    tnsVectorSet2v(into->InRange,vm->InRange); tnsVectorSet2v(into->OutRange,vm->OutRange);
+    return into;
+}
 real laValueMapperEvaluate(laValueMapper* vm, real x, real* force_InMin, real* force_InMax, real* force_OutMin, real* force_OutMax, int ClampIn, int ClampOut){
     real InMin=vm->InRange[0], InMax=vm->InRange[1], OutMin=vm->OutRange[0], OutMax=vm->OutRange[1];
     if(force_InMin) InMin=*force_InMin; if(force_InMax) InMax=*force_InMax; if(force_OutMin) OutMin=*force_OutMin; if(force_OutMax) OutMax=*force_OutMax;