|  | @@ -38,6 +38,8 @@ laBaseNodeType LA_IDN_VECTOR_MATH;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_COMMENT;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_RGB2OKHSL;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_OKHSL2RGB;
 | 
	
		
			
				|  |  | +laBaseNodeType LA_IDN_LOOP;
 | 
	
		
			
				|  |  | +laBaseNodeType LA_IDN_LOOP_INDEX;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_GENERIC;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_KEYBOARD;
 | 
	
	
		
			
				|  | @@ -57,6 +59,8 @@ laPropContainer* LA_PC_IDN_VECTOR_MATH;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_COMMENT;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_RGB2OKHSL;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_OKHSL2RGB;
 | 
	
		
			
				|  |  | +laPropContainer* LA_PC_IDN_LOOP;
 | 
	
		
			
				|  |  | +laPropContainer* LA_PC_IDN_LOOP_INDEX;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_INPUT;
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_MATH;
 | 
	
	
		
			
				|  | @@ -65,6 +69,8 @@ laNodeCategory* LA_NODE_CATEGORY_AUX;
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_DRIVER;
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_COLOR;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void la_PageClearBranch(laRackPage* rp, int mask);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define LA_IDN_CONTROLLER_RESET_SOCKET(ns) \
 | 
	
		
			
				|  |  |      {ns->IntVal[0]=0; ns->Out->DataType=LA_PROP_INT; ns->Offset=0; ns->Out->Data=&ns->IntVal;}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -790,6 +796,105 @@ void laui_OKHSL2RGBNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
 | 
	
		
			
				|  |  |      laEndRow(uil,b);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void IDN_LoopInit(laLoopNode* n, int NoCreate){
 | 
	
		
			
				|  |  | +    if(!NoCreate){
 | 
	
		
			
				|  |  | +        strSafeSet(&n->Base.Name,"Loop");
 | 
	
		
			
				|  |  | +        n->Branch=laCreateInSocket("CTRL",LA_PROP_FLOAT);
 | 
	
		
			
				|  |  | +        n->InIndex=laCreateInSocket("IDX",LA_PROP_FLOAT);
 | 
	
		
			
				|  |  | +        n->InIterations=laCreateInSocket("NUM",LA_PROP_FLOAT);
 | 
	
		
			
				|  |  | +        n->Prev=laCreateInSocket("PREV",LA_PROP_FLOAT); n->Next=laCreateOutSocket(n,"NEXT",LA_PROP_INT);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    n->Next->Data=&n->Iterations;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void IDN_LoopDestroy(laLoopNode* n){
 | 
	
		
			
				|  |  | +    strSafeDestroy(&n->Base.Name);
 | 
	
		
			
				|  |  | +    laDestroyInSocket(n->Prev); laDestroyInSocket(n->InIterations); laDestroyInSocket(n->InIndex);
 | 
	
		
			
				|  |  | +    laDestroyInSocket(n->Branch); laDestroyOutSocket(n->Next);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int IDN_LoopVisit(laLoopNode* n, laNodeVisitInfo* vi){
 | 
	
		
			
				|  |  | +    LA_GUARD_THIS_NODE(n,vi);if(!LA_SRC_AND_PARENT(n->Branch)){ return LA_DAG_FLAG_PERM; }
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->Prev)){ laBaseNode* bn=n->Prev->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->InIterations)){ laBaseNode* bn=n->InIterations->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->InIndex)){ laBaseNode* bn=n->InIndex->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  | +    uint64_t OrigBranch=vi->Branch; int NextBranch=vi->NextBranch; n->Page=vi->Page;
 | 
	
		
			
				|  |  | +    vi->NextBranch+=1; n->BranchControl=(1<<(NextBranch-1));
 | 
	
		
			
				|  |  | +    if(OrigBranch==1){ lstAppendPointer(vi->br,n); }
 | 
	
		
			
				|  |  | +    vi->Branch=OrigBranch|n->BranchControl;
 | 
	
		
			
				|  |  | +    laBaseNode* sw=n->Branch->Source->Parent; LA_VISIT_NODE(sw,vi);
 | 
	
		
			
				|  |  | +    vi->Branch=OrigBranch;
 | 
	
		
			
				|  |  | +    LA_ADD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  | +    return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#define LA_GET_SRC_AS_INT(var, socket) \
 | 
	
		
			
				|  |  | +    {if(socket->Source->DataType&LA_PROP_FLOAT) var=*((real*)socket->Source->Data);\
 | 
	
		
			
				|  |  | +    if(socket->Source->DataType&(LA_PROP_INT|LA_PROP_ENUM)) var=*((int*)socket->Source->Data);}
 | 
	
		
			
				|  |  | +int IDN_LoopEval(laLoopNode* n){
 | 
	
		
			
				|  |  | +    laLoopIndexNode* lin=LA_SRC_AND_PARENT(n->InIndex)?n->InIndex->Source->Parent:0;
 | 
	
		
			
				|  |  | +    if(lin->Base.Type!=LA_IDN_LOOP_INDEX){lin=0;}
 | 
	
		
			
				|  |  | +    int Iterations=n->Iterations;
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->InIterations)){ LA_GET_SRC_AS_INT(Iterations, n->InIterations); }
 | 
	
		
			
				|  |  | +    for(int i=0; i<Iterations; i++){
 | 
	
		
			
				|  |  | +        if(lin){ lin->Iteration=i; }
 | 
	
		
			
				|  |  | +        la_PageClearBranch(n->Page,n->BranchControl);
 | 
	
		
			
				|  |  | +        laRunPage(n->Page,n->BranchControl);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laui_LoopNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn* c=laFirstColumn(uil), *cl,*cr; 
 | 
	
		
			
				|  |  | +    laLoopNode*n=This->EndInstance;
 | 
	
		
			
				|  |  | +    laUiItem*b,*b2;
 | 
	
		
			
				|  |  | +    LA_BASE_NODE_HEADER(uil,c,This);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    laSplitColumn(uil,c,0.6); cl=laLeftColumn(c,0); cr=laRightColumn(c,5);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    b=laBeginRow(uil,cl,0,0);
 | 
	
		
			
				|  |  | +    laShowItem(uil,cl,This,"branch")->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  | +    laShowItem(uil,cl,This,"in_iterations")->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  | +    laShowSeparator(uil,cl)->Expand=1;
 | 
	
		
			
				|  |  | +    laEndRow(uil,b);
 | 
	
		
			
				|  |  | +    b=laBeginRow(uil,cl,1,0);
 | 
	
		
			
				|  |  | +    laShowItem(uil,cl,This,"index")->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  | +    laShowItem(uil,cl,This,"iterations");
 | 
	
		
			
				|  |  | +    laShowSeparator(uil,cl)->Expand=1;
 | 
	
		
			
				|  |  | +    laEndRow(uil,b);
 | 
	
		
			
				|  |  | +    b=laBeginRow(uil,cr,1,0);
 | 
	
		
			
				|  |  | +    laShowItem(uil,cr,This,"prev")->Flags|=LA_UI_SOCKET_LABEL_S;
 | 
	
		
			
				|  |  | +    laShowItem(uil,cr,This,"next")->Flags|=LA_UI_SOCKET_LABEL_N;
 | 
	
		
			
				|  |  | +    laEndRow(uil,b);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void IDN_LoopIndexInit(laLoopIndexNode* n, int NoCreate){
 | 
	
		
			
				|  |  | +    if(!NoCreate){
 | 
	
		
			
				|  |  | +        strSafeSet(&n->Base.Name,"Loop Index");
 | 
	
		
			
				|  |  | +        n->Out=laCreateOutSocket(n,"IDX",LA_PROP_INT);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    n->Out->Data=&n->Iteration; n->Out->DataType=LA_PROP_INT;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void IDN_LoopIndexDestroy(laLoopIndexNode* n){
 | 
	
		
			
				|  |  | +    strSafeDestroy(&n->Base.Name);
 | 
	
		
			
				|  |  | +    laDestroyOutSocket(n->Out);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int IDN_LoopIndexVisit(laLoopIndexNode* n, laNodeVisitInfo* vi){
 | 
	
		
			
				|  |  | +    LA_GUARD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  | +    LA_ADD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  | +    return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int IDN_LoopIndexEval(laLoopIndexNode* n){
 | 
	
		
			
				|  |  | +    return 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laui_LoopIndexNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn* c=laFirstColumn(uil);
 | 
	
		
			
				|  |  | +    laLoopIndexNode*n=This->EndInstance;
 | 
	
		
			
				|  |  | +    laUiItem*b,*b2;
 | 
	
		
			
				|  |  | +    LA_BASE_NODE_HEADER(uil,c,This);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    b=laBeginRow(uil,c,0,0);
 | 
	
		
			
				|  |  | +    laShowSeparator(uil,c)->Expand=1;
 | 
	
		
			
				|  |  | +    laShowItem(uil,c,This,"index")->Flags|=LA_UI_SOCKET_LABEL_W;
 | 
	
		
			
				|  |  | +    laEndRow(uil,b);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int OPINV_AddInputMapperPage(laOperator* a, laEvent *e){
 | 
	
	
		
			
				|  | @@ -1254,7 +1359,22 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_h", "H","Hue","la_in_socket",0,0,0,offsetof(laOKHSL2RGBNode, InH),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_s", "S","Chroma","la_in_socket",0,0,0,offsetof(laOKHSL2RGBNode, InS),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_l", "L","Brightness","la_in_socket",0,0,0,offsetof(laOKHSL2RGBNode, InL),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | -   
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    pc=laAddPropertyContainer("la_loop_node", "Loop", "Repeat a series of operations for a few times",0,laui_LoopNode,sizeof(laLoopNode),lapost_Node,0,1);
 | 
	
		
			
				|  |  | +    LA_PC_IDN_LOOP=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
 | 
	
		
			
				|  |  | +    laAddIntProperty(pc, "iterations", "Iterations", "Loop iterations", 0,0,0,0,0,0,0,0,offsetof(laLoopNode, Iterations),0,0,0,0,0,0,0,0,0,0,0);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"in_iterations", "In Iterations","Hue","la_in_socket",0,0,0,offsetof(laLoopNode, InIterations),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"branch", "Branch","Control branch","la_in_socket",0,0,0,offsetof(laLoopNode, Branch),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"index", "Index","Index node","la_in_socket",0,0,0,offsetof(laLoopNode, InIndex),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"prev", "Next","Next node in sequence","la_in_socket",0,0,0,offsetof(laLoopNode, Prev),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"next", "Next","Next node in sequence","la_out_socket",0,0,0,offsetof(laLoopNode, Next),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    pc=laAddPropertyContainer("la_loop_index_node", "Loop Index", "Index reference of the loop node",0,laui_LoopIndexNode,sizeof(laLoopIndexNode),lapost_Node,0,1);
 | 
	
		
			
				|  |  | +    LA_PC_IDN_LOOP_INDEX=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
 | 
	
		
			
				|  |  | +    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);
 | 
	
	
		
			
				|  | @@ -1271,7 +1391,9 @@ typedef laMathNode laSmallMathNode;
 | 
	
		
			
				|  |  |      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_NODE_CATEGORY_INPUT=laAddNodeCategory("Input",0,LA_RACK_TYPE_INPUT);
 | 
	
		
			
				|  |  |      LA_NODE_CATEGORY_MATH=laAddNodeCategory("Math",0,LA_RACK_TYPE_ALL);
 | 
	
		
			
				|  |  |      LA_NODE_CATEGORY_COLOR=laAddNodeCategory("Color",0,LA_RACK_TYPE_ALL);
 | 
	
	
		
			
				|  | @@ -1280,7 +1402,7 @@ typedef laMathNode laSmallMathNode;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_INPUT, &LA_IDN_CONTROLLER,0);
 | 
	
		
			
				|  |  |      laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_MATH, &LA_IDN_MATH, &LA_IDN_SMALL_MATH,&LA_IDN_VECTOR_MATH,&LA_IDN_MAPPER,&LA_IDN_RANDOM,&LA_IDN_MATRIX, &LA_IDN_VALUES,0);
 | 
	
		
			
				|  |  | -    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_ROUTE, &LA_IDN_SPLIT, &LA_IDN_SWITCH, &LA_IDN_COMBINE,0);
 | 
	
		
			
				|  |  | +    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_ROUTE, &LA_IDN_SPLIT, &LA_IDN_SWITCH, &LA_IDN_COMBINE, &LA_IDN_LOOP, &LA_IDN_LOOP_INDEX,0);
 | 
	
		
			
				|  |  |      laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_AUX, &LA_IDN_COMMENT, &LA_IDN_VISUALIZER,0);
 | 
	
		
			
				|  |  |      laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_COLOR, &LA_IDN_RGB2OKHSL, &LA_IDN_OKHSL2RGB,0);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1293,6 +1415,12 @@ void laMappingRequestEval(){ MAIN.InputMapping->NeedEval=1; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int __DEBUG_PAGE_EVAL__=1;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void la_PageClearBranch(laRackPage* rp, int mask){
 | 
	
		
			
				|  |  | +    for(laListItemPointer*lip=rp->Eval.pFirst;lip;lip=lip->pNext){
 | 
	
		
			
				|  |  | +        laBaseNode* n=lip->p; if(!n->InitDone){ n->Type->Init(n,1); n->InitDone=1; }
 | 
	
		
			
				|  |  | +        if(n->Branch&mask){ n->EvalMagic=0; }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  int laRebuildPageEval(laRackPage* rp){
 | 
	
		
			
				|  |  |      if(!rp)return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  |      while(lstPopPointer(&rp->Eval)); while(lstPopPointer(&rp->AlwaysBranchers));
 | 
	
	
		
			
				|  | @@ -1313,7 +1441,7 @@ int laRebuildPageEval(laRackPage* rp){
 | 
	
		
			
				|  |  |      return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  int laRunPage(laRackPage* rp, uint64_t mask){
 | 
	
		
			
				|  |  | -    static uint64_t magic=3;
 | 
	
		
			
				|  |  | +    static uint64_t magic=3; if(!magic){ magic=3; }
 | 
	
		
			
				|  |  |      if(!rp || (!rp->Eval.pFirst && !rp->AlwaysBranchers.pFirst)) return 0;
 | 
	
		
			
				|  |  |      if(__DEBUG_PAGE_EVAL__ && mask==1){ printf("Page eval %s\n",(rp->Name&&rp->Name->Ptr)?rp->Name->Ptr:""); }
 | 
	
		
			
				|  |  |      if(mask==1){
 |