Java 地形曲线到点阵列

Java 地形曲线到点阵列,java,algorithm,andengine,points,curve,Java,Algorithm,Andengine,Points,Curve,在我的2D游戏中,我使用图形工具创建漂亮、平滑的黑色地形: 用java编写的简单算法每15个像素查找一次黑色,生成以下一组线条(灰色): 正如你所看到的,有些地方的地图绘制得非常糟糕,有些相当不错。在其他情况下,不需要每15个像素采样一次,例如,如果地形平坦 使用尽可能少的点将此曲线转换为一组点[线]的最佳方法是什么? 每15像素采样一次=55 FPS,每10像素采样一次=40 FPS 以下算法正在执行该任务,从右到左采样,并将可粘贴输出到代码数组中: public void loadMap

在我的2D游戏中,我使用图形工具创建漂亮、平滑的黑色地形:

用java编写的简单算法每15个像素查找一次黑色,生成以下一组线条(灰色):

正如你所看到的,有些地方的地图绘制得非常糟糕,有些相当不错。在其他情况下,不需要每15个像素采样一次,例如,如果地形平坦

使用尽可能少的点将此曲线转换为一组点[线]的最佳方法是什么? 每15像素采样一次=55 FPS,每10像素采样一次=40 FPS

以下算法正在执行该任务,从右到左采样,并将可粘贴输出到代码数组中:

public void loadMapFile(String path) throws IOException {
    File mapFile = new File(path);
    image = ImageIO.read(mapFile);
    boolean black;
    System.out.print("{ ");

    int[] lastPoint = {0, 0};

    for (int x = image.getWidth()-1; x >= 0; x -= 15) {
        for (int y = 0; y < image.getHeight(); y++) {
            black = image.getRGB(x, y) == -16777216 ? true : false;

            if (black) {
                lastPoint[0] = x;
                lastPoint[1] = y;
                System.out.print("{" + (x) + ", " + (y) + "}, ");
                break;
            }

        }
    }

    System.out.println("}");
}
public void loadMapFile(字符串路径)引发IOException{
文件映射文件=新文件(路径);
image=ImageIO.read(映射文件);
布尔黑;
系统输出打印(“{”);
int[]lastPoint={0,0};
对于(int x=image.getWidth()-1;x>=0;x-=15){
对于(int y=0;y

我在Android上开发,使用Java和AndEngine

这个问题几乎与信号(如声音)的数字化问题相同,其中的基本规律是,输入中频率过高的信号不会反映在数字化输出中。所以问题是,如果你检查30个像素,然后按照bmorris591的建议测试中间,你可能会错过采样点之间的7个像素孔。这表明,如果有10个像素的特征你不能错过,你需要每5个像素扫描一次:你的采样率应该是信号中最高频率的两倍

可以帮助改进算法的一件事是更好的y维搜索。当前,您正在线性搜索天空和地形之间的交点,但二进制搜索应该更快

int y = image.getHeight()/2; // Start searching from the middle of the image
int yIncr = y/2;
while (yIncr>0) {
    if (image.getRGB(x, y) == -16777216) {
        // We hit the terrain, to towards the sky
        y-=yIncr;
    } else {
        // We hit the sky, go towards the terrain
        y+=yIncr;
    }
    yIncr = yIncr/2;
}
// Make sure y is on the first terrain point: move y up or down a few pixels
// Only one of the following two loops will execute, and only one or two iterations max
while (image.getRGB(x, y) != -16777216) y++; 
while (image.getRGB(x, y-1) == -16777216) y--;

其他优化也是可能的。如果您知道您的地形没有悬崖,则只需搜索从lastY+maxDropoff到lastY maxDropoff的窗口。此外,如果地形的高度永远无法达到整个位图的高度,则也不需要搜索位图的顶部。这将有助于释放一些CPU周期,您可以使用这些CPU周期对地形进行更高分辨率的x扫描。

最有效的解决方案(关于所需的点)是允许沿x轴的点之间存在可变间距。这样,大型平坦部分将需要很少的点/样本,而复杂地形将需要更多

在三维网格处理中,有一种很好的网格简化算法,名为“二次边折叠”,可以适应您的问题

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Program {

    public static void main(String[] args) throws IOException {
        BufferedImage image = ImageIO.read(new File("/home/michal/Desktop/FkXG1.png"));
        PathFinder pathFinder = new PathFinder(10);
        List<Point> borderPoints = pathFinder.findBorderPoints(image);
        System.out.println(Arrays.toString(borderPoints.toArray()));
        System.out.println(borderPoints.size());

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new ImageBorderPanel(image, borderPoints));
        frame.pack();
        frame.setMinimumSize(new Dimension(image.getWidth(), image.getHeight()));
        frame.setVisible(true);
    }
}

class PathFinder {

    private int maxDelta = 3;

    public PathFinder(int delta) {
        this.maxDelta = delta;
    }

    public List<Point> findBorderPoints(BufferedImage image) {
        int width = image.getWidth();
        int[][] imageInBytes = convertTo2DWithoutUsingGetRGB(image);
        int[] borderPoints = findBorderPoints(width, imageInBytes);

        List<Integer> indexes = dwindlePoints(width, borderPoints);
        List<Point> points = new ArrayList<Point>(indexes.size());
        for (Integer index : indexes) {
            points.add(new Point(index, borderPoints[index]));
        }
        return points;
    }

    private List<Integer> dwindlePoints(int width, int[] borderPoints) {
        List<Integer> indexes = new ArrayList<Integer>(width);
        indexes.add(borderPoints[0]);
        int delta = 0;
        for (int index = 1; index < width; index++) {
            delta += Math.abs(borderPoints[index - 1] - borderPoints[index]);
            if (delta >= maxDelta) {
                indexes.add(index);
                delta = 0;
            }
        }
        return indexes;
    }

    private int[] findBorderPoints(int width, int[][] imageInBytes) {
        int[] borderPoints = new int[width];
        int black = Color.BLACK.getRGB();
        for (int y = 0; y < imageInBytes.length; y++) {
            int maxX = imageInBytes[y].length;
            for (int x = 0; x < maxX; x++) {
                int color = imageInBytes[y][x];
                if (color == black && borderPoints[x] == 0) {
                    borderPoints[x] = y;
                }
            }
        }
        return borderPoints;
    }

    private int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) {
        final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        final int width = image.getWidth();
        final int height = image.getHeight();
        final boolean hasAlphaChannel = image.getAlphaRaster() != null;

        int[][] result = new int[height][width];
        if (hasAlphaChannel) {
            final int pixelLength = 4;
            for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                int argb = 0;
                argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
                argb += ((int) pixels[pixel + 1] & 0xff); // blue
                argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
                argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
                result[row][col] = argb;
                col++;
                if (col == width) {
                    col = 0;
                    row++;
                }
            }
        } else {
            final int pixelLength = 3;
            for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                int argb = 0;
                argb += -16777216; // 255 alpha
                argb += ((int) pixels[pixel] & 0xff); // blue
                argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
                argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
                result[row][col] = argb;
                col++;
                if (col == width) {
                    col = 0;
                    row++;
                }
            }
        }

        return result;
    }
}

class ImageBorderPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    private BufferedImage image;
    private List<Point> borderPoints;

    public ImageBorderPanel(BufferedImage image, List<Point> borderPoints) {
        this.image = image;
        this.borderPoints = borderPoints;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, null);

        Graphics2D graphics2d = (Graphics2D) g;

        g.setColor(Color.YELLOW);
        for (Point point : borderPoints) {
            graphics2d.fillRect(point.x, point.y, 3, 3);
        }
    }
}
这是一个想法,转化为您的问题-它实际上比原始3D算法简单得多:

  • 用太多的点表示曲线
  • 对于每个点,如果删除,则测量误差(即与平滑地形的差异)
  • 删除产生最小误差的点
  • 重复此操作,直到点数减少得足够多或错误过大为止 更精确地说,关于第2步:给定点
    p,Q,R
    Q
    的误差是两条直线的地形近似值
    p->Q
    Q->R
    ,与一条直线的地形近似值
    p->R
    之间的差值


    请注意,删除点时,只有其相邻点需要更新其错误值

    我建议找到存在于白色和黑色像素边界上的边界点。之后,我们可以数字化这些点。要做到这一点,我们应该定义
    DELTA,它指定了我们应该跳过的点和应该添加到结果列表中的点

    DELTA = 3, Number of points = 223
    

    下面,我已经把源代码,打印图像和寻找点。我希望你能读懂它,找到解决问题的方法

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.image.BufferedImage;
    import java.awt.image.DataBufferByte;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Program {
    
        public static void main(String[] args) throws IOException {
            BufferedImage image = ImageIO.read(new File("/home/michal/Desktop/FkXG1.png"));
            PathFinder pathFinder = new PathFinder(10);
            List<Point> borderPoints = pathFinder.findBorderPoints(image);
            System.out.println(Arrays.toString(borderPoints.toArray()));
            System.out.println(borderPoints.size());
    
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new ImageBorderPanel(image, borderPoints));
            frame.pack();
            frame.setMinimumSize(new Dimension(image.getWidth(), image.getHeight()));
            frame.setVisible(true);
        }
    }
    
    class PathFinder {
    
        private int maxDelta = 3;
    
        public PathFinder(int delta) {
            this.maxDelta = delta;
        }
    
        public List<Point> findBorderPoints(BufferedImage image) {
            int width = image.getWidth();
            int[][] imageInBytes = convertTo2DWithoutUsingGetRGB(image);
            int[] borderPoints = findBorderPoints(width, imageInBytes);
    
            List<Integer> indexes = dwindlePoints(width, borderPoints);
            List<Point> points = new ArrayList<Point>(indexes.size());
            for (Integer index : indexes) {
                points.add(new Point(index, borderPoints[index]));
            }
            return points;
        }
    
        private List<Integer> dwindlePoints(int width, int[] borderPoints) {
            List<Integer> indexes = new ArrayList<Integer>(width);
            indexes.add(borderPoints[0]);
            int delta = 0;
            for (int index = 1; index < width; index++) {
                delta += Math.abs(borderPoints[index - 1] - borderPoints[index]);
                if (delta >= maxDelta) {
                    indexes.add(index);
                    delta = 0;
                }
            }
            return indexes;
        }
    
        private int[] findBorderPoints(int width, int[][] imageInBytes) {
            int[] borderPoints = new int[width];
            int black = Color.BLACK.getRGB();
            for (int y = 0; y < imageInBytes.length; y++) {
                int maxX = imageInBytes[y].length;
                for (int x = 0; x < maxX; x++) {
                    int color = imageInBytes[y][x];
                    if (color == black && borderPoints[x] == 0) {
                        borderPoints[x] = y;
                    }
                }
            }
            return borderPoints;
        }
    
        private int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) {
            final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
            final int width = image.getWidth();
            final int height = image.getHeight();
            final boolean hasAlphaChannel = image.getAlphaRaster() != null;
    
            int[][] result = new int[height][width];
            if (hasAlphaChannel) {
                final int pixelLength = 4;
                for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                    int argb = 0;
                    argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
                    argb += ((int) pixels[pixel + 1] & 0xff); // blue
                    argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
                    argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
                    result[row][col] = argb;
                    col++;
                    if (col == width) {
                        col = 0;
                        row++;
                    }
                }
            } else {
                final int pixelLength = 3;
                for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                    int argb = 0;
                    argb += -16777216; // 255 alpha
                    argb += ((int) pixels[pixel] & 0xff); // blue
                    argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
                    argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
                    result[row][col] = argb;
                    col++;
                    if (col == width) {
                        col = 0;
                        row++;
                    }
                }
            }
    
            return result;
        }
    }
    
    class ImageBorderPanel extends JPanel {
    
        private static final long serialVersionUID = 1L;
    
        private BufferedImage image;
        private List<Point> borderPoints;
    
        public ImageBorderPanel(BufferedImage image, List<Point> borderPoints) {
            this.image = image;
            this.borderPoints = borderPoints;
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(image, 0, 0, null);
    
            Graphics2D graphics2d = (Graphics2D) g;
    
            g.setColor(Color.YELLOW);
            for (Point point : borderPoints) {
                graphics2d.fillRect(point.x, point.y, 3, 3);
            }
        }
    }
    
    导入java.awt.Color;
    导入java.awt.Dimension;
    导入java.awt.Graphics;
    导入java.awt.Graphics2D;
    导入java.awt.Point;
    导入java.awt.image.buffereImage;
    导入java.awt.image.DataBufferByte;
    导入java.io.File;
    导入java.io.IOException;
    导入java.util.ArrayList;
    导入java.util.array;
    导入java.util.List;
    导入javax.imageio.imageio;
    导入javax.swing.JFrame;
    导入javax.swing.JPanel;
    公共课程{
    公共静态void main(字符串[]args)引发IOException{
    BuffereImage image=ImageIO.read(新文件(“/home/michal/Desktop/FkXG1.png”);
    探路者探路者=新探路者(10);
    List borderPoints=pathFinder.findBorderPoints(图像);
    System.out.println(Arrays.toString(borderPoints.toArray());
    System.out.println(borderPoints.size());
    JFrame=新JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(新的ImageBorderPanel(图像,边界点));
    frame.pack();
    frame.setMinimumSize(新维度(image.getWidth(),image.getHeight());
    frame.setVisible(true);
    }
    }
    类探路者{
    私有整数maxDelta=3;
    公共探路者(int delta){
    this.maxDelta=delta;
    }
    公共列表findBorderPoints(BuffereImage图像){
    int width=image.getWidth();
    int[][]imageInBytes=在不使用GetRGB(图像)的情况下转换为2;
    int[]borderPoints=findBorderPoints(宽度,imageInBytes);
    列表索引=缩小点(宽度、边界点);
    列表点=新的ArrayList(index.size());
    for(整数索引:索引){
    点。添加(新点(索引,边界点[ind
    
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.image.BufferedImage;
    import java.awt.image.DataBufferByte;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Program {
    
        public static void main(String[] args) throws IOException {
            BufferedImage image = ImageIO.read(new File("/home/michal/Desktop/FkXG1.png"));
            PathFinder pathFinder = new PathFinder(10);
            List<Point> borderPoints = pathFinder.findBorderPoints(image);
            System.out.println(Arrays.toString(borderPoints.toArray()));
            System.out.println(borderPoints.size());
    
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new ImageBorderPanel(image, borderPoints));
            frame.pack();
            frame.setMinimumSize(new Dimension(image.getWidth(), image.getHeight()));
            frame.setVisible(true);
        }
    }
    
    class PathFinder {
    
        private int maxDelta = 3;
    
        public PathFinder(int delta) {
            this.maxDelta = delta;
        }
    
        public List<Point> findBorderPoints(BufferedImage image) {
            int width = image.getWidth();
            int[][] imageInBytes = convertTo2DWithoutUsingGetRGB(image);
            int[] borderPoints = findBorderPoints(width, imageInBytes);
    
            List<Integer> indexes = dwindlePoints(width, borderPoints);
            List<Point> points = new ArrayList<Point>(indexes.size());
            for (Integer index : indexes) {
                points.add(new Point(index, borderPoints[index]));
            }
            return points;
        }
    
        private List<Integer> dwindlePoints(int width, int[] borderPoints) {
            List<Integer> indexes = new ArrayList<Integer>(width);
            indexes.add(borderPoints[0]);
            int delta = 0;
            for (int index = 1; index < width; index++) {
                delta += Math.abs(borderPoints[index - 1] - borderPoints[index]);
                if (delta >= maxDelta) {
                    indexes.add(index);
                    delta = 0;
                }
            }
            return indexes;
        }
    
        private int[] findBorderPoints(int width, int[][] imageInBytes) {
            int[] borderPoints = new int[width];
            int black = Color.BLACK.getRGB();
            for (int y = 0; y < imageInBytes.length; y++) {
                int maxX = imageInBytes[y].length;
                for (int x = 0; x < maxX; x++) {
                    int color = imageInBytes[y][x];
                    if (color == black && borderPoints[x] == 0) {
                        borderPoints[x] = y;
                    }
                }
            }
            return borderPoints;
        }
    
        private int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) {
            final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
            final int width = image.getWidth();
            final int height = image.getHeight();
            final boolean hasAlphaChannel = image.getAlphaRaster() != null;
    
            int[][] result = new int[height][width];
            if (hasAlphaChannel) {
                final int pixelLength = 4;
                for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                    int argb = 0;
                    argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
                    argb += ((int) pixels[pixel + 1] & 0xff); // blue
                    argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
                    argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
                    result[row][col] = argb;
                    col++;
                    if (col == width) {
                        col = 0;
                        row++;
                    }
                }
            } else {
                final int pixelLength = 3;
                for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
                    int argb = 0;
                    argb += -16777216; // 255 alpha
                    argb += ((int) pixels[pixel] & 0xff); // blue
                    argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
                    argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
                    result[row][col] = argb;
                    col++;
                    if (col == width) {
                        col = 0;
                        row++;
                    }
                }
            }
    
            return result;
        }
    }
    
    class ImageBorderPanel extends JPanel {
    
        private static final long serialVersionUID = 1L;
    
        private BufferedImage image;
        private List<Point> borderPoints;
    
        public ImageBorderPanel(BufferedImage image, List<Point> borderPoints) {
            this.image = image;
            this.borderPoints = borderPoints;
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(image, 0, 0, null);
    
            Graphics2D graphics2d = (Graphics2D) g;
    
            g.setColor(Color.YELLOW);
            for (Point point : borderPoints) {
                graphics2d.fillRect(point.x, point.y, 3, 3);
            }
        }
    }