Beispiel #1
0
 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);
     }
 }
Beispiel #2
0
        public static Edge NewFormingEdge(Shape data, Node staticNode, Node liveNode)
        {
            Edge newFormingEdge = new Edge(staticNode, liveNode);

            newFormingEdge.UpdateValues();
            if (data.formingEdges.ContainsKey(liveNode))
            {
                data.formingEdges.Remove(liveNode);
            }
            data.formingEdges.Add(liveNode, newFormingEdge);
            return(newFormingEdge);
        }
Beispiel #3
0
        public void OffsetPoly(float direction)
        {
            if (!_init)
            {
                return;
            }

            float amount        = _shape.shrinkLength * SHINK_MULTIPLIER * Mathf.Sign(direction);
            int   liveEdgeCount = _shape.liveEdges.Count;
            int   liveNodeCount = _shape.liveNodes.Count;

            if (liveNodeCount == 0 || liveEdgeCount == 0)//nothing more to calculate
            {
                OffsetShapeLog.AddLine("Skeleton Complete");
                _complete = true;
                if (OnCompleteEvent != null)
                {
                    OnCompleteEvent();
                }
                return;
            }

            bool  earlyTermination = false;
            float directionSign    = Mathf.Sign(direction);
            float maxOffsetSign    = Mathf.Sign(maxOffset);
            float useMaxOffset     = (directionSign == maxOffsetSign) ? maxOffset : -maxOffset;

            for (int l = 0; l < liveNodeCount; l++)
            {
                Node node = _shape.liveNodes[l];
                //                if(l==0)Debug.Log(node.height+" "+amount+" "+useMaxOffset);
                if (useMaxOffset > 0)
                {
                    if (node.height + amount >= useMaxOffset)//terminate nodes that have reached a defined maximum
                    {
                        amount           = useMaxOffset - node.height;
                        earlyTermination = true;
                    }
                }
                else if (useMaxOffset < 0)
                {
                    if (node.height + amount <= useMaxOffset)//terminate nodes that have reached a defined maximum
                    {
                        amount           = useMaxOffset - node.height;
                        earlyTermination = true;
                    }
                }
            }

            float maxMovement = 0;

            liveNodeCount = _shape.liveNodes.Count;
            float[] angleNodeMovements = new float[liveNodeCount];
            for (int l = 0; l < _shape.liveNodes.Count; l++)
            {
//                Debug.Log(l);
                Node node = _shape.liveNodes[l];//TODO out of range error
                angleNodeMovements[l] = amount / Mathf.Sin(node.angle * 0.5f * Mathf.Deg2Rad);
                if (Mathf.Abs(angleNodeMovements[l]) > Mathf.Abs(maxMovement) && Mathf.Abs(angleNodeMovements[l]) > 0)
                {
                    maxMovement = angleNodeMovements[l];

                    if (node.angle > 350)
                    {
                        Edge[] edges = Utils.GetABEdge(shape, node);
                        if (edges[0] == null || edges[1] == null)
                        {
                            continue;
                        }
                        float shortestLength = (edges[0].length + edges[1].length) * 0.5f;
                        //                        Debug.Log(Mathf.Abs(shortestLength / amount));
                        maxMovement /= Mathf.Abs(shortestLength / amount);
                    }

                    //                    Debug.Log(node.id+" "+node.angle);
                }
            }

            float angleScale = amount / maxMovement;

            if (angleScale == Mathf.Infinity)
            {
                angleScale = 1;
            }
            OffsetShapeLog.AddLine(angleScale);

            for (int l = 0; l < _shape.liveNodes.Count; l++)
            {
                Node node = _shape.liveNodes[l];
                if (node.direction.magnitude < Mathf.Epsilon)//directionless node
                {
                    _shape.liveNodes.Remove(node);
                    liveNodeCount--;
                    l--;
                    continue;
                }
                node.MoveForward(angleNodeMovements[l] * angleScale, amount * angleScale);
            }

            Vector2[] edgeMovements = new Vector2[liveEdgeCount];
            if (calculateInteractions)
            {
                //Event log will collect all events and sort them for us for use once we're happy everything has been processed
                EventLog eventLog = new EventLog();
                EventLog.pointAccuracy   = pointAccuracy;
                EventLog.percentAccuracy = percentAccuracy;

                //flip events
                for (int e = 0; e < liveEdgeCount; e++)//TODO check laters
                {
                    Edge edge = _shape.liveEdges[e];
                    edge.DebugDraw(Color.cyan);
                    edge.UpdateValues();//update the edge values to reflect the new node positions
                    Node nodeA = edge.nodeA;
                    Node nodeB = edge.nodeB;
                    edgeMovements[e] = (nodeA.movement + nodeB.movement) * 0.5f;
                    Vector2 intersectionPoint;
                    if (Utils.Intersects(edge.nodeA.previousPosition, edge.nodeA.position, edge.nodeB.previousPosition, edge.nodeB.position, out intersectionPoint))
                    {
                        eventLog.AddEvent(eventLog.CreateFlipEvent(edge, intersectionPoint));
                    }
                }

                //

                //split events
                for (int n = 0; n < _shape.liveNodeCount; n++)
                {
                    Node node   = _shape.liveNodes[n];
                    int  nodeID = node.id;

                    //find connecting nodes of splitting node
                    Node nodeA = null, nodeB = null;
                    for (int e = 0; e < liveEdgeCount; e++)
                    {
                        Edge edge = _shape.liveEdges[e];
                        if (edge.Contains(node))
                        {
                            if (edge.nodeA == node)
                            {
                                nodeB = edge.nodeB;
                            }
                            if (edge.nodeB == node)
                            {
                                nodeA = edge.nodeA;
                            }
                        }
                    }


                    for (int e = 0; e < liveEdgeCount; e++)
                    {
                        Edge edge = _shape.liveEdges[e];
                        if (edge.Contains(node))
                        {
                            continue;                     //nodes can't split their own edges - carry on!
                        }
                        if (nodeA != null && edge.Contains(nodeA))
                        {
                            continue;                                       //nodes can't split adjacent edges - carry on!
                        }
                        if (nodeB != null && edge.Contains(nodeB))
                        {
                            continue;                          //nodes can't split adjacent edges - carry on!
                        }
                        if (currentSplits.ContainsKey(nodeID)) //previous splits should never intersect - ingore
                        {
                            if (currentSplits[nodeID].Contains(edge.nodeA.id))
                            {
                                continue;
                            }
                            if (currentSplits[nodeID].Contains(edge.nodeB.id))
                            {
                                continue;
                            }
                        }

                        //                        if(!isPartOfShape(node, edge)) continue;

                        Vector2 edgeMovement = edgeMovements[e];
                        Vector2 nodeMovement = node.direction * node.distance - edgeMovement; //simulate collision by moving the point by the vectors of both the point and the edge,
                        //note: collisions are simpler if only one body is moving so we're going to add the edge vector onto the point vector, making the edge remain stationary
                        Vector2 calculationPoint = node.previousPosition + nodeMovement;      //calculate the point vector by adding the edge one to it
                        Vector2 edgePosA         = edge.nodeA.previousPosition;
                        Vector2 edgePosB         = edge.nodeB.previousPosition;
//                        float intersectionalDot = Vector2.Dot(edgeMovement.normalized, node.direction.normalized);

                        //                        OffsetShapeLog.DrawLine(node.previousPosition, calculationPoint,new Color(1,0,0,0.4f));
                        //                        OffsetShapeLog.DrawLine(edgePosA, edgePosB, new Color(1, 0, 1, 0.4f));

                        Vector2 intersectionPoint;
                        float   percent    = 0;
                        bool    intersects = false;
                        //                        if (intersectionalDot < -10.75f)
                        //                        {
                        //                            Debug.DrawLine(Utils.ToV3(node.previousPosition), Utils.ToV3(calculationPoint), Color.red);
                        //                            intersects = Utils.Intersects(node.previousPosition, calculationPoint, edgePosA, edgePosB, out intersectionPoint);
                        //                            if (intersects)
                        //                            {
                        //                                Debug.DrawLine(Utils.ToV3(calculationPoint), Utils.ToV3(calculationPoint) + Vector3.up * 5, Color.magenta);
                        //                            }
                        //                            Vector2 a = node.previousPosition;
                        //                            Vector2 b = node.position;
                        //                            float movementMag = nodeMovement.magnitude;
                        //                            float intersectionMag = (intersectionPoint - a).magnitude;
                        //                            percent = intersectionMag / movementMag;
                        //                            intersectionPoint = Vector2.Lerp(a, b, percent);//translate the point to the real movement point
                        //                        }
                        //                        else
                        //                        {
                        bool dbi = false;//node.id == 14;
                        intersects       = Utils.SweepIntersects2(edge.nodeA.previousPosition, edge.nodeB.previousPosition, edge.nodeA.position, edge.nodeB.position, node.previousPosition, node.position, out intersectionPoint, out percent, 0.1f, dbi);
                        calculationPoint = node.position;
                        //                        }
                        //                        if(Utils.Intersects(node.previousPosition, calculationPoint, edgePosA, edgePosB, out intersectionPoint))
                        //                        if (Utils.SweepIntersects(edge.nodeA.previousPosition , edge.nodeB.previousPosition, edge.nodeA.position, edge.nodeB.position, node.previousPosition, node.position, out intersectionPoint, out percent))
                        if (intersects)
                        {
                            for (int be = 0; be < shape.baseEdges.Count; be++)
                            {
                                Edge baseEdge = shape.edges[be];
                                if (Utils.FastLineIntersection(node.previousPosition, node.position, baseEdge.positionA, baseEdge.positionB))
                                {
                                    intersects = false;
                                }
                                if (Utils.FastLineIntersection(edge.nodeA.position, edge.nodeB.position, baseEdge.positionA, baseEdge.positionB))
                                {
                                    intersects = false;
                                }
                            }
                        }
                        if (intersects)
                        {
                            OffsetShapeLog.AddLine("Split event detected");
                            SplitEvent splitEvent = new SplitEvent();
                            splitEvent.node = node;
                            splitEvent.edge = edge;
                            OffsetShapeLog.AddLine("node " + node.id);
                            OffsetShapeLog.AddLine("splits edge " + edge.ToString());

                            OffsetShapeLog.DrawLine(node.previousPosition, calculationPoint, Color.red);
                            OffsetShapeLog.DrawLine(edgePosA, edgePosB, Color.magenta);

                            //                            Vector2 a = node.previousPosition;
                            //                            Vector2 b = node.position;
                            //                            Vector2 x = intersectionPoint;//intersectionInfo.Point0;

                            //                            float movementMag = nodeMovement.magnitude;
                            //                            float intersectionMag = (x - a).magnitude;
                            //                            float percent = intersectionMag / movementMag;
                            OffsetShapeLog.AddLine("at percent " + percent);
                            //                            Vector2 actualIntersectionPoint = Vector2.Lerp(a, b, percent);//translate the point to the real movement point

                            float newLengthA = (intersectionPoint - edge.positionA).magnitude;
                            float newLengthB = (intersectionPoint - edge.positionB).magnitude;
                            OffsetShapeLog.AddLine("line a length ", newLengthA);
                            OffsetShapeLog.AddLine("line b length ", newLengthB);

                            SplitEvent sEvent = eventLog.CreateSplitEvent(_shape, node, edge, intersectionPoint, percent, calculationPoint);
                            if (sEvent == null)
                            {
                                continue;
                            }
                            if (newLengthA > pointAccuracy && newLengthB > pointAccuracy)
                            {
                                eventLog.AddEvent(sEvent);//can split - split point not close to either edge nodes
                            }
                            else
                            {
                                Node[] nodes = null;
                                if (newLengthA < pointAccuracy && newLengthB < pointAccuracy)
                                {
                                    nodes = new[] { node, edge.nodeA, edge.nodeB }
                                }
                                ;                                                  //point will split the edge into two edges that can't exist - collapse all nodes
                                else if (newLengthA < pointAccuracy)
                                {
                                    nodes = new[] { node, edge.nodeA }
                                }
                                ;                                      //split point close to node a - collapse split node into edge.nodea
                                else if (newLengthB < pointAccuracy)
                                {
                                    nodes = new[] { node, edge.nodeB }
                                }
                                ;                                      //split point close to node b - collapse split node into edge.nodeb
                                if (nodes != null)
                                {
                                    MergedEvent mEvent = eventLog.CreateMergeEvent(nodes, intersectionPoint, percent, calculationPoint);
                                    mEvent.Merge(sEvent);
                                    eventLog.AddEvent(mEvent);
                                }
                            }
                        }
                    }
                }

                currentSplits.Clear();
                int eventCount = eventLog.count;
                OffsetShapeLog.AddLine("event count: ", eventCount);
                if (eventCount > 0)
                {
                    float percent = eventLog.percent;
                    earlyTermination = false;
                    foreach (Node node in _shape.liveNodes)
                    {
                        node.MoveBack(percent);//move all nodes back to the position of the event
                    }
                    foreach (Edge edge in _shape.liveEdges)
                    {
                        edge.UpdateValues();//update all edges to reflect this
                    }
                    foreach (Node node in _shape.liveNodes)
                    {
                        if (_shape.formingEdges.ContainsKey(node))
                        {
                            _shape.formingEdges[node].UpdateValues();
                        }
                    }
                    for (int e = 0; e < eventCount; e++)
                    {
                        IEvent sevent = eventLog[e];
                        sevent.DrawDebug();
                        OffsetShapeLog.AddLine(string.Format("Event {0} of type {4} at {1} percent and {2},{3}", e, sevent.percent, sevent.point.x, sevent.point.y, sevent.GetType()));
                        switch (sevent.GetType().ToString())
                        {
                        case "BuildR2.ShapeOffset.FlipEvent":
                            FlipEvent fEvent = (FlipEvent)sevent;
                            OffsetShapeLog.AddLine(fEvent.ToString());
                            if (OnFlipEvent != null)
                            {
                                OnFlipEvent(fEvent);
                            }
                            //                            CollapseEdge(fEvent.edge, fEvent.point, fEvent.height);
                            break;

                        case "BuildR2.ShapeOffset.SplitEvent":
                            SplitEvent sEvent = (SplitEvent)sevent;
                            OffsetShapeLog.AddLine(sevent.ToString());
                            if (OnSplitEvent != null)
                            {
                                OnSplitEvent(sEvent);
                            }
                            //                            SplitEdge(sEvent);
                            break;

                        case "BuildR2.ShapeOffset.MergedEvent":
                            MergedEvent mEvent = (MergedEvent)sevent;
                            OffsetShapeLog.AddLine(mEvent.ToString());
                            if (OnMergedEvent != null)
                            {
                                OnMergedEvent(mEvent);
                            }
                            //                            MergeEvent(mEvent);
                            break;
                        }
                    }
                }
                else
                {
                    if (!earlyTermination)
                    {
                        float percent = 1.0f - percentAccuracy;
                        earlyTermination = false;
                        foreach (Node node in _shape.liveNodes)
                        {
                            node.MoveBack(percent);//move all nodes back to the position of the event
                        }
                        foreach (Edge edge in _shape.liveEdges)
                        {
                            edge.UpdateValues();//update all edges to reflect this
                        }
                    }
                    foreach (Node node in _shape.liveNodes)
                    {
                        if (_shape.formingEdges.ContainsKey(node))
                        {
                            _shape.formingEdges[node].UpdateValues();
                        }
                    }
                }
            }

            if (earlyTermination)
            {
                _complete = true;
                _shape.TerminateAllNodes();
                if (OnCompleteEvent != null)
                {
                    OnCompleteEvent();
                }
                return;
            }
            else
            {
                //recalculate node directions
                foreach (Node node in _shape.liveNodes)
                {
                    Utils.CalculateNodeDirAng(_shape, node);
                }
            }
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }