public Node RelatedNode(Edge other) { if (other.Contains(_nodeA)) { return(nodeA); } if (other.Contains(_nodeB)) { return(nodeB); } return(null); }
private bool isPartOfShape(Node subject, Edge edge) { Node startNode = edge.nodeA; Node thisNode = edge.nodeB; Node lastNode = edge.nodeA; if (edge.Contains(subject)) { return(true); } int liveEdgeCount = _shape.liveEdges.Count; int it = liveEdgeCount * liveEdgeCount; while (thisNode != startNode) { for (int l = 0; l < liveEdgeCount; l++) { Edge thisEdge = _shape.liveEdges[l]; if (thisEdge.Contains(thisNode) && !thisEdge.Contains(lastNode)) { lastNode = thisNode; thisNode = thisEdge.GetOtherNode(lastNode); break; } } if (thisNode == subject) { return(true); } it--; if (it < 0) { break; } } return(false); }
public static void CalculateNodeDirAng(Shape data, Node node) { Edge edgeA = null; Edge edgeB = null; int liveEdgeCount = data.liveEdges.Count; for (int e = 0; e < liveEdgeCount; e++) { Edge edge = data.liveEdges[e]; if (edge.Contains(node)) { if (edgeA == null) { edgeA = edge; } else if (edgeB == null) { edgeB = edge; } else { return;//wtf? } } } if (edgeA != null && edgeB != null) { Node x = edgeA.GetOtherNode(node); Node y = edgeB.GetOtherNode(node); if (edgeA.nodeA == node) { CalculateNodeDirAng(node, x, y); } else { CalculateNodeDirAng(node, y, x); } } }
public void CollapseNodes(List <Node> nodes, Vector2 toPoint, float height) { 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); int liveEdgeCount = _shape.liveEdges.Count; List <Edge> liveEdges = new List <Edge>(); Node newLiveNode = new Node(toPoint, height);//new live node to continue shape forming 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); } for (int n = 0; n < nodeCount; n++) { nodes[n].position = toPoint; nodes[n].height = maxHeight; } 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 < pointAccuracy)//when the edge reaches 0 length it has flipped and should be collapsed { OffsetShapeLog.AddLine(edge.ToString(), "has collapsed", edge.length, pointAccuracy); // _shape.mesh.CollapseEdge(edge, newLiveNode, newStaticNode); OffsetShapeLog.AddLine("Remove edge ", edge.ToString()); _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(edge.length < pointAccuracy) 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 // _shape.mesh.EdgeComplete(edge); 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 // _shape.mesh.ReplaceNode(edge.nodeA, newLiveNode); 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 // _shape.mesh.ReplaceNode(edge.nodeB, newLiveNode); liveEdges.Add(edge); } } for (int n = 0; n < nodeCount; n++) { Node node = nodes[n]; 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 calculted ", newLiveNode.id); } } // foreach (Node node in nodes) // _data.mesh.ReplaceNode(node, newStaticNode); }
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); } } }
public void SplitEdge(SplitEvent e) { Edge oldLiveEdge = e.edge; Vector3 splitTangent = Utils.ToV3(oldLiveEdge.direction); SkeletonFace oldFace = _nodeDic[oldLiveEdge]; Node staticNode = e.newStaticNode; Edge newLiveEdgeA = e.newLiveEdgeA; Edge newLiveEdgeB = e.newLiveEdgeB; Node oldNode = e.node; Node newLiveNodeA = e.newLiveNodeA; Node newLiveNodeB = e.newLiveNodeB; //centre tri SkeletonTri newTriangle = new SkeletonTri(oldFace[0], oldFace[1], staticNode, splitTangent); AddTriangle(newTriangle); if (!_nodeDic.ContainsKey(e.nodeEdgeA)) { return; } SkeletonFace faceA = _nodeDic[e.nodeEdgeA]; faceA.ReplaceNode(oldNode, newLiveNodeA); //left tri SkeletonTri newTriangleA = new SkeletonTri(staticNode, faceA[0], newLiveNodeA, splitTangent); AddTriangle(newTriangleA); if (!_nodeDic.ContainsKey(e.nodeEdgeB)) { return; } SkeletonFace faceB = _nodeDic[e.nodeEdgeB]; faceB.ReplaceNode(oldNode, newLiveNodeB); //right tri SkeletonTri newTriangleB = new SkeletonTri(faceB[1], staticNode, newLiveNodeB, splitTangent); AddTriangle(newTriangleB); RemoveFace(oldFace); AddFace(newLiveEdgeA, oldFace[0], staticNode); AddFace(newLiveEdgeB, staticNode, oldFace[1]); int oldNodeReferenceCount = 0; if (_edgeDic.ContainsKey(oldNode)) { oldNodeReferenceCount = _edgeDic[oldNode].Count; } for (int r = 0; r < oldNodeReferenceCount; r++) { SkeletonFace face = _edgeDic[oldNode][r]; Edge liveEdge = face.liveEdge; if (liveEdge.Contains(newLiveNodeA)) { if (!_edgeDic.ContainsKey(newLiveNodeA)) { _edgeDic.Add(newLiveNodeA, new List <SkeletonFace>()); } _edgeDic[newLiveNodeA].Add(face); } if (liveEdge.Contains(newLiveNodeB)) { if (!_edgeDic.ContainsKey(newLiveNodeB)) { _edgeDic.Add(newLiveNodeB, new List <SkeletonFace>()); } _edgeDic[newLiveNodeB].Add(face); } } }
public void CollapseEdge(Edge liveEdge, Node newLiveNode, Node newStaticNode) { Edge edgeA = null, edgeB = null; if (!_edgeDic.ContainsKey(newLiveNode)) { _edgeDic.Add(newLiveNode, new List <SkeletonFace>()); } foreach (SkeletonFace face in _faces) { Edge liveFaceEdge = face.liveEdge; if (liveEdge == liveFaceEdge) { continue; } if (liveFaceEdge.Contains(liveEdge.nodeA)) { edgeA = liveFaceEdge; _edgeDic[newLiveNode].Add(face); } if (liveFaceEdge.Contains(liveEdge.nodeB)) { edgeB = liveFaceEdge; _edgeDic[newLiveNode].Add(face); } } if (edgeA != null) { SkeletonFace faceA = _nodeDic[edgeA]; Node n0 = faceA[0]; Node n1 = faceA[1]; Node n2 = newStaticNode; if (n0 != n2 && n1 != n2) { SkeletonTri newtriangle = new SkeletonTri(n0, n1, n2, faceA.tangent); AddTriangle(newtriangle);//remove static triangle from face - add to triangle stack faceA.ReplaceNode(faceA[1], newStaticNode); faceA.ReplaceNode(faceA[3], newLiveNode); } } if (edgeB != null) { SkeletonFace faceB = _nodeDic[edgeB]; Node n0 = faceB[0]; Node n1 = faceB[1]; Node n2 = newStaticNode; if (n0 != n2 && n1 != n2) { SkeletonTri newtriangle = new SkeletonTri(n0, n1, n2, faceB.tangent); AddTriangle(newtriangle); faceB.ReplaceNode(faceB[0], newStaticNode); faceB.ReplaceNode(faceB[2], newLiveNode); } } if (_nodeDic.ContainsKey(liveEdge)) { SkeletonFace liveFace = _nodeDic[liveEdge]; Node lfn0 = liveFace[0]; Node lfn1 = liveFace[1]; Node lfn2 = newStaticNode; if (lfn0 != lfn2 && lfn1 != lfn2) { SkeletonTri newFaceTriangle = new SkeletonTri(lfn0, lfn1, lfn2, liveFace.tangent); AddTriangle(newFaceTriangle); } _nodeDic.Remove(liveEdge); RemoveFace(liveFace); } }
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); }