public static void RetireFormingEdge(Shape data, Node liveNode, Node newStaticNode) { if (data.formingEdges.ContainsKey(liveNode)) { Edge formingEdge = data.formingEdges[liveNode]; data.formingEdges.Remove(liveNode); formingEdge.ReplaceNode(liveNode, newStaticNode); formingEdge.UpdateValues(); data.edges.Add(formingEdge); } }
private void CollapseNodes(List <Node> nodes, Vector2 toPoint, float height) { Shape _shape = _core.shape; int nodeCount = nodes.Count; float minHeight = nodes[0].height; float maxHeight = nodes[0].height; for (int n = 1; n < nodeCount; n++) { minHeight = Mathf.Min(minHeight, nodes[n].height); maxHeight = Mathf.Min(maxHeight, nodes[n].height); } Node survivor = nodes[0]; survivor.position = toPoint; survivor.height = maxHeight; int liveEdgeCount = _shape.liveEdges.Count; for (int e = 0; e < liveEdgeCount; e++) { Edge edge = _shape.liveEdges[e]; if (!nodes.Contains(edge.nodeA) || !nodes.Contains(edge.nodeB)) { continue; } edge.UpdateValues(); if (nodes.Contains(edge.nodeA) && nodes.Contains(edge.nodeB)) { OffsetShapeLog.AddLine(edge.ToString(), "has collapsed", edge.length, _core.pointAccuracy); OffsetShapeLog.AddLine("Remove edge ", edge.ToString()); _shape.liveEdges.Remove(edge);//remove collapsed edge - from length reaching zero liveEdgeCount--; e--; continue; // if (!nodes.Contains(edge.nodeA)) // nodes.Add(edge.nodeA); // if (!nodes.Contains(edge.nodeB)) // nodes.Add(edge.nodeB); } //else affected edge if (nodes.Contains(edge.nodeA)) { edge.ReplaceNode(edge.nodeA, survivor); } if (nodes.Contains(edge.nodeB)) { edge.ReplaceNode(edge.nodeB, survivor); } } // OffsetShapeLog.AddLine("Collapse Nodes:"); // foreach (Node node in nodes) // OffsetShapeLog.Add(node.id + " "); // OffsetShapeLog.AddLine("to point: ", toPoint); // // Node newStaticNode = new Node(toPoint, height);//new static node to mark point of collapse // // _shape.AddStaticNode(newStaticNode);//add static node to node array // // OffsetShapeLog.AddLine("new static node added ", newStaticNode.id); // // // Node newLiveNode = new Node(toPoint, height);//new live node to continue shape forming // // for (int n = 0; n < nodeCount; n++) // { // nodes[n].position = toPoint; // nodes[n].height = maxHeight; // } // // Edge collapsedEdge = null; // for (int e = 0; e < liveEdgeCount; e++) // { // Edge edge = _shape.liveEdges[e]; // if (!nodes.Contains(edge.nodeA) || !nodes.Contains(edge.nodeB)) continue; // edge.UpdateValues(); // if (edge.length < _core.pointAccuracy)//when the edge reaches 0 length it has flipped and should be collapsed // { // OffsetShapeLog.AddLine(edge.ToString(), "has collapsed", edge.length, _core.pointAccuracy); // // _shape.mesh.CollapseEdge(edge, newLiveNode, newStaticNode); // OffsetShapeLog.AddLine("Remove edge ", edge.ToString()); // collapsedEdge = edge; // _shape.liveEdges.Remove(edge);//remove collapsed edge - from length reaching zero // liveEdgeCount--; // e--; // if (!nodes.Contains(edge.nodeA)) // nodes.Add(edge.nodeA); // if (!nodes.Contains(edge.nodeB)) // nodes.Add(edge.nodeB); // } // } // OffsetShapeLog.AddLine("find live node edges"); // for (int e = 0; e < liveEdgeCount; e++) // { // Edge edge = _shape.liveEdges[e]; // if (!edge.Contains(nodes)) continue; // if (nodes.Contains(edge.nodeA) && nodes.Contains(edge.nodeB)) // { // OffsetShapeLog.AddLine("Remove collapsed edge ", edge.ToString()); // _shape.liveEdges.Remove(edge);//remove collapsed edge - likely from parallel // liveEdgeCount--; // e--; // continue; // } // if (nodes.Contains(edge.nodeA) || newLiveNode == edge.nodeA) // { // OffsetShapeLog.AddLine("replace node a"); // edge.ReplaceNode(edge.nodeA, newLiveNode);//replace old live node reference to new one // liveEdges.Add(edge); // continue; // } // if (nodes.Contains(edge.nodeB) || newLiveNode == edge.nodeB) // { // OffsetShapeLog.AddLine("replace node b"); // edge.ReplaceNode(edge.nodeB, newLiveNode);//replace old live node reference to new one // liveEdges.Add(edge); // } // } for (int n = 0; n < nodeCount; n++) { Node node = nodes[n]; if (node == survivor) { continue; } // Utils.RetireFormingEdge(_shape, node, newStaticNode); _shape.liveNodes.Remove(node); } Utils.CheckParrallel(_shape); // OffsetShapeLog.AddLine("Live edges: ", liveEdges.Count); // if (liveEdges.Count > 0)//deal with left live edges after the collapse // { // _shape.AddLiveNode(newLiveNode);//new live node from collapse // Edge edgeA = null, edgeB = null; // liveEdgeCount = _shape.liveEdges.Count; // for (int e = 0; e < liveEdgeCount; e++)//find the two edges left from the collapse // { // Edge edge = _shape.liveEdges[e]; // if (!_shape.liveEdges.Contains(edge)) continue; // if (edge.nodeA == newLiveNode) edgeA = edge; // if (edge.nodeB == newLiveNode) edgeB = edge; // } // // if (edgeA != null && edgeB != null)//if there is a live edge // { // Node x = edgeA.GetOtherNode(newLiveNode); // Node y = edgeB.GetOtherNode(newLiveNode); // Utils.CalculateNodeDirAng(newLiveNode, x, y);//recalculate node angle // Utils.NewFormingEdge(_shape, newStaticNode, newLiveNode);//add new forming edge // } // else // { // OffsetShapeLog.AddLine("New live node has not been calculated ", newLiveNode.id); // } // } // foreach (Node node in nodes) // _data.mesh.ReplaceNode(node, newStaticNode); }
public void SplitEdge(SplitEvent e) { OffsetShapeLog.AddLine("Split event"); OffsetShapeLog.AddLine("by node ", e.node.id); OffsetShapeLog.AddLine(e.edge.ToString()); //nodes float realHeight = e.node.height; Node nodeStatic = (e.newStaticNode == null) ? new Node(e.point, realHeight) : e.newStaticNode; e.newStaticNode = nodeStatic; Node nodeOldA = e.edge.nodeA; Node nodeOldB = e.edge.nodeB; Edge[] edges = Utils.GetABEdge(shape, e.node); if (edges[0] == null || edges[1] == null) { //TODO work out what to really do here. return; } Edge byEdgeA = edges[0]; Edge byEdgeB = edges[1]; Node byNodeA = byEdgeA.GetOtherNode(e.node); Node byNodeB = byEdgeB.GetOtherNode(e.node); OffsetShapeLog.AddLine("by node a", byNodeA.id); OffsetShapeLog.AddLine("by node b", byNodeB.id); if (byNodeA == null || byNodeB == null) { return; } int insertionIndex = _core.shape.LiveIndex(e.node); _core.shape.AddStaticNode(nodeStatic); _core.shape.liveNodes.Remove(e.node); //discard the old edge OffsetShapeLog.AddLine("Discard old edge ", e.edge.ToString()); _core.shape.liveEdges.Remove(e.edge);// if (!_splitEdges.ContainsKey(e.edge)) { _splitEdges.Add(e.edge, e); } Utils.RetireFormingEdge(_core.shape, e.node, nodeStatic); Node newLiveNodeA = null; Node newLiveNodeB = null; //node a if (!e.edge.Contains(byNodeA)) { newLiveNodeA = new Node(e.point, realHeight); e.newLiveNodeA = newLiveNodeA; //calculate new node directions Utils.CalculateNodeDirAng(newLiveNodeA, byNodeA, nodeOldA); _core.shape.InsertLiveNode(insertionIndex, newLiveNodeA); byEdgeA.ReplaceNode(e.node, newLiveNodeA); //create the two new edges from the split Edge newEdgeA = new Edge(nodeOldA, newLiveNodeA); _core.shape.liveEdges.Add(newEdgeA); e.newLiveEdgeA = newEdgeA; Edge formingEdgeA = Utils.NewFormingEdge(_core.shape, nodeStatic, newLiveNodeA); OffsetShapeLog.AddLine("new live node a"); OffsetShapeLog.AddLine(newLiveNodeA.id); OffsetShapeLog.AddLine("new edges - old edge - forming edge a"); OffsetShapeLog.AddLine(newEdgeA.ToString()); OffsetShapeLog.AddLine(byEdgeA.ToString()); OffsetShapeLog.AddLine(formingEdgeA.ToString()); } else { Utils.RetireFormingEdge(_core.shape, byNodeA, nodeStatic); _core.shape.liveNodes.Remove(byNodeA); } //node b if (!e.edge.Contains(byNodeB)) { newLiveNodeB = new Node(e.point, realHeight); e.newLiveNodeB = newLiveNodeB; Utils.CalculateNodeDirAng(newLiveNodeB, nodeOldB, byNodeB); _core.shape.InsertLiveNode(insertionIndex, newLiveNodeB); byEdgeB.ReplaceNode(e.node, newLiveNodeB); Edge newEdgeB = new Edge(newLiveNodeB, nodeOldB); _core.shape.liveEdges.Add(newEdgeB); e.newLiveEdgeB = newEdgeB; Edge formingEdgeB = Utils.NewFormingEdge(_core.shape, nodeStatic, newLiveNodeB); OffsetShapeLog.AddLine("new live node b"); OffsetShapeLog.AddLine(newLiveNodeB.id); OffsetShapeLog.AddLine("new edges - old edge - forming edge b"); OffsetShapeLog.AddLine(byEdgeB.ToString()); OffsetShapeLog.AddLine(newEdgeB.ToString()); OffsetShapeLog.AddLine(formingEdgeB.ToString()); } else { Utils.RetireFormingEdge(_core.shape, byNodeB, nodeStatic); _core.shape.liveNodes.Remove(byNodeB); } if (newLiveNodeA != null && newLiveNodeB != null) { if (!_core.currentSplits.ContainsKey(newLiveNodeA.id)) { _core.currentSplits.Add(newLiveNodeA.id, new List <int>()); } _core.currentSplits[newLiveNodeA.id].Add(newLiveNodeB.id); if (!_core.currentSplits.ContainsKey(newLiveNodeB.id)) { _core.currentSplits.Add(newLiveNodeB.id, new List <int>()); } _core.currentSplits[newLiveNodeB.id].Add(newLiveNodeA.id); } }
public void SplitEdge(SplitEvent e) { OffsetShapeLog.AddLine("Split event"); OffsetShapeLog.AddLine("by node ", e.node.id); OffsetShapeLog.AddLine(e.edge.ToString()); //nodes Node nodeStatic = new Node(e.point, e.height); Node newLiveNodeA = new Node(e.point, e.height); Node newLiveNodeB = new Node(e.point, e.height); e.newLiveNodeA = newLiveNodeA; e.newLiveNodeB = newLiveNodeB; e.newStaticNode = nodeStatic; Node nodeOldA = e.edge.nodeA; Node nodeOldB = e.edge.nodeB; Edge byEdgeA = e.nodeEdgeA; Edge byEdgeB = e.nodeEdgeB; if (byEdgeA == null || byEdgeB == null) { //TODO work out what to really do here. return; } Node byNodeA = byEdgeA.GetOtherNode(e.node); Node byNodeB = byEdgeB.GetOtherNode(e.node); OffsetShapeLog.AddLine("by node a", byNodeA.id); OffsetShapeLog.AddLine("by node b", byNodeB.id); if (byNodeA == null || byNodeB == null) { return; } //calculate new node directions Utils.CalculateNodeDirAng(newLiveNodeA, byNodeA, nodeOldA); Utils.CalculateNodeDirAng(newLiveNodeB, nodeOldB, byNodeB); _shape.AddLiveNode(newLiveNodeA); _shape.AddLiveNode(newLiveNodeB); _shape.AddStaticNode(nodeStatic); _shape.liveNodes.Remove(e.node); //discard the old edge OffsetShapeLog.AddLine("Discard old edge ", e.edge.ToString()); _shape.liveEdges.Remove(e.edge);// byEdgeA.ReplaceNode(e.node, newLiveNodeA); byEdgeB.ReplaceNode(e.node, newLiveNodeB); //create the two new edges from the split Edge newEdgeA = new Edge(nodeOldA, newLiveNodeA); _shape.liveEdges.Add(newEdgeA); e.newLiveEdgeA = newEdgeA; Edge newEdgeB = new Edge(newLiveNodeB, nodeOldB); _shape.liveEdges.Add(newEdgeB); e.newLiveEdgeB = newEdgeB; //forming edges Utils.RetireFormingEdge(_shape, e.node, nodeStatic); Edge formingEdgeA = Utils.NewFormingEdge(_shape, nodeStatic, newLiveNodeA); Edge formingEdgeB = Utils.NewFormingEdge(_shape, nodeStatic, newLiveNodeB); // int aIndex = data.liveNodes.IndexOf(nodeLiveA); // int bIndex = data.liveNodes.IndexOf(nodeLiveB); newLiveNodeA.MoveForward(0.1f, 1); newLiveNodeB.MoveForward(0.1f, 1); if (!currentSplits.ContainsKey(newLiveNodeA.id)) { currentSplits.Add(newLiveNodeA.id, new List <int>()); } currentSplits[newLiveNodeA.id].Add(newLiveNodeB.id); if (!currentSplits.ContainsKey(newLiveNodeB.id)) { currentSplits.Add(newLiveNodeB.id, new List <int>()); } currentSplits[newLiveNodeB.id].Add(newLiveNodeA.id); // _shape.mesh.SplitEdge(e); OffsetShapeLog.AddLine("new live nodes"); OffsetShapeLog.AddLine(newLiveNodeA.id); OffsetShapeLog.AddLine(newLiveNodeB.id); OffsetShapeLog.AddLine("new edges - old edge - forming edge a"); OffsetShapeLog.AddLine(newEdgeA.ToString()); OffsetShapeLog.AddLine(byEdgeA.ToString()); OffsetShapeLog.AddLine(formingEdgeA.ToString()); OffsetShapeLog.AddLine("new edges - old edge - forming edge b"); OffsetShapeLog.AddLine(byEdgeB.ToString()); OffsetShapeLog.AddLine(newEdgeB.ToString()); OffsetShapeLog.AddLine(formingEdgeB.ToString()); Utils.CheckParrallel(_shape); }
private void CollapseNodes(List <Node> nodes, Vector2 toPoint, float height, BaseEvent evt) { if (nodes.Count < 2) { return; } OffsetShapeLog.AddLine("Collapse Nodes:"); foreach (Node node in nodes) { OffsetShapeLog.Add(node.id + " "); } OffsetShapeLog.AddLine("to point: ", toPoint); Node newStaticNode = (evt.newStaticNode == null) ? new Node(toPoint, height) : evt.newStaticNode;//new static node to mark point of collapse evt.newStaticNode = newStaticNode; _core.shape.AddStaticNode(newStaticNode);//add static node to node array int liveEdgeCount = _core.shape.liveEdges.Count; List <Edge> liveEdges = new List <Edge>(); // Node newLiveNode = new Node(toPoint, nodes[0].height);//new live node to continue shape forming int nodeCount = nodes.Count; for (int n = 0; n < nodeCount; n++) { nodes[n].position = toPoint; } for (int e = 0; e < liveEdgeCount; e++) { Edge edge = _core.shape.liveEdges[e]; if (!nodes.Contains(edge.nodeA) || !nodes.Contains(edge.nodeB)) { continue; } edge.UpdateValues(); if (edge.length < pointAccuracy)//when the edge reaches 0 length it has flipped and should be collapsed { OffsetShapeLog.AddLine(edge.ToString(), "has collapsed", edge.length, pointAccuracy); OffsetShapeLog.AddLine("Remove edge ", edge.ToString()); _core.shape.liveEdges.Remove(edge);//remove collapsed edge - from length reaching zero _collapsedEdges.Add(edge, newStaticNode); liveEdgeCount--; e--; if (!nodes.Contains(edge.nodeA)) { nodes.Add(edge.nodeA); } if (!nodes.Contains(edge.nodeB)) { nodes.Add(edge.nodeB); } } } OffsetShapeLog.AddLine("find live node edges"); Dictionary <Node, int> nodeOccurances = new Dictionary <Node, int>();//check parallel collapses for (int e = 0; e < liveEdgeCount; e++) { Edge edge = _core.shape.liveEdges[e]; if (!edge.Contains(nodes)) { continue; //unaffected edge } if (nodes.Contains(edge.nodeA) && nodes.Contains(edge.nodeB)) //edge is completely affected by the merge and has collapsed { OffsetShapeLog.AddLine("Remove collapsed edge ", edge.ToString()); _core.shape.liveEdges.Remove(edge);//remove collapsed edge _collapsedEdges.Add(edge, newStaticNode); liveEdgeCount--; e--; continue; } if (nodes.Contains(edge.nodeA))// || newLiveNode == edge.nodeA) { // edge.ReplaceNode(edge.nodeA, newLiveNode);//replace old live node reference to new one //TODO liveEdges.Add(edge); OffsetShapeLog.AddLine("Live edges: ", edge); if (nodeOccurances.ContainsKey(edge.nodeB)) { nodeOccurances[edge.nodeB]++; } else { nodeOccurances.Add(edge.nodeB, 1); } continue; } if (nodes.Contains(edge.nodeB))// || newLiveNode == edge.nodeB) { // edge.ReplaceNode(edge.nodeB, newLiveNode);//replace old live node reference to new one //TODO liveEdges.Add(edge); OffsetShapeLog.AddLine("Live edges: ", edge); if (nodeOccurances.ContainsKey(edge.nodeA)) { nodeOccurances[edge.nodeA]++; } else { nodeOccurances.Add(edge.nodeA, 1); } } } int affectedLiveEdges = liveEdges.Count; foreach (KeyValuePair <Node, int> kv in nodeOccurances) { OffsetShapeLog.AddLine("node occured: ", kv.Key.id, kv.Value); if (kv.Value > 1) { Node pinchedNode = kv.Key; OffsetShapeLog.AddLine("Pinched node: ", pinchedNode.id); pinchedNode.position = toPoint; for (int a = 0; a < affectedLiveEdges; a++) { shape.formingEdges[kv.Key].ReplaceNode(kv.Key, newStaticNode); if (liveEdges[a].Contains(kv.Key))//any live edges that contains the node should be culled - it has collapsed { Edge edge = liveEdges[a]; OffsetShapeLog.AddLine("Collapsed Edge: ", edge); liveEdges.Remove(edge); _core.shape.liveEdges.Remove(edge);//remove collapsed edge affectedLiveEdges--; a--; } } Utils.RetireFormingEdge(shape, kv.Key, newStaticNode); _core.shape.liveNodes.Remove(kv.Key); } } // for (int n = 0; n < nodeCount; n++) // { // Node node = nodes[n]; // Utils.RetireFormingEdge(_core.shape, node, newStaticNode); // _core.shape.liveNodes.Remove(node); //// _substitutions.Add(node, newLiveNode); TODO EEK! // } OffsetShapeLog.AddLine("Live edges: ", liveEdges.Count); if (affectedLiveEdges > 0)//deal with left live edges after the collapse - calculate the angle the new node needs to move into { float[] angles = new float[affectedLiveEdges]; int smallestAngleIndex = 0;//keep this for when we need to loop the angle comparison float smallestAngle = Mathf.Infinity; for (int a = 0; a < affectedLiveEdges; a++) { Node from = liveEdges[a].GetEdgeNode(nodes); Node to = liveEdges[a].GetOtherNode(from); Vector2 dir = (to.position - from.position).normalized; float angle = Utils.SignAngle(dir); angles[a] = angle; OffsetShapeLog.AddLine(liveEdges[a], angle); if (angle < smallestAngle) { smallestAngle = angle; smallestAngleIndex = a; } } Edge startEdge = null; for (int a = 0; a < affectedLiveEdges; a++) { if (nodes.Contains(liveEdges[a].nodeA)) { startEdge = liveEdges[a]; break; } } if (startEdge != null) { Edge[] orderedEdges = new Edge[affectedLiveEdges]; orderedEdges[0] = startEdge; Edge currentEdge = startEdge; float currentAngle = angles[liveEdges.IndexOf(currentEdge)]; int orderIndex = 1; OffsetShapeLog.AddLine("order edges by angle"); OffsetShapeLog.AddLine(0, startEdge); while (orderIndex < affectedLiveEdges) { Edge candidate = null; float candidateAngle = Mathf.Infinity; for (int a = 0; a < affectedLiveEdges; a++) { Edge nextEdge = liveEdges[a]; if (currentEdge == nextEdge) { continue; } float nextAngle = angles[liveEdges.IndexOf(nextEdge)]; if (nextAngle > currentAngle) { if (nextAngle < candidateAngle) { candidateAngle = nextAngle; candidate = nextEdge; } } } if (candidate == null) { candidate = liveEdges[smallestAngleIndex]; } if (candidate != null) { OffsetShapeLog.AddLine(orderIndex, candidate); orderedEdges[orderIndex] = candidate; orderIndex++; currentEdge = candidate; currentAngle = angles[liveEdges.IndexOf(currentEdge)]; } } OffsetShapeLog.AddLine("affected Live Edge count" + affectedLiveEdges); List <Node> newLiveNodes = new List <Node>(); if (affectedLiveEdges % 2 != 0) { // Debug.LogError("affected Live Edge count uneven: "+ affectedLiveEdges); // Debug.LogError(""); return; } for (int o = 0; o < affectedLiveEdges; o += 2) { Edge splitEdgeA = orderedEdges[o]; Edge splitEdgeB = orderedEdges[o + 1]; OffsetShapeLog.AddLine("split Edge A", splitEdgeA); OffsetShapeLog.AddLine("split Edge B", splitEdgeB); Node newLiveNode = new Node(toPoint, nodes[0].height); //new live node to continue shape forming _core.shape.AddLiveNode(newLiveNode); //new live node from collapse newLiveNodes.Add(newLiveNode); if (nodes.Contains(splitEdgeA.nodeA)) { splitEdgeA.ReplaceNode(splitEdgeA.nodeA, newLiveNode);//replace old live node reference to new one } else { splitEdgeA.ReplaceNode(splitEdgeA.nodeB, newLiveNode);//replace old live node reference to new one } if (nodes.Contains(splitEdgeB.nodeA)) { splitEdgeB.ReplaceNode(splitEdgeB.nodeA, newLiveNode);//replace old live node reference to new one } else { splitEdgeB.ReplaceNode(splitEdgeB.nodeB, newLiveNode);//replace old live node reference to new one } Node x = splitEdgeA.GetOtherNode(newLiveNode); Node y = splitEdgeB.GetOtherNode(newLiveNode); Utils.CalculateNodeDirAng(newLiveNode, x, y); //recalculate node angle Utils.NewFormingEdge(_core.shape, newStaticNode, newLiveNode); //add new forming edge } int newLiveNodeCount = newLiveNodes.Count; for (int l = 0; l < newLiveNodeCount; l++) { Node lNode = newLiveNodes[l]; if (!_core.currentSplits.ContainsKey(lNode.id)) { _core.currentSplits.Add(lNode.id, new List <int>()); } for (int lb = 0; lb < newLiveNodeCount; lb++) { if (l == lb) { continue; } Node lbNode = newLiveNodes[lb]; _core.currentSplits[lNode.id].Add(lbNode.id); } } } // Edge edgeA = null, edgeB = null; // liveEdgeCount = _core.shape.liveEdges.Count; // for (int e = 0; e < liveEdgeCount; e++)//find the two edges left from the collapse // { // Edge edge = _core.shape.liveEdges[e]; // if (!_core.shape.liveEdges.Contains(edge)) continue;//not a live edge // if (edge.nodeA == newLiveNode) edgeA = edge; // if (edge.nodeB == newLiveNode) edgeB = edge; // } // // if (edgeA != null && edgeB != null)//if there is a live edge // { // Node x = edgeA.GetOtherNode(newLiveNode); // Node y = edgeB.GetOtherNode(newLiveNode); // Utils.CalculateNodeDirAng(newLiveNode, x, y);//recalculate node angle // Utils.NewFormingEdge(_core.shape, newStaticNode, newLiveNode);//add new forming edge // } // else // { // OffsetShapeLog.AddLine("New live node has not been calculted ", newLiveNode.id); // } } for (int n = 0; n < nodeCount; n++) { Node node = nodes[n]; Utils.RetireFormingEdge(_core.shape, node, newStaticNode); _core.shape.liveNodes.Remove(node); // _substitutions.Add(node, newLiveNode); TODO EEK! } Utils.CheckParrallel(_core.shape); }