Java 在基于平铺的地图中查找多边形的内部坐标

Java 在基于平铺的地图中查找多边形的内部坐标,java,awt,coordinates,polygon,java-2d,Java,Awt,Coordinates,Polygon,Java 2d,如果我在一个列表中有多边形的所有外边缘,我将如何找到内部坐标??为了简单起见,我绘制了以下图像来表示问题: 我需要在一个基于瓷砖的游戏中找到“建筑物”的内部 外墙-灰色阴影单元格 建筑内部-浅蓝色单元格 如果建筑未完全显示在视图中(右建筑),我已通过将整个绿色部分(-1、-1、0、-1等)添加到列表中解决了问题 没有遵循一些疯狂的如果搜索树,我不知道如何解决这个问题。我在这里发布一些提示、代码或psuedo代码。非常感谢您的帮助。非常感谢!:) 编辑 @安德鲁·汤普森:我想我写错了我的处

如果我在一个列表中有多边形的所有外边缘,我将如何找到内部坐标??为了简单起见,我绘制了以下图像来表示问题:

我需要在一个基于瓷砖的游戏中找到“建筑物”的内部

  • 外墙-灰色阴影单元格
  • 建筑内部-浅蓝色单元格
如果建筑未完全显示在视图中(右建筑),我已通过将整个绿色部分(-1、-1、0、-1等)添加到列表中解决了问题

没有遵循一些疯狂的如果搜索树,我不知道如何解决这个问题。我在这里发布一些提示、代码或psuedo代码。非常感谢您的帮助。非常感谢!:)


编辑

@安德鲁·汤普森:我想我写错了我的处境。这与链接到的副本不同。我没有这个形象。我上面做的excel绘图只是一个例子。例如:

我有一个包含棕色值的列表: 即{“1,1”、“2,1”、“3,1”、“1,2”等)


我需要一个蓝色值的对应列表:即{“2,2”,“2,6”,“3,6”,“4,6”,等等}

我发现这是一个非常有趣的帖子:)我发现刷新尝试解决这些看起来很简单的问题,真的很刷新

我想出了一个简单的算法,可以处理矩形区域。它只是在纸上,未经测试,但我将尽可能更好地解释它,以看到您可以将其转换为实际代码

我的想法是对该区域进行水平扫描,并使用某种状态机来识别可能的蓝色瓷砖。该过程大致如下所示:

  • 我们读了一行。在那里,我们寻找三个或更多连续的棕色瓷砖。如果我们碰巧发现了这一点,我们会在一个数据变量中存储两个值:第一个值的列(使用图像的第一个正方形,列1)和最后一个值的列(同一个示例,3)
  • 我们读了另一行。在查找连续行之前,我们先查看1列和3列。如果它们是棕色的,我们会查看它们之间瓷砖的颜色(在本例中,瓷砖[2,2])。如果这些图块不是棕色的,那么我们将其存储在数据结构中,并在其中跟踪可能的蓝色图块
  • 我们读了另一行。我们再次查看第1列和第3列。再说一次,它们是棕色的。再次,我们看看它们之间瓷砖的颜色,这次它们是棕色的。很好,这是一个封闭的广场!我们将所有存储为潜在蓝色的瓷砖([2,2])放入实际蓝色
  • 我们读了另一行。我们再次查看第1列和第3列。哦,不走运。它们不再是棕色的了。我们弹出1和3的值,只要不再找到连续的行,我们就不再在那里查找
我现在有一个会议,若我有时间的话,我将编写一个简单的伪代码(只是为了好玩),在循环的每个点上进行检查。如果我有更多的时间,我会考虑一些改进来检测非正方形区域。我认为这可能非常简单,比如寻找与标记列相交的连续棕色瓷砖(通过标记,我指的是上面示例中的1和3),并相应地调整该区域的新标记限制


我也试着去考虑类似山区的区域,你在地图的上部发现了两个不同的区域,但是它们最终连接到了下部。毕竟,我从来没有想过这是一个简单的问题:)同时,我希望我能为您提供一些见解。

本周我玩弄了a*算法。对于您的请求,可能还有其他解决方案,但由于我已经有了代码,所以只需根据您的需要对其进行调整即可。但是,对于您的特定需求,您也可以简单地使用基本洪水填充算法并以这种方式对单元格进行分类,请参见算法代码中的方法

查找从给定起点到给定目标的路径。在您的案例中,起点是目标,这意味着它是一个参考点,用于对“外部”单元进行分类。从那里开始,我们寻找我们能穿越的一切

我在示例中为您留下了路径查找代码,也许它对您的进一步需求有用

代码如下:

Demo.java

import java.util.List;
import java.util.Set;


public class Demo {

    public static void main(String[] args) {

        // create grid like in the example
        int cols = 9;
        int rows = 9;

        Grid grid = new Grid( cols, rows);

        // create walls like in the example
        grid.getCell( 1, 1).setTraversable( false);
        grid.getCell( 2, 1).setTraversable( false);
        grid.getCell( 3, 1).setTraversable( false);
        grid.getCell( 1, 2).setTraversable( false);
        grid.getCell( 3, 2).setTraversable( false);
        grid.getCell( 6, 2).setTraversable( false);
        grid.getCell( 7, 2).setTraversable( false);
        grid.getCell( 8, 2).setTraversable( false);
        grid.getCell( 1, 3).setTraversable( false);
        grid.getCell( 2, 3).setTraversable( false);
        grid.getCell( 3, 3).setTraversable( false);
        grid.getCell( 6, 3).setTraversable( false);
        grid.getCell( 6, 4).setTraversable( false);
        grid.getCell( 7, 4).setTraversable( false);
        grid.getCell( 1, 5).setTraversable( false);
        grid.getCell( 2, 5).setTraversable( false);
        grid.getCell( 3, 5).setTraversable( false);
        grid.getCell( 4, 5).setTraversable( false);
        grid.getCell( 5, 5).setTraversable( false);
        grid.getCell( 7, 5).setTraversable( false);
        grid.getCell( 8, 5).setTraversable( false);
        grid.getCell( 1, 6).setTraversable( false);
        grid.getCell( 5, 6).setTraversable( false);
        grid.getCell( 1, 7).setTraversable( false);
        grid.getCell( 2, 7).setTraversable( false);
        grid.getCell( 3, 7).setTraversable( false);
        grid.getCell( 4, 7).setTraversable( false);
        grid.getCell( 5, 7).setTraversable( false);

        // find traversables
        // -------------------------

        AStarAlgorithm alg = new AStarAlgorithm();

        Cell start;
        Cell goal;

        // reference point = 0/0
        start = grid.getCell(0, 0);
        Set<Cell> visited = alg.getFloodFillCells(grid, start, true);

        // find inside cells
        for( int row=0; row < rows; row++) {
            for( int col=0; col < cols; col++) {

                Cell cell = grid.getCell(col, row);

                if( !cell.traversable) {
                    cell.setType(Type.WALL);
                }
                else if( visited.contains( cell)) {
                    cell.setType(Type.OUTSIDE);
                } 
                else {
                    cell.setType(Type.INSIDE);
                }

            }
        }

        // log inside cells
        for( int row=0; row < rows; row++) {
            for( int col=0; col < cols; col++) {
                Cell cell = grid.getCell(col, row);
                if( cell.getType() == Type.INSIDE) {
                    System.out.println("Inside: " + cell);
                }
            }
        }

        // path finding
        // -------------------------

        // start = top/left, goal = bottom/right
        start = grid.getCell(0, 0);
        goal = grid.getCell(8, 8);

        // find a* path
        List<Cell> path = alg.findPath(grid, start, goal, true);

        // log path
        System.out.println(path);

        System.exit(0);

    }

}
Cell.java

public class Cell implements Cloneable {

    int col;
    int row;
    boolean traversable;
    Type type;

    double g;
    double f;
    double h;

    Cell cameFrom;

    public Cell( int col, int row, boolean traversable) {
        this.col=col;
        this.row=row;
        this.traversable = traversable;
    }

    public double getF() {
        return f;
    }

    public double getG() {
        return g;
    }

    public double getH() {
        return h;
    }

    public void setTraversable( boolean traversable) {
        this.traversable = traversable;
    }

    public void setType( Type type) {
        this.type = type;
    }

    public Type getType() {
        return this.type;
    }

    public String toString() {
        return col + "/" + row;
    }
}
Grid.java

public class Grid {

    Cell[][] cells;

    int cols;
    int rows;

    public Grid( int cols, int rows) {
        this.cols = cols;
        this.rows = rows;
        cells = new Cell[rows][cols];

        for( int row=0; row < rows; row++) {
            for( int col=0; col < cols; col++) {
                cells[row][col] = new Cell( col, row, true); 
            }
        }
    }

    public Cell getCell( int col, int row) {
        return cells[row][col];
    }

    /**
     * Get neighboring cells relative to the given cell. By default they are top/right/bottom/left. 
     * If allowDiagonals is enabled, then also top-left, top-right, bottom-left, bottom-right cells are in the results.
     * @param cell
     * @param allowDiagonals
     * @return
     */
    public Cell[] getNeighbors(Cell cell, boolean allowDiagonals) {

        Cell[] neighbors = new Cell[ allowDiagonals ? 8 : 4];

        int currentColumn = cell.col;
        int currentRow = cell.row;

        int neighborColumn;
        int neighborRow;

        // top
        neighborColumn = currentColumn;
        neighborRow = currentRow - 1;

        if (neighborRow >= 0) {
            if( cells[neighborRow][neighborColumn].traversable) {
                neighbors[0] = cells[neighborRow][neighborColumn];
            }
        }

        // bottom
        neighborColumn = currentColumn;
        neighborRow = currentRow + 1;

        if (neighborRow < rows) {
            if( cells[neighborRow][neighborColumn].traversable) {
                neighbors[1] = cells[neighborRow][neighborColumn];
            }
        }

        // left
        neighborColumn = currentColumn - 1;
        neighborRow = currentRow;

        if ( neighborColumn >= 0) {
            if( cells[neighborRow][neighborColumn].traversable) {
                neighbors[2] = cells[neighborRow][neighborColumn];
            }
        }

        // right
        neighborColumn = currentColumn + 1;
        neighborRow = currentRow;

        if ( neighborColumn < cols) {
            if( cells[neighborRow][neighborColumn].traversable) {
                neighbors[3] = cells[neighborRow][neighborColumn];
            }
        }

        if (allowDiagonals) {

            // top/left
            neighborColumn = currentColumn - 1;
            neighborRow = currentRow - 1;

            if (neighborRow >= 0 && neighborColumn >= 0) {
                if( cells[neighborRow][neighborColumn].traversable) {
                    neighbors[4] = cells[neighborRow][neighborColumn];
                }
            }

            // bottom/right
            neighborColumn = currentColumn + 1;
            neighborRow = currentRow + 1;

            if (neighborRow < rows && neighborColumn < cols) {
                if( cells[neighborRow][neighborColumn].traversable) {
                    neighbors[5] = cells[neighborRow][neighborColumn];
                }
            }

            // top/right
            neighborColumn = currentColumn + 1;
            neighborRow = currentRow - 1;

            if (neighborRow >= 0 && neighborColumn < cols) {
                if( cells[neighborRow][neighborColumn].traversable) {
                    neighbors[6] = cells[neighborRow][neighborColumn];
                }
            }

            // bottom/left
            neighborColumn = currentColumn - 1;
            neighborRow = currentRow + 1;

            if (neighborRow < rows && neighborColumn >= 0) {
                if( cells[neighborRow][neighborColumn].traversable) {
                    neighbors[7] = cells[neighborRow][neighborColumn];
                }
            }

        }


        return neighbors;
    }

}
从0/0到8/8的路径的结果为

[8/8, 7/7, 7/6, 6/5, 5/4, 5/3, 5/2, 4/1, 3/0, 2/0, 1/0, 0/0]
我在JavaFX中为此编写了一个编辑器,如果您感兴趣的话,很快将作为博客文章发布。基本上,您的网格将如下所示:

在哪里

  • 黑细胞=墙
  • 绿色单元格=可遍历单元格
  • 蓝色单元格=从开始到结束的路径
  • 白细胞=细胞内壁
这些数字是来自A*算法的数字:

  • 上/左=g(从开始到当前单元格)
  • 顶部/右侧=h(从当前单元格到目标)
  • 中心=f=g+h
如果你不允许对角线运动:

但这只是离题:-)

这真是个问题

你知道多边形顶点的坐标吗?还有一些算法可以更快地计算洪水。你不必决定哪一部分是建筑,哪一部分不是

如果不知道顶点,则必须进行“真实”泛光填充:

如果你知道有一块瓷砖不在建筑物内,就在那里填上,其余的就是建筑物

如果你不知道这一点,那么你可以继续填海,直到没有更多的瓷砖,你将该地区划分为几个区域

事实上,如果不做一些假设,你就无法区分建筑物和其他建筑物。例如,假设您的区域被一条棕色线一分为二:哪一条是建筑

也许你知道地面的瓷砖比任何建筑物都多?那两个家伙
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;

/**
 * A* algorithm from http://en.wikipedia.org/wiki/A*_search_algorithm
 */
public class AStarAlgorithm {

    public class CellComparator implements Comparator<Cell>
    {
        @Override
        public int compare(Cell a, Cell b)
        {
            return Double.compare(a.f, b.f);
        }

    }

    /**
     * Find all cells that we can traverse from a given reference start point that's an outside cell.
     * Algorithm is like the A* path finding, but we don't stop when we found the goal, neither do we consider the calculation of the distance.
     * @param g
     * @param start
     * @param goal
     * @param allowDiagonals
     * @return
     */
    public Set<Cell> getFloodFillCells(Grid g, Cell start, boolean allowDiagonals) {

        Cell current = null;

        Set<Cell> closedSet = new HashSet<>();

        Set<Cell> openSet = new HashSet<Cell>();
        openSet.add(start);

        while (!openSet.isEmpty()) {

            current = openSet.iterator().next();

            openSet.remove(current);

            closedSet.add(current);

            for (Cell neighbor : g.getNeighbors(current, allowDiagonals)) {

                if (neighbor == null) {
                    continue;
                }

                if (closedSet.contains(neighbor)) {
                    continue;
                }

                openSet.add(neighbor);
            }

        }

        return closedSet;

    }

    /**
     * Find path from start to goal.
     * @param g
     * @param start
     * @param goal
     * @param allowDiagonals
     * @return
     */
    public List<Cell> findPath( Grid g, Cell start, Cell goal, boolean allowDiagonals) {

        Cell current = null;
        boolean containsNeighbor;

        int cellCount = g.rows * g.cols;

        Set<Cell> closedSet = new HashSet<>( cellCount);

        PriorityQueue<Cell> openSet = new PriorityQueue<Cell>( cellCount, new CellComparator());
        openSet.add( start);

        start.g = 0d;
        start.f = start.g + heuristicCostEstimate(start, goal);


        while( !openSet.isEmpty()) {

            current = openSet.poll();

            if( current == goal) {
                return reconstructPath( goal);
            }

            closedSet.add( current);

            for( Cell neighbor: g.getNeighbors( current, allowDiagonals)) {

                if( neighbor == null) {
                    continue;
                }

                if( closedSet.contains( neighbor)) {
                    continue;
                }

                double tentativeScoreG = current.g + distBetween( current, neighbor);

                if( !(containsNeighbor=openSet.contains( neighbor)) || Double.compare(tentativeScoreG, neighbor.g) < 0) {

                    neighbor.cameFrom = current;

                    neighbor.g = tentativeScoreG;

                    neighbor.h = heuristicCostEstimate(neighbor, goal);
                    neighbor.f = neighbor.g + neighbor.h;

                    if( !containsNeighbor) {
                        openSet.add( neighbor);
                    }
                }
            }

        }

        return new ArrayList<>();
    }

    private List<Cell> reconstructPath( Cell current) {

        List<Cell> totalPath = new ArrayList<>(200); // arbitrary value, we'll most likely have more than 10 which is default for java

        totalPath.add( current);

        while( (current = current.cameFrom) != null) {

            totalPath.add( current);

        }

        return totalPath;
    }

    private double distBetween(Cell current, Cell neighbor) {
        return heuristicCostEstimate( current, neighbor); // TODO: dist_between is heuristic_cost_estimate for our use-case; use various other heuristics
    }

    private double heuristicCostEstimate(Cell from, Cell to) {

        return Math.sqrt((from.col-to.col)*(from.col-to.col) + (from.row - to.row)*(from.row-to.row));

    }

}
Inside: 2/2
Inside: 7/3
Inside: 8/3
Inside: 8/4
Inside: 2/6
Inside: 3/6
Inside: 4/6
[8/8, 7/7, 7/6, 6/5, 5/4, 5/3, 5/2, 4/1, 3/0, 2/0, 1/0, 0/0]