public static FindEdgeByNodes ( |
||
n1 | ||
n2 | ||
_edges | List |
|
return |
public void CutAcuteAngles() { if (type != PrimitiveType.MinimalCycle) { return; } List <Node> _nodesToRemove = new List <Node>(); List <Node> _newNodes = new List <Node>(); List <Edge> _newEdges = new List <Edge>(); // Old edges are kept in the edges list, but node changes are saved here and refreshed to the edges once all angles are checked Dictionary <string, List <NodePair> > nodesToSwitchInEdges = new Dictionary <string, List <NodePair> >(); for (int i = 0; i < nodes.Count; i++) { Node prevNode = EdgeGraphUtility.GetNode(nodes[i].adjacents[0], ref nodes); Node nextNode = EdgeGraphUtility.GetNode(nodes[i].adjacents[1], ref nodes); Vector3 dirToPrev = (prevNode.Position - nodes[i].Position).normalized; Vector3 dirToNext = (nextNode.Position - nodes[i].Position).normalized; Edge prevEdge = EdgeGraphUtility.FindEdgeByNodes(nodes[i], prevNode, edges); Edge nextEdge = EdgeGraphUtility.FindEdgeByNodes(nodes[i], nextNode, edges); float angle = Vector3.Angle(dirToPrev, dirToNext); if (angle < 45f) { // Move nodes so that the cut side is 1f wide float distanceToMove = .55f / Mathf.Sin(Mathf.Deg2Rad * angle / 2f); float distToPrev = Vector3.Distance(prevNode.Position, nodes[i].Position); float distToNext = Vector3.Distance(nextNode.Position, nodes[i].Position); if (distanceToMove > distToPrev) { distanceToMove = distToPrev * .8f; } if (distanceToMove > distToNext) { distanceToMove = distToNext * .8f; } Vector3 newNodePrevPos = (nodes[i].Position + dirToPrev * distanceToMove); Vector3 newNodeNextPos = (nodes[i].Position + dirToNext * distanceToMove); string oldNodeID = nodes[i].ID; // Remove old node _nodesToRemove.Add(nodes[i]); // Make new nodes Node newNodeToPrev = new Node(newNodePrevPos); Node newNodeToNext = new Node(newNodeNextPos); if (!nodesToSwitchInEdges.ContainsKey(prevEdge.ID)) { nodesToSwitchInEdges.Add(prevEdge.ID, new List <NodePair>()); } nodesToSwitchInEdges[prevEdge.ID].Add(new NodePair(oldNodeID, newNodeToPrev.ID)); if (!nodesToSwitchInEdges.ContainsKey(nextEdge.ID)) { nodesToSwitchInEdges.Add(nextEdge.ID, new List <NodePair>()); } nodesToSwitchInEdges[nextEdge.ID].Add(new NodePair(oldNodeID, newNodeToNext.ID)); // Add the new edge Edge newEdge = new Edge(newNodeToNext.ID, newNodeToPrev.ID); _newEdges.Add(newEdge); // Add new nodes to the dict _newNodes.Add(newNodeToPrev); _newNodes.Add(newNodeToNext); } } foreach (var n in _nodesToRemove) { for (int i = 0; i < nodes.Count; i++) { if (nodes[i].ID == n.ID) { nodes.RemoveAt(i); i--; } } } foreach (var kvp in nodesToSwitchInEdges) { for (int i = 0; i < edges.Count; i++) { if (edges[i].ID == kvp.Key) { Edge e = edges[i]; for (int j = 0; j < kvp.Value.Count; j++) { NodePair pair = kvp.Value[j]; if (e.Node1 == pair.oldNode) { e.Node1 = pair.newNode; } if (e.Node2 == pair.oldNode) { e.Node2 = pair.newNode; } } } } } _newEdges.ForEach((e) => edges.Add(e)); _newNodes.ForEach((n) => nodes.Add(n)); EdgeGraphUtility.CleanUpEdges(ref nodes, ref edges); EdgeGraphUtility.CheckAdjacentNodes(ref nodes, ref edges); }
// Algorithms from http://www.geometrictools.com/Documentation/MinimalCycleBasis.pdf // The Minimal Cycle Basis for a Planar Graph by David Eberly /// <summary> /// Attempts to find minimal cycles /// </summary> public static void ExtractPrimitive(Node _n0, ref List <Node> _nodes, ref List <Edge> _edges, ref List <Primitive> _primitives) { List <Node> visited = new List <Node>(); List <Node> sequence = new List <Node>(); EdgeGraphUtility.CheckAdjacentNodes(ref _nodes, ref _edges); if (_n0.adjacents.Count == 0) { EdgeGraphUtility.RemoveNodeAndCleanAdjacents(_n0, ref _nodes, ref _edges); return; } sequence.Add(_n0); Node _n1 = GetClockwiseMostAdjacent(null, _n0, ref _nodes); Node prev = _n0; Node curr = _n1; while (curr != null && curr != _n0 && !visited.Contains(curr)) { sequence.Add(curr); visited.Add(curr); Node next = GetCounterClockwiseMostAdjacent(prev, curr, ref _nodes); prev = curr; curr = next; } if (curr == null) { // Filament found, not necessarily rooted at prev ExtractFilament(prev, EdgeGraphUtility.GetNode(prev.adjacents[0], ref _nodes), ref _nodes, ref _edges, ref _primitives); } else if (curr == _n0) { // Minimal cycle found Primitive primitive = new Primitive(Primitive.PrimitiveType.MinimalCycle); primitive.nodes.AddRange(sequence); for (int i = 0; i < sequence.Count; i++) { Node n1; Node n2; if (i == sequence.Count - 1) { n1 = sequence[i]; n2 = sequence[0]; } else { n1 = sequence[i]; n2 = sequence[i + 1]; } Edge e = EdgeGraphUtility.FindEdgeByNodes(n1, n2, _edges); if (e != null) { primitive.edges.Add(e); e.isPartOfCycle = true; } } EdgeGraphUtility.RemoveEdgeAndCleanAdjacents(_n0, _n1, ref _nodes, ref _edges); if (_n0.adjacents.Count == 1) { // Remove the filament rooted at v0 ExtractFilament(_n0, EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes), ref _nodes, ref _edges, ref _primitives); } if (_n1.adjacents.Count == 1) { // Remove the filament rooted at v1 ExtractFilament(_n1, EdgeGraphUtility.GetNode(_n1.adjacents[0], ref _nodes), ref _nodes, ref _edges, ref _primitives); } _primitives.Add(primitive); } else // curr was visited earlier { // A cycle has been found, but is not guaranteed to be a minimal cycle. // This implies v0 is part of a filament // Locate the starting point for the filament by traversing from v0 away from the initial v1 while (_n0.adjacents.Count == 2) { if (_n0.adjacents[0] != _n1.ID) { _n1 = _n0; _n0 = EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes); } else { _n1 = _n0; _n0 = EdgeGraphUtility.GetNode(_n0.adjacents[1], ref _nodes); } } ExtractFilament(_n0, _n1, ref _nodes, ref _edges, ref _primitives); } }
void ShiftNodes() { if (type != PrimitiveType.MinimalCycle) { return; } Dictionary <string, Vector3> newNodePositionDict = new Dictionary <string, Vector3>(); for (int i = 0; i < nodes.Count; i++) { position += nodes[i].Position; if (nodes[i].adjacents.Count != 2) { continue; } Node prevNode = null; Node nextNode = null; if (i == 0) { prevNode = nodes[nodes.Count - 1]; nextNode = nodes[i + 1]; } else if (i == nodes.Count - 1) { prevNode = nodes[i - 1]; nextNode = nodes[0]; } else { prevNode = nodes[i - 1]; nextNode = nodes[i + 1]; } Vector3 dirToPrev = (prevNode.Position - nodes[i].Position).normalized; Vector3 dirToNext = (nextNode.Position - nodes[i].Position).normalized; Edge prevEdge = EdgeGraphUtility.FindEdgeByNodes(nodes[i], prevNode, edges); Edge nextEdge = EdgeGraphUtility.FindEdgeByNodes(nodes[i], nextNode, edges); Node prevEdgeOtherNode = prevEdge.Node1 == nodes[i].ID ? Node.GetNode(nodes, prevEdge.Node2) : Node.GetNode(nodes, prevEdge.Node1); Node nextEdgeOtherNode = nextEdge.Node1 == nodes[i].ID ? Node.GetNode(nodes, nextEdge.Node2) : Node.GetNode(nodes, nextEdge.Node1); if (prevEdgeOtherNode == null || nextEdgeOtherNode == null) { continue; } Vector3 prevLeftNormal = Edge.GetLeftPerpendicular(prevEdgeOtherNode.Position, nodes[i].Position); Vector3 nextLeftNormal = Edge.GetLeftPerpendicular(nodes[i].Position, nextEdgeOtherNode.Position); Vector3 newNodePos = nodes[i].Position; // In case the next and edge create a 180 degree angle, just shift this node towards the left normal if (Mathf.Approximately(Vector3.Dot(dirToNext, dirToPrev), -1f)) { float avgEdgeWidth = (prevEdge.Width + nextEdge.Width) / 2f; newNodePos = nodes[i].Position + prevLeftNormal * avgEdgeWidth / 2f; } else { // Shifted edges, streched a bit to ensure the intersection point Vector3 prevEdgeNormalPoint = EdgeGraphUtility.GetEdgePosition(prevEdge.ID, ref nodes, ref edges) + prevLeftNormal * (prevEdge.Width / 2f); float prevEdgeLength = Vector3.Distance(nodes[i].Position, prevEdgeOtherNode.Position); Vector3 prevEdgeFar = prevEdgeNormalPoint + dirToPrev * prevEdgeLength * 2f; Vector3 prevEdgeNear = prevEdgeNormalPoint - dirToPrev * prevEdgeLength * 2f; Vector3 nextEdgeNormalPoint = EdgeGraphUtility.GetEdgePosition(nextEdge.ID, ref nodes, ref edges) + nextLeftNormal * (nextEdge.Width / 2f); float nextEdgeLength = Vector3.Distance(nodes[i].Position, nextEdgeOtherNode.Position); Vector3 nextEdgeFar = nextEdgeNormalPoint + dirToNext * nextEdgeLength * 2f; Vector3 nextEdgeNear = nextEdgeNormalPoint - dirToNext * nextEdgeLength * 2f; // Get intersect point of the shifted edges Vector3 intersectPoint = nodes[i].Position; Vector3 intersectPointXZ; if (UtilityTools.MathHelper.AreIntersecting(out intersectPointXZ, prevEdgeFar, prevEdgeNear, nextEdgeFar, nextEdgeNear) == 1) { intersectPoint = new Vector3(intersectPointXZ.x, nodes[i].Position.y, intersectPointXZ.y); } //Vector3 nodeToNew = (intersectPoint - nodes[i].Position).normalized; //Vector3 prevToNew = (intersectPoint - prevEdgeOtherNode.Position).normalized; //Vector3 nextToNew = (intersectPoint - nextEdgeOtherNode.Position).normalized; newNodePos = intersectPoint; } newNodePositionDict.Add(nodes[i].ID, newNodePos); } position /= nodes.Count; //Set shifted positions for (int i = 0; i < nodes.Count; i++) { if (newNodePositionDict.ContainsKey(nodes[i].ID)) { nodes[i].Position = newNodePositionDict[nodes[i].ID]; } } }
/// <summary> /// Extracts filament consisting of nodes and edges /// </summary> public static void ExtractFilament(Node _n0, Node _n1, ref List <Node> _nodes, ref List <Edge> _edges, ref List <Primitive> _primitives) { Edge e = EdgeGraphUtility.FindEdgeByNodes(_n0, _n1, _edges); if (e != null && e.isPartOfCycle) { if (_n0.adjacents.Count >= 3) { EdgeGraphUtility.RemoveEdgeAndCleanAdjacents(_n0, _n1, ref _nodes, ref _edges); _n0 = _n1; if (_n0.adjacents.Count == 1) { _n1 = EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes); } } while (_n0.adjacents.Count == 1) { _n1 = EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes); Edge ee = EdgeGraphUtility.FindEdgeByNodes(_n0, _n1, _edges); if (ee != null && e.isPartOfCycle) { EdgeGraphUtility.RemoveNodeAndCleanAdjacents(_n0, ref _nodes, ref _edges); EdgeGraphUtility.RemoveEdgeAndCleanAdjacents(_n0, _n1, ref _nodes, ref _edges); } else { break; } } if (_n0.adjacents.Count == 0) { EdgeGraphUtility.RemoveNodeAndCleanAdjacents(_n0, ref _nodes, ref _edges); } } else { Primitive primitive = new Primitive(Primitive.PrimitiveType.Filament); if (_n0.adjacents.Count >= 3) { primitive.nodes.Add(_n0); primitive.edges.Add(e); EdgeGraphUtility.RemoveEdgeAndCleanAdjacents(_n0, _n1, ref _nodes, ref _edges); _n0 = _n1; if (_n0.adjacents.Count == 1) { _n1 = EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes); } } while (_n0.adjacents.Count == 1) { primitive.nodes.Add(_n0); _n1 = EdgeGraphUtility.GetNode(_n0.adjacents[0], ref _nodes); EdgeGraphUtility.RemoveNodeAndCleanAdjacents(_n0, ref _nodes, ref _edges); Edge _e = EdgeGraphUtility.RemoveEdgeAndCleanAdjacents(_n0, _n1, ref _nodes, ref _edges); if (_e != null) { primitive.edges.Add(_e); } _n0 = _n1; } primitive.nodes.Add(_n0); if (_n0.adjacents.Count == 0) { EdgeGraphUtility.RemoveNodeAndCleanAdjacents(_n0, ref _nodes, ref _edges); } _primitives.Add(primitive); } }