Как заполнить треугольник с помощью линий? - PullRequest
0 голосов
/ 19 декабря 2018

Я пытаюсь заполнить треугольник, используя горизонтальные линии, и я не могу понять, что не так с моим текущим методом.Прежде чем кто-то скажет просто использовать fillPolygon, я не могу это использовать.Мне нужно заполнить его, используя строки.Кажется, что в одних ситуациях все работает нормально, а в других - полностью.

enter image description here

Вот так это должно выглядеть.Но затем я попытался применить свой метод к вращающемуся трехмерному кубу и ...

enter image description here

Я понятия не имею, что не так.Кроме того, красные границы также являются одним из моих методов треугольника.Они отлично работают, и заполненные треугольники и выделенные треугольники имеют одинаковые введенные вершины.

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) возвращает мне координату x, где линия проходит через значение y.Если это горизонтальная линия, она просто возвращает начало линии x

Точка A является самой высокой на экране и самым низким значением, B является серединой, а C является самой низкой на экране и самым высоким значением

Я использую буферизованное изображение на jframe, чтобы нарисовать его, если это поможет.

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

Я видел, что вы делаете в учебнике Software Renderer .Это объясняется в этом и этом эпизодах.

То, что он делает там, сканирует самое длинное, чтобы получить каждый пиксель в этой строке, в нем хранятся значения min X и max X (заданные двумя другими строками).Первоначально он делает это для определенных треугольников, но затем он обновляет код, чтобы принимать общие треугольники.

Вот хорошая диаграмма, чтобы объяснить, что: enter image description here

Я предполагаюто, что вы испытываете, это из-за проецирования трехмерных треугольников в двухмерные (отсечение, треугольники получают бесконечные координаты, или потому что ваша программа не принимает слишком хорошо пустые треугольники.

0 голосов
/ 19 декабря 2018

Один из способов - нарисовать линии на изображении, а затем использовать это изображение в TexturePaint, чтобы заполнить Shape (в данном случае треугольник).

Это может выглядеть примерно так: (если вы используете одно изображение, содержащее одну красную линию, поместите его поверх случайного BG-цвета и используйте сглаженный мазок в 1,5 пикселя, чтобы нарисовать саму фигуру синим цветом).

enter image description here

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);
    }
}
0 голосов
/ 19 декабря 2018

Я не проверял ваш код, но могу сказать, что вы не всегда соединяете пересечения с соответствующими сторонами.

Вы можете работать следующим образом:

Для данной строки сканирования (некоторыеY),

  • сравнить ординаты конечных точек трех сторон попарно (Y0-Y1, Y1-Y2, Y2-Y0),

  • будет ноль или две стороны, которые колеблются Y;используйте условие (Yi > Y) != (Yi+1 > Y) (индексы по модулю 3) и никакие другие,

  • для сторон, которые охватывают Y, вычислите точку пересечения.

Вы будете сканировать от min(Y0, Y1, Y2) до max(Y0, Y1, Y2) и каждый раз соединять два перекрестка.

...