public GraphLinked AsGraph() { GraphLinked graph = new GraphLinked(); graph.Contour = this; graph.links = new List <GraphLinked.Link>(); graph.segments = new List <List <GraphLinked.Link> >(); /*for (int i = 0; i < contour.Count; ++i) * { * graph.AddNode(contour[i], true); * }*/ for (int i = 0; i < contour.Count; ++i) { GraphLinked.Link link = new GraphLinked.Link(contour[i], contour[(i + 1) % contour.Count], true); graph.links.Add(link); graph.segments.Add(new List <GraphLinked.Link>()); graph.segments[i].Add(link); } return(graph); }
void Subdivide(int subdivision = 5) { blockContours = new List <GraphLinked.Cell>(); if (subdivision == 1) { foreach (GraphLinked.Cell cell in regions) { blockContours.Add(cell); } } else { blocks = new List <GraphLinked>(); foreach (GraphLinked.Cell region in regions) { GraphLinked regionGraph = region.AsGraph(); blocks.Add(regionGraph); Bounds bounds = region.Bounds; Vector3 delta = bounds.size / subdivision; bool[,] valid = new bool[subdivision + 1, subdivision + 1]; GraphLinked.Node[,] nodes = new GraphLinked.Node[subdivision + 1, subdivision + 1]; for (int x = 0; x <= subdivision; ++x) { float u = x / (float)subdivision * bounds.size.x + bounds.min.x; for (int z = 0; z <= subdivision; ++z) { float v = z / (float)subdivision * bounds.size.z + bounds.min.z; Vector3 point = new Vector3(u, 0, v); if (region.ContainsPoint(point)) { valid[x, z] = true; nodes[x, z] = regionGraph.AddNode(point); } } } // create internal nodes and links, divide shell edges when intersecting them for (int x = 0; x <= subdivision; ++x) { float u = x / (float)subdivision * bounds.size.x + bounds.min.x; for (int z = 0; z <= subdivision; ++z) { float v = z / (float)subdivision * bounds.size.z + bounds.min.z; if (valid[x, z]) { Vector3 fromPosition = new Vector3(u, 0, v); GraphLinked.Node from = nodes[x, z]; // RIGHT int x2 = x + 1; if (x2 > subdivision || !valid[x2, z]) { float u2 = x2 / (float)subdivision * bounds.size.x + bounds.min.x; Vector3 targetPosition = new Vector3(u2, 0, v); regionGraph.SegmentShell(from, targetPosition); } else { regionGraph.AddLink(nodes[x, z], nodes[x2, z]); city.AddLink(city.AddNode(nodes[x, z].position), city.AddNode(nodes[x2, z].position)); } // LEFT x2 = x - 1; if (x2 < 0 || !valid[x2, z]) { float u2 = x2 / (float)subdivision * bounds.size.x + bounds.min.x; Vector3 targetPosition = new Vector3(u2, 0, v); regionGraph.SegmentShell(from, targetPosition); } // no else: intersections with the edge are done only done right-wards // UP int z2 = z + 1; if (z2 > subdivision || !valid[x, z2]) { float v2 = z2 / (float)subdivision * bounds.size.z + bounds.min.z; Vector3 targetPosition = new Vector3(u, 0, v2); regionGraph.SegmentShell(from, targetPosition); } else { regionGraph.AddLink(nodes[x, z], nodes[x, z2]); city.AddLink(city.AddNode(nodes[x, z].position), city.AddNode(nodes[x, z2].position)); } //DOWN z2 = z - 1; if (z2 < 0 || !valid[x, z2]) { float v2 = z2 / (float)subdivision * bounds.size.z + bounds.min.z; Vector3 targetPosition = new Vector3(u, 0, v2); regionGraph.SegmentShell(from, targetPosition); } // no else: intersections with the edge are done only done up-wards } } } for (int x = 0; x <= subdivision - 1; ++x) { for (int z = 0; z <= subdivision - 1; ++z) { if (valid[x, z] && valid[x + 1, z] && valid[x, z + 1] && valid[x + 1, z + 1]) { List <GraphLinked.Node> contour = new List <GraphLinked.Node>(); contour.Add(nodes[x, z]); contour.Add(nodes[x + 1, z]); contour.Add(nodes[x + 1, z + 1]); contour.Add(nodes[x, z + 1]); GraphLinked.Cell subcell = new GraphLinked.Cell(contour); subcell.Parent = region; blockContours.Add(subcell); } } } List <GraphLinked.Link> unused = new List <GraphLinked.Link>(); foreach (var link in regionGraph.segments) { unused.AddRange(link); } // get the cells at the edges regionGraph.ComputeNeighbours(); while (unused.Count > 0) { List <GraphLinked.Link> contour = new List <GraphLinked.Link>(); GraphLinked.Link link = unused[0]; unused.RemoveAt(0); if (link == null) { continue; } contour.Add(link); Queue <List <GraphLinked.Node> > queueNodes = new Queue <List <GraphLinked.Node> >(); Queue <List <GraphLinked.Link> > queueLinks = new Queue <List <GraphLinked.Link> >(); HashSet <GraphLinked.Node> used = new HashSet <GraphLinked.Node>(); var startNodes = new List <GraphLinked.Node>(); used.Add(link.from); startNodes.Add(link.from); startNodes.Add(link.to); queueNodes.Enqueue(startNodes); List <GraphLinked.Link> startLink = new List <GraphLinked.Link>(); startLink.Add(link); queueLinks.Enqueue(startLink); int count = 0; List <GraphLinked.Node> solutionPath = null; List <GraphLinked.Link> solutionLinks = null; var neighbours = regionGraph.ComputeNeighbours(); while (queueNodes.Count > 0 && count < 100 && solutionPath == null) { List <GraphLinked.Node> currentSolution = queueNodes.Dequeue(); List <GraphLinked.Link> currentSolutionLinks = queueLinks.Dequeue(); GraphLinked.Node current = currentSolution[currentSolution.Count - 1]; used.Add(current); foreach (var nextLink in neighbours[current]) { //flips the edge if needed GraphLinked.Node next = current == nextLink.from ? nextLink.to : nextLink.from; // is it a solution? if (currentSolution.Count > 2 && next == link.from) { solutionPath = currentSolution; solutionLinks = currentSolutionLinks; } //no solution, but it can be navigated else if (!used.Contains(next)) { // single option, no need to create new solution if (neighbours[current].Count == 1) { currentSolution.Add(next); queueNodes.Enqueue(currentSolution); currentSolutionLinks.Add(nextLink); queueLinks.Enqueue(currentSolutionLinks); } // more than one edge, create a new solution for each else if (neighbours[current].Count > 1) { var newSolution = new List <GraphLinked.Node>(currentSolution); newSolution.Add(next); queueNodes.Enqueue(newSolution); var newSolutionLinks = new List <GraphLinked.Link>(currentSolutionLinks); newSolutionLinks.Add(nextLink); queueLinks.Enqueue(newSolutionLinks); } } ++count; } } { if (solutionPath != null) { List <GraphLinked.Node> contourCell = new List <GraphLinked.Node>(); foreach (var entry in solutionPath) { if (contourCell.Count == 0 || Vector3.SqrMagnitude(entry.position - contourCell[contourCell.Count - 1].position) > float.Epsilon) { contourCell.Add(entry); } } if (contourCell.Count > 2) { GraphLinked.Cell building = new GraphLinked.Cell(contourCell); if (building.Area > 0) { foreach (var usedLink in solutionLinks) { if (unused.Contains(usedLink)) { unused.Remove(usedLink); } } building.Parent = region; blockContours.Add(building); } } else { string p = ""; foreach (var element in contourCell) { p += element.ToString() + " "; } Debug.LogWarning("path:" + p); } } else { Debug.Log("STATUS:" + (solutionPath != null ? solutionPath.Count.ToString() : "null") + ", in " + count + " steps"); } } } } } /*foreach (Cell building in buildingContours) * { * building.Deflate(0.1f); * }*/ }