void OnDrawGizmosSelected() { //Primitives if (mainPrimitives != null && mainPrimitives.Count > 0) { int primitiveCount = 0; foreach (var p in mainPrimitives) { //Primitive sub edge targets if (p.subEdgeTargets != null && p.subEdgeTargets.Count > 0) { //Gizmos.color = Color.magenta; for (int i = 0; i < p.subEdgeTargets.Count; i++) { Gizmos.color = Color.blue; Vector3 pointPos = transform.TransformPoint(p.subEdgeTargets[i]); Gizmos.DrawSphere(pointPos, .15f); //Gizmos.DrawLine(pointOutside, pointPos); } } //Subedges if (p.subEdges != null && p.subEdges.Count > 0) { Gizmos.color = Color.magenta; // Edges for (int j = 0; j < p.subEdges.Count; j++) { Node n1 = Node.GetNode(p.subNodes, p.subEdges[j].Node1); Node n2 = Node.GetNode(p.subNodes, p.subEdges[j].Node2); if (n1 != null && n2 != null) { Gizmos.DrawLine(transform.TransformPoint(n1.Position), transform.TransformPoint(n2.Position)); } } for (int k = 0; k < p.subNodes.Count; k++) { if (p.subNodes[k].adjacents.Count >= 2) { Gizmos.color = Color.green; } else { Gizmos.color = Color.blue; } Gizmos.DrawSphere(transform.TransformPoint(p.subNodes[k].Position), .05f); } } primitiveCount++; } } }
/// <summary> /// Splits given edge into two edges at given point /// If a node is given, it will be used to split the edges /// Otherwise make a new node /// </summary> public static Node SplitEdge(Edge edge, Vector3 point, List <Node> nodes, List <Edge> edges, Node node = null) { bool isNewNode = false; // Create new node if (node == null) { node = new Node(point); isNewNode = true; } // Get start and end nodes of the edge Node start = Node.GetNode(nodes, edge.Node1); Node end = Node.GetNode(nodes, edge.Node2); // Add new node between start and end start.adjacents.Remove(end.ID); end.adjacents.Remove(start.ID); start.adjacents.Add(node.ID); end.adjacents.Add(node.ID); int startIndex = nodes.IndexOf(start); if (startIndex == -1) { Debug.LogError("Edge::SplitEdge() - StartIndex not found. Are you sure you gave the right node list?"); return(null); } if (isNewNode) { if (startIndex == nodes.Count - 1) { nodes.Add(node); } else { nodes.Insert(startIndex + 1, node); } } // Remove original edge from edges edges.Remove(edge); // Create new edges Edge e1 = new Edge(start.ID, node.ID, edge.width); Edge e2 = new Edge(node.ID, end.ID, edge.width); edges.Add(e1); edges.Add(e2); return(node); }
public static void CleanUpEdges(ref List <Node> nodes, ref List <Edge> edges) { for (int i = 0; i < edges.Count; i++) { // Make sure the edge is still an existing one Node n1 = Node.GetNode(nodes, edges[i].Node1); Node n2 = Node.GetNode(nodes, edges[i].Node2); if (n1 == null || n2 == null) { edges.RemoveAt(i); i--; continue; } // Remove edges that have the same node twice if (edges[i].Node1 == edges[i].Node2) { edges.RemoveAt(i); i--; continue; } // Remove duplicate edges for (int j = 0; j < edges.Count; j++) { if (j != i && j < edges.Count - 1 && i < edges.Count - 1 && i > 0 && j > 0) { if ((edges[i].Node1 == edges[j].Node1 && edges[i].Node2 == edges[j].Node2) || (edges[i].Node1 == edges[j].Node2 && edges[i].Node2 == edges[j].Node1)) { edges.RemoveAt(i); i--; } } } } }
void ConnectEndPoints(List <Node> _nodes, List <Edge> _edges) { // Find endpoints List <Node> endNodes = new List <Node>(); for (int i = 1; i < _nodes.Count; i++) { if (_nodes[i].adjacents.Count == 1) { endNodes.Add(_nodes[i]); } } // Connect endpoints to edges or other nodes for (int i = 0; i < endNodes.Count; i++) { // End node direction Node adjacent = Node.GetNode(_nodes, endNodes[i].adjacents[0]); Vector3 nodeDir = (endNodes[i].Position - adjacent.Position).normalized; //Check if there is any nodes in the rough direction of the end node float roughAngle = 30f; List <Node> nodesInDir = new List <Node>(); for (int j = 0; j < _nodes.Count; j++) { if (_nodes[j] == endNodes[i]) { continue; } Vector3 dirToNode = (_nodes[j].Position - endNodes[i].Position).normalized; if (Vector3.Angle(nodeDir, dirToNode) < roughAngle && Vector3.Angle(nodeDir, dirToNode) > 30f) { nodesInDir.Add(_nodes[j]); } } // The node with which this endpoint is connected to Node connectTo = null; // If there are some nodes in the rough direction, pick the one that is closest if (nodesInDir.Count > 0) { float toClosest = Mathf.Infinity; Node closest = null; for (int j = 0; j < nodesInDir.Count; j++) { float toCurrent = Vector3.Distance(endNodes[i].Position, nodesInDir[j].Position); if (toCurrent < toClosest) { toClosest = toCurrent; closest = nodesInDir[j]; } } if (toClosest < subEdgeEndConnectionRange) { connectTo = closest; } } // Else get intersection with closest edge in the direction of this endpoint if (connectTo == null) { // Ending point for tested segment in the direction of this endpoint Vector3 segmentEnd = endNodes[i].Position + nodeDir * 1000f; // Get the intersection Vector3 intersectPoint = Vector3.zero; // Convert all used points to XZ space Vector3 intersectPointXZ = Vector2.zero; Vector2 endPointXZ = new Vector2(endNodes[i].Position.x, endNodes[i].Position.z); Vector2 segmentEndXZ = new Vector2(segmentEnd.x, segmentEnd.z); List <Edge> intersectedEdges = new List <Edge>(); List <Vector3> intersectPoints = new List <Vector3>(); // Ignore the edge that starts on this endpoint Edge endEdge = _edges.Find(e => (e.Node1 == endNodes[i].ID || e.Node2 == endNodes[i].ID)); for (int j = 0; j < _edges.Count; j++) { if (_edges[j] == endEdge) { continue; } Node n1 = Node.GetNode(_nodes, _edges[j].Node1); Node n2 = Node.GetNode(_nodes, _edges[j].Node2); Vector2 node1XZ = new Vector2(n1.Position.x, n1.Position.z); Vector2 node2XZ = new Vector2(n2.Position.x, n2.Position.z); if (UtilityTools.MathHelper.AreIntersecting(out intersectPointXZ, endPointXZ, segmentEndXZ, node1XZ, node2XZ) == 1) { intersectPoints.Add(new Vector3(intersectPointXZ.x, n1.Position.y, intersectPointXZ.y)); intersectedEdges.Add(_edges[j]); } } // Get closest intersect point float toClosest = Mathf.Infinity; Edge closestIntersectedEdge = null; for (int j = 0; j < intersectPoints.Count; j++) { float toPoint = Vector3.Distance(endNodes[i].Position, intersectPoints[j]); if (toPoint < toClosest) { toClosest = toPoint; intersectPoint = intersectPoints[j]; closestIntersectedEdge = intersectedEdges[j]; } } // Split the intersected edge on the intersection if (closestIntersectedEdge == null || intersectPoint == Vector3.zero) { Debug.Log("Primitive::ConnectEndPoints() - Intersect point not found."); continue; } else { connectTo = Edge.SplitEdge(closestIntersectedEdge, intersectPoint, _nodes, _edges); } } _edges.Add(new Edge(endNodes[i].ID, connectTo.ID, subEdgeWidth)); } // Refresh adjacent nodes after all the endpoint connections EdgeGraphUtility.CheckAdjacentNodes(ref _nodes, ref _edges); }
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> /// Advances forward by segmentLength from current node /// </summary> /// <param name="_node"></param> /// <param name="_dir"></param> /// <returns>True if new node was made</returns> bool Advance(ref Node _node, Vector3 _dir) { bool retval = false; Vector3 newPos = _node.Position + _dir * m_segmentLength; if (newPos == _node.Position) { return(retval); } bool addNew = false; if (_node.adjacents.Count > 0) { Node adj = Node.GetNode(nodes, _node.adjacents[0]); if (adj != null) { Vector3 dirToPrevious = (adj.Position - _node.Position).normalized; // If direction stays the same, just move current node float dot = Vector3.Dot(_dir.normalized, dirToPrevious); if (Mathf.Approximately(dot, -1f)) { _node.Position = newPos; } else { // If resulting angle is too sharp, make a 90 degree turn Vector3 referenceRight = Vector3.Cross(Vector3.up, dirToPrevious); // Pick adjacent with which current direction creates the smallest angle float angle = Mathf.Infinity; for (int i = 0; i < _node.adjacents.Count; i++) { adj = Node.GetNode(nodes, _node.adjacents[0]); Vector3 dirToCur = (adj.Position - _node.Position).normalized; float toCur = Vector3.Angle(dirToCur, _dir); if (toCur < angle) { angle = toCur; dirToPrevious = dirToCur; } } if (angle < m_minAngle) { //Check if current direction points left or right from previous and make a 90 degree turn to that direction if (Vector3.Dot(referenceRight, _dir) > 0) // Right { _dir = Vector3.Cross(Vector3.up, dirToPrevious); } else { _dir = Vector3.Cross(dirToPrevious, Vector3.up); } newPos = _node.Position + _dir * m_segmentLength; } addNew = true; } } else { addNew = true; } } else { addNew = true; } if (addNew) { Node newNode = new Node(newPos); nodes.Add(newNode); newNode.adjacents.Add(_node.ID); _node.adjacents.Add(newNode.ID); edges.Add(new Edge(_node.ID, newNode.ID, m_edgeWidth)); _node = newNode; retval = true; } return(retval); }
/// Goes through all the edges and finds intersections /// If intersections are found, splits both edges in the intersection point /// </summary> /// <returns>True if an edge was fixed</returns> public static bool FixIntersectingEdges(ref List <Node> nodes, ref List <Edge> edges) { bool retval = false; CleanUpEdges(ref nodes, ref edges); int limit = edges.Count; for (int i = 0; i < edges.Count; i++) { if (i > limit) { break; } Vector3 intersectPoint = Vector3.zero; Node n1 = Node.GetNode(nodes, edges[i].Node1); Node n2 = Node.GetNode(nodes, edges[i].Node2); if (n1 == null || n2 == null) { continue; } Vector2 node1XZ = new Vector2(n1.Position.x, n1.Position.z); Vector2 node2XZ = new Vector2(n2.Position.x, n2.Position.z); for (int j = 0; j < edges.Count; j++) { if (i == j) { continue; } Node otherN1 = Node.GetNode(nodes, edges[j].Node1); Node otherN2 = Node.GetNode(nodes, edges[j].Node2); if (otherN1 == null || otherN2 == null || otherN1.adjacents.Contains(n1.ID) || otherN2.adjacents.Contains(n1.ID) || otherN1.adjacents.Contains(n2.ID) || otherN2.adjacents.Contains(n2.ID)) { continue; } Vector2 otherN1XZ = new Vector2(otherN1.Position.x, otherN1.Position.z); Vector2 otherN2XZ = new Vector2(otherN2.Position.x, otherN2.Position.z); Vector3 intersectPointXZ; if (UtilityTools.MathHelper.AreIntersecting(out intersectPointXZ, node1XZ, node2XZ, otherN1XZ, otherN2XZ) == 1) { intersectPoint = new Vector3(intersectPointXZ.x, otherN1.Position.y, intersectPointXZ.y); Node intersectNode = Edge.SplitEdge(edges[i], intersectPoint, nodes, edges); Edge.SplitEdge(edges[j], intersectPoint, nodes, edges, intersectNode); retval = true; break; } } } return(retval); }