public static CheckAdjacentNodes ( List |
||
_nodes | List |
|
_edges | List |
|
return | void |
public void ProcessMinimalCycles() { if (GraphID == -1) { GraphID = Guid.NewGuid().GetHashCode(); } // Use different lists of nodes and edges for processing time, so the serialized lists won't change m_edges = new List <Edge>(); m_nodes = new List <Node>(); m_primitives = new List <Primitive>(); UtilityTools.Helper.DestroyChildren(transform); EdgeGraphUtility.CopyNodesAndEdges(nodes, edges, out m_nodes, out m_edges); EdgeGraphUtility.CheckAdjacentNodes(ref m_nodes, ref m_edges); MinimalCycle.Extract(ref m_nodes, ref m_edges, ref m_primitives); // Serialize and process primitives mainPrimitives = m_primitives; try { ProcessPrimitives(); } catch (Exception e) { Debug.LogWarning("Graph::ProcessMinimalCycles() - Error while processing primitives: " + e.Message); } }
/// <summary> /// Combines two nodes into one /// </summary> /// <param name="node1">First node, this node is transformed into the combined node</param> /// <param name="node2">Second node</param> /// <param name="edges">List of edges in which the IDs are fixed</param> /// <returns>Combined node</returns> public static Node CombineNodes(Node node1, Node node2, List <Edge> edges, List <Node> nodes) { //Combine node positions node1.position = (node1.position + node2.position) / 2f; //Fix edges for (int i = 0; i < edges.Count; i++) { //Remove edges that end in the node2, which will be removed if (node2.adjacents.Count == 1 && (edges[i].Node1 == node2.ID || edges[i].Node2 == node2.ID)) { edges.RemoveAt(i); i--; continue; } if (edges[i].Node1 == node2.ID) { edges[i].Node1 = node1.ID; } if (edges[i].Node2 == node2.ID) { edges[i].Node2 = node1.ID; } } EdgeGraphUtility.CleanUpEdges(ref nodes, ref edges); //Fix node adjacents EdgeGraphUtility.CheckAdjacentNodes(ref nodes, ref edges); return(node1); }
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 GenerateSubEdges() { // Save hard copies of this primitive's original nodes and edges List <Node> nodeCopies = new List <Node>(); List <Edge> edgeCopies = new List <Edge>(); EdgeGraphUtility.CopyNodesAndEdges(nodes, edges, out nodeCopies, out edgeCopies, false, false); if (subEdgeSegmentLength <= 0f || subEdgeTargets == null || subEdgeTargets.Count <= 0) { return; } if (subEdgeRootIndex >= nodeCopies.Count) { subEdgeRootIndex = nodeCopies.Count - 1; } Node rootNode = nodeCopies[subEdgeRootIndex]; //EdgeBuilder builder = null; if (subEdgeSegmentLength == 0f) { subEdgeSegmentLength = .5f; } List <Node> _builtSubNodes = new List <Node>(); List <Edge> _builtSubEdges = new List <Edge>(); new EdgeBuilder(rootNode, subEdgeTargets, subEdgeWidth, subEdgeSegmentLength, subEdgeMinAngle, subEdgeMinDistance, subEdgeMaxDistance, (_nodes, _edges) => { if (_nodes == null || _nodes.Count <= 0) { Debug.Log("Primitive::GenerateSubEdges() - Builder nodes null / empty."); return; } _builtSubNodes = _nodes; if (_edges == null || _edges.Count <= 0) { Debug.Log("Primitive::GenerateSubEdges() - Builder edges null / empty."); return; } _builtSubEdges = _edges; CombineSubNodes(rootNode, _builtSubNodes, _builtSubEdges, subEdgeNodeCombineRange); }); nodeCopies.AddRange(_builtSubNodes); edgeCopies.AddRange(_builtSubEdges); ConnectEndPoints(nodeCopies, edgeCopies); CombineSubNodes(rootNode, nodeCopies, edgeCopies, subEdgeNodeCombineRange, false); List <Node> _copiedSubNodes = new List <Node>(); List <Edge> _copiedSubEdges = new List <Edge>(); EdgeGraphUtility.CopyNodesAndEdges(nodeCopies, edgeCopies, out _copiedSubNodes, out _copiedSubEdges); EdgeGraphUtility.CheckAdjacentNodes(ref nodeCopies, ref edgeCopies); EdgeGraphUtility.CleanUpEdges(ref nodeCopies, ref edgeCopies); subNodes = nodeCopies; subEdges = edgeCopies; }
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); }
void ProcessSubPrimitives(Primitive p) { if (p.subEdges == null || p.subEdges.Count <= 0) { return; } // Copy local lists from primitive's sub nodes and edges List <Node> _nodes = new List <Node>(); List <Edge> _edges = new List <Edge>(); EdgeGraphUtility.CopyNodesAndEdges(p.subNodes, p.subEdges, out _nodes, out _edges); EdgeGraphUtility.CheckAdjacentNodes(ref _nodes, ref _edges); //subPrimitives = new List<Primitive>(); List <Primitive> _subPrimitives = new List <Primitive>(); // Extract primitives inside main primitives try { MinimalCycle.Extract(ref _nodes, ref _edges, ref _subPrimitives); } catch (Exception e) { Debug.LogWarning("Graph::GeneratePrimitiveSubPrimitives() - Error while extracting primitives: " + e.Message); return; } _subPrimitives.ForEach((sp) => { sp.Process(); }); for (int i = _subPrimitives.Count - 1; i >= 0; i--) { if (!_subPrimitives[i].EvaluationResult) { _subPrimitives.RemoveAt(i); } } _subPrimitives.ForEach((sp) => { sp.parent = p.ID; GameObject subGraphObj = new GameObject("SubGraph"); subGraphObj.transform.SetParent(transform); subGraphObj.transform.localPosition = Vector3.zero; subGraphObj.transform.localScale = Vector3.one; Graph subGraph = subGraphObj.AddComponent <Graph>(); subGraph.GraphID = Guid.NewGuid().GetHashCode(); subGraphObj.name += subGraph.GraphID; subGraph.nodes = new List <Node>(); foreach (var node in sp.nodes) { subGraph.nodes.Add(node); } subGraph.edges = new List <Edge>(); foreach (var edge in sp.edges) { edge.Width = 0f; subGraph.edges.Add(edge); } subGraph.ProcessMinimalCycles(); subGraph.mainPrimitives[0].parent = p.ID; subGraphs.Add(subGraph); //subPrimitives.Add(subGraph.mainPrimitives[0]); FacadeBuilder builder = GetComponent <FacadeBuilder>(); if (builder != null) { FacadeBuilder subBuilder = subGraph.gameObject.AddComponent <FacadeBuilder>(); subBuilder.inSet = builder.inSet; subBuilder.facadeStretchPrefab = builder.facadeStretchPrefab; subBuilder.facadePrefabs = builder.facadePrefabs; subBuilder.roofMiddleMaterial = builder.roofMiddleMaterial; subBuilder.roofSideMaterial = builder.roofSideMaterial; subBuilder.roofHeight = builder.roofHeight; subBuilder.roofMiddleAddHeight = builder.roofMiddleAddHeight; subBuilder.roofAccentWidth = builder.roofAccentWidth; } FootprintPlacer placer = GetComponent <FootprintPlacer>(); if (placer != null) { FootprintPlacer _placer = subGraph.gameObject.AddComponent <FootprintPlacer>(); _placer.footprintPrefabsOnEdge = placer.footprintPrefabsOnEdge; _placer.footprintPrefabsInside = placer.footprintPrefabsInside; _placer.UpdateData(); } }); }
// 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); } }