/* * Removes all voxels in a voxelgrid which match any of the given templates in the list. */ private static int DeleteByTemplates(List <GridTemplate> gridTemplates, DistanceGrid distanceGrid) { List <Point3> coordsToRemove = new List <Point3>(); for (int x = 1; x < distanceGrid.xBound; x++) { for (int y = 1; y < distanceGrid.yBound; y++) { for (int z = 1; z < distanceGrid.zBound; z++) { if (distanceGrid.grid[x][y][z] != 0) { foreach (GridTemplate gridTemplate in gridTemplates) { if (gridTemplate.Matches(distanceGrid.grid, x, y, z)) { coordsToRemove.Add(new Point3(x, y, z)); break; } } } } } } foreach (var p in coordsToRemove) { distanceGrid.grid[p.x][p.y][p.z] = 0; } return(coordsToRemove.Count); }
/* * Removes voxels from a filled and marked voxelgrid by removing voxels until only "necessary" ones are left. * Necessary voxels are ones which are needed to maintain the geometry and topology of the component. */ public static void ThinToSkeleton(DistanceGrid distanceGrid) { int removedVoxels; do { removedVoxels = 0; removedVoxels += DeleteByTemplates(GridTemplate.USWGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.DNEGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.USEGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.DNWGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.UNEGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.DSWGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.UNWGridTemplates, distanceGrid); removedVoxels += DeleteByTemplates(GridTemplate.DSEGridTemplates, distanceGrid); } while (removedVoxels > 0); for (int x = 1; x < distanceGrid.xBound; x++) { for (int y = 1; y < distanceGrid.yBound; y++) { for (int z = 1; z < distanceGrid.zBound; z++) { if (distanceGrid.grid[x][y][z] != 0 && GridTemplate.NeighborLessTemplate.Matches(distanceGrid.grid, x, y, z)) { distanceGrid.grid[x][y][z] = 0; } } } } }
/* * Return all possible start points for nodgraph. */ private static Queue <(Point3, Point3)> GetStartPoints(DistanceGrid distanceGrid) { // First is start point, second is the immediate neighbor to go to Queue <(Point3, Point3)> startPoints = new Queue <(Point3, Point3)>(); bool done = false; // Find a "starting point" to traverse from. // A voxel is a "starting point" if it only has one "26-neighbor" for (int x = 1; x < distanceGrid.xBound; x++) { for (int y = 1; y < distanceGrid.yBound; y++) { for (int z = 1; z < distanceGrid.zBound; z++) { if (distanceGrid.grid[x][y][z] == 0) { continue; } // If coordinate is a valid startpoint if (distanceGrid.Get26AdjacentNeighbors(x, y, z).Count == 1) { Point3 nextPoint = new Point3(); for (int xi = -1; xi <= 1; xi++) { for (int yi = -1; yi <= 1; yi++) { for (int zi = -1; zi <= 1; zi++) { if (xi == 0 && yi == 0 && zi == 0) { continue; } if (distanceGrid.grid[x + xi][y + yi][z + zi] > 0) { nextPoint = new Point3(x + xi, y + yi, z + zi); } } } } startPoints.Enqueue((new Point3(x, y, z), nextPoint)); done = true; break; } } if (done) { break; } } if (done) { break; } } return(startPoints); }
/** * Can be used when visualising in Unity. */ public static DistanceGrid GeneratePeeledGrid(VoxelGrid voxelGrid) { DistanceGrid distanceGrid = new DistanceGrid(voxelGrid); ThinByDistanceMapping(distanceGrid, voxelGrid); return(distanceGrid); }
/** * Can be used when visualising in Unity. */ public static DistanceGrid GenerateSkeletalGrid(VoxelGrid voxelGrid) { DistanceGrid distanceGrid = new DistanceGrid(voxelGrid); Skeletonize(distanceGrid, voxelGrid); return(distanceGrid); }
private static NodeGraph GenerateNodeGraph(VoxelGrid voxelGrid) { DistanceGrid distanceGrid = new DistanceGrid(voxelGrid); Skeletonize(distanceGrid, voxelGrid); NodeGraph nodeGraph = CreateNodeGraph(distanceGrid, voxelGrid); nodeGraph = MergeNodeGraph(nodeGraph, voxelGrid); return(nodeGraph); }
/** * Pre processing for the skeletonizing. Removes the outer most layers. */ private static void ThinByDistanceMapping(DistanceGrid distanceGrid, VoxelGrid voxelGrid) { int outerLayersToRemove = Convert.ToInt32(voxelGrid.ComponentWiseResolutionDivider * outerLayerLimit); for (int x = 1; x < distanceGrid.xBound; x++) { for (int y = 1; y < distanceGrid.yBound; y++) { for (int z = 1; z < distanceGrid.zBound; z++) { if (distanceGrid.grid[x][y][z] <= outerLayersToRemove) { distanceGrid.grid[x][y][z] = 0; } } } } }
/** * Generates a NodeGraph from a skeletonized voxelgrid, by traversing neighboring * voxels and creating nodes connected by edges to maintain the topology of the voxelgrid. */ private static NodeGraph CreateNodeGraph(DistanceGrid distanceGrid, VoxelGrid voxelGrid) { bool[][][] visitedGrid = new bool[distanceGrid.xBound + 1][][]; for (int i = 0; i < visitedGrid.Length; i++) { visitedGrid[i] = new bool[distanceGrid.yBound + 1][]; for (int j = 0; j < visitedGrid[i].Length; j++) { visitedGrid[i][j] = new bool[distanceGrid.zBound + 1]; } } NodeGraph nodeGraph = new NodeGraph(); Queue <(Point3, Point3)> startPoints = GetStartPoints(distanceGrid); Point3 initialPoint = startPoints.Peek().Item1; nodeGraph.AddNode(voxelGrid.LowestPositionAtCoordinate(initialPoint)); visitedGrid[initialPoint.x][initialPoint.y][initialPoint.z] = true; //Breadth-first traversing of the Voxels while (startPoints.Count > 0) { (Point3 startPoint, Point3 currentPoint) = startPoints.Dequeue(); //Special case for when connecting to an allready visited node if (visitedGrid[currentPoint.x][currentPoint.y][currentPoint.z]) { Vect3 pos1 = voxelGrid.LowestPositionAtCoordinate(startPoint); Vect3 pos2 = voxelGrid.LowestPositionAtCoordinate(currentPoint); Node startNode = nodeGraph.GetNode(pos1); Node currentNode = nodeGraph.GetNode(pos2); if (nodeGraph.GetNode(pos1) == null || nodeGraph.GetNode(pos2) == null) { continue; } bool shouldLink = true; foreach (var neighbor in currentNode.neighbors) { if (neighbor.nodeIndex == startNode.index) { shouldLink = false; } else { Node neighborNode = nodeGraph.GetNode(neighbor.nodeIndex); foreach (var nextNeighbor in neighborNode.neighbors) { if (nextNeighbor.nodeIndex == startNode.index) { shouldLink = false; } } } if (!shouldLink) { break; } } if (shouldLink) { nodeGraph.LinkNodes(pos1, pos2); } else { continue; } } Point3 cameFrom = startPoint; List <Point3> pathPoints = new List <Point3>() { startPoint }; Vect3 startCoordinate = voxelGrid.LowestPositionAtCoordinate(startPoint); Vect3 currentCoordinate = voxelGrid.LowestPositionAtCoordinate(currentPoint); List <Vect3> intermediateCoords = new List <Vect3>(); List <Point3> neighbors = new List <Point3>(); bool distanceWasValid = false; //Traversing untill the Nodeplacement would make the Edge to far away from the voxels it represents, //an already visited node is reached, an intersection is found or it's a dead end. while (DistancesAreValid(startCoordinate, currentCoordinate, intermediateCoords, voxelGrid.resolution)) { // Node we found was visited if (visitedGrid[currentPoint.x][currentPoint.y][currentPoint.z]) { // Ensures nothing happens after we break distanceWasValid = true; break; } neighbors = distanceGrid.Get26AdjacentNeighbors(currentPoint); pathPoints.Add(currentPoint); if (neighbors.Count != 2) { visitedGrid[currentPoint.x][currentPoint.y][currentPoint.z] = true; distanceWasValid = true; break; } // Only one new neighbor intermediateCoords.Add(voxelGrid.LowestPositionAtCoordinate(currentPoint)); Point3 temp = currentPoint; currentPoint = neighbors[0] == cameFrom ? neighbors[1] : neighbors[0]; currentCoordinate = voxelGrid.LowestPositionAtCoordinate(currentPoint); cameFrom = temp; visitedGrid[cameFrom.x][cameFrom.y][cameFrom.z] = true; } double maxDistanceValue = 0; foreach (var point in pathPoints) { maxDistanceValue += distanceGrid.grid[point.x][point.y][point.z] * voxelGrid.resolution * SquareRootOfThree; // Distance is 26-distance from edge // double distanceValue = distanceGrid.grid[point.x][point.y][point.z]*voxelGrid.resolution*2; // maxDistanceValue = distanceValue > maxDistanceValue ? distanceValue : maxDistanceValue; } maxDistanceValue /= (double)pathPoints.Count; double minWidth = maxDistanceValue; double minHeight = maxDistanceValue; if (!distanceWasValid) { nodeGraph.AddNode(intermediateCoords[intermediateCoords.Count - 1], minWidth, minHeight, nodeGraph.GetNode(startCoordinate).index); visitedGrid[cameFrom.x][cameFrom.y][cameFrom.z] = true; if (!visitedGrid[currentPoint.x][currentPoint.y][currentPoint.z]) { startPoints.Enqueue((cameFrom, currentPoint)); } } else { nodeGraph.AddNode(currentCoordinate, minWidth, minHeight, nodeGraph.GetNode(startCoordinate).index); foreach (var neighbor in neighbors) { if (neighbor == cameFrom || visitedGrid[neighbor.x][neighbor.y][neighbor.z]) { continue; } startPoints.Enqueue((currentPoint, neighbor)); } } } return(nodeGraph); }
public static void Skeletonize(DistanceGrid distanceGrid, VoxelGrid voxelGrid) { ThinByDistanceMapping(distanceGrid, voxelGrid); ThinToSkeleton(distanceGrid); }