【问题标题】:How to fill a triangle using lines?如何使用线条填充三角形?
【发布时间】:2019-05-19 08:22:05
【问题描述】:

我正在尝试使用水平线填充三角形,但我无法弄清楚我当前的方法有什么问题。在有人说只使用 fillPolygon 之前,我不能使用它。我需要使用线条填充它。 在某些情况下似乎可以正常工作,而在其他情况下则完全中断。

它应该是这样的。但后来我尝试将我的方法应用于旋转的 3D 立方体,然后......

我不知道出了什么问题。此外,红色边框也是我的三角形方法之一。这些工作完美,填充三角形和轮廓三角形输入了相同的顶点。

public void filledTri(int x1,int y1,int x2,int y2,int x3,int y3){
    int[] xs = {x1,x2,x3};
    int[] ys = {y1,y2,y3};
    //Sort vertices in vertical order so A/1 is highest and C/3 is lowest
    int I,tempx,tempy;
    for(int i=1;i<3;i++){
        I = i-1;
        tempx = xs[i];
        tempy = ys[i];
        while(I>=0&&tempy<ys[I]){
            xs[I+1] = xs[I];
            ys[I+1] = ys[I];
            I--;
        }
        xs[I+1] = tempx;
        ys[I+1] = tempy;
    }
    //Set left and right edges
    linepts ab = new linepts(xs[0],ys[0],xs[1],ys[1]),
    ac = new linepts(xs[0],ys[0],xs[2],ys[2]);
    linepts[] lines = {ab.getEndX() < ac.getEndX() ? ab : ac,
    ab.getEndX() > ac.getEndX() ? ab : ac,
    new linepts(xs[1],ys[1],xs[2],ys[2])};
    //Fill triangle
    int startY = ys[0],endY = ys[2];
    for(int y=startY;y<=endY;y++){
        if(y>ys[1])
        horizontalLine((int)Math.round(lines[2].getX(y)),
        y,
        (int)Math.round(lines[1].getX(y)));
        else
        horizontalLine((int)Math.round(lines[0].getX(y)),
        y,
        (int)Math.round(lines[1].getX(y)));
    }

getX(int y) 获取直线通过 y 值的 x 坐标。如果它是一条水平线,它只返回线的开始 x

A点是屏幕上的最高点和最低点,B点是中间点,C点是屏幕上的最低点和最高点

如果有帮助,我会在 jframe 上使用缓冲图像来绘制它。

【问题讨论】:

  • 我在第一个“工作”图像中看到的只是纯白色或接近白色的填充。应该出现在那里的线条是否平行?考虑将它们绘制到图像上,然后使用TexturePaint 填充Shape(本例中为三角形)。
  • “我不知道出了什么问题。” 嗯.. 现在我更仔细地查看图像,我意识到我不知道你想要达到什么目的。第一个三角形中的水平线在哪里? 一般提示:如需尽快获得更好的帮助,请edit 添加minimal reproducible exampleShort, Self Contained, Correct Example
  • 你能展示getXhorizontalLine的实现吗?如果不仔细审查,问题可能出在他们身上。
  • public double getX(int y){ double m = ((double)(y1-y2))/((double)(x1-x2)); if(m==0) return x1; else return (double)(y-y1)/m+x1; }
  • public void horizontalLine(int x1,int y,int x2){ //Only one of these will run, the second one only runs //if x2 &lt; x1 for(int x=x1;x&lt;=x2;x++) pixel(x,y); for(int x=x1;x&gt;=x2;x--) pixel(x,y); }

标签: java swing awt computational-geometry java-2d


【解决方案1】:

我没有仔细检查你的代码,但我可以告诉你,你并不总是加入与相关方的交叉点。

您可以按以下方式工作:

对于给定的扫描线(一些Y),

  • 成对比较三个边的端点坐标(Y0-Y1Y1-Y2Y2-Y0),

  • 将有零或两条边跨越Y;使用条件(Yi &gt; Y) != (Yi+1 &gt; Y)(索引模3),没有其他,

  • 对于跨越Y 的边,计算交点。

您将从min(Y0, Y1, Y2) 扫描到max(Y0, Y1, Y2) 并每次加入两个交叉路口。

【讨论】:

  • 这会不会造成很大的压力,尤其是在更高的多边形模型上?
  • 你所说的“压力很大”。这个解决方案本质上是你的,有一个修复。
  • 您可以使用扫描线的方法来优化它,这相当于将三角形分解为三角形和三角形或梯形;但由于您的主要问题是正确性,我向您展示了一个防弹解决方案(但相当有效)
【解决方案2】:

我在Software Renderer tutorial 中看到了你在做什么。 thisthis 剧集中对此进行了解释。

他在那里所做的是扫描最长以获取该行上的每个像素,它存储最小 X 值和最大 X 值(由其他 2 行给出)。他最初是为特定的三角形制作的,但后来他升级了代码以接受通用三角形。

这是一个很好的图表来解释这一点:

我假设您遇到的情况是因为将 3D 三角形投影到 2D 三角形中(剪辑,三角形获得无限坐标,或者因为您的程序不能很好地处理空三角形。

【讨论】:

  • 问题是,红色轮廓三角形与填充三角形具有相同的顶点。实心三角形有时有效,有时无效,我在那些无效的三角形中找不到任何相似之处。据我所知,这种方法非常快,但我觉得当我将它从另一种编码语言转换为 java 时,我没有正确转换它。
【解决方案3】:

一种方法是在图像上画线,然后在TexturePaint 中使用该图像来填充Shape(在本例中为三角形)。

它可能看起来像这样:(如果您使用包含一条红线的单个图像,将其放在随机的 BG 颜色上,并使用平滑的 1.5 像素笔划将形状本身绘制为蓝色)。

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.util.*;

public class LinesFillShape {

    private JComponent ui = null;

    LinesFillShape() {
        initUI();
    }

    public final void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        ui.add(new JLabel(new ImageIcon(getImage())));
    }

    private void drawPolygon(Graphics2D g, int sz, Random r) {
        int[] xpoints = {
            r.nextInt(sz), r.nextInt(sz), r.nextInt(sz)
        };
        int[] ypoints = {
            r.nextInt(sz), r.nextInt(sz), r.nextInt(sz)
        };
        Polygon p = new Polygon(xpoints, ypoints, 3);
        Color bg = new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255));
        g.setColor(bg);
        g.fill(p);

        g.setPaint(
                new TexturePaint(getTexture(),
                        new Rectangle2D.Double(0, 0, 8, 8)));
        g.fill(p);
        g.setStroke(new BasicStroke(1.5f));
        g.setColor(Color.BLUE);
        g.draw(p);
    }

    private BufferedImage getImage() {
        int sz = 600;
        BufferedImage bi = new BufferedImage(sz, sz, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bi.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        Random r = new Random();
        drawPolygon(g, sz, r);
        drawPolygon(g, sz, r);
        drawPolygon(g, sz, r);

        g.dispose();
        return bi;
    }

    private BufferedImage getTexture() {
        BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
        Graphics g = bi.getGraphics();
        g.setColor(Color.RED);
        // TODO: something more interesting here.. 
        g.drawLine(0, 0, 0, 8);
        g.dispose();

        return bi;
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            try {
                UIManager.setLookAndFeel(
                        UIManager.getSystemLookAndFeelClassName());
            } catch (Exception useDefault) {
            }
            LinesFillShape o = new LinesFillShape();

            JFrame f = new JFrame(o.getClass().getSimpleName());
            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            f.setLocationByPlatform(true);

            f.setContentPane(o.getUI());
            f.pack();
            f.setMinimumSize(f.getSize());

            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);
    }
}

【讨论】:

  • 非常适合 Java2D,但请注意纹理如何附着在屏幕上而不是表面上;对于 3D,请考虑 TriangleMeshPhongMaterial
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多