Clyme
Clyme

Reputation: 94

Filling a Quadrilateral

I have some code that renders a cube and rotates it if an arrow key is pressed.

Now I tried to fill one side of the cube which, at first, worked just fine.

But after I observed it a little bit I noticed that, if I rotate the cube in certain angle, the surface that should be filled behaves very weird.

Code:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;

public class Cube extends JFrame {
double p[][] = new double[9][4];

int x=1, y=2, z=3;

boolean xRotUp = false;
boolean yRotRight = false;
boolean xRotDown = false;
boolean yRotLeft = false;

double px, py, pz;

double angle_x = 0.02;
double angle_y = 0.0150;
double angle_z = 0.010; 


public Cube() {
    setSize(500,500);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true);

    addKeyListener(new KeyListener() {

        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyReleased(KeyEvent e) {
            switch (e.getKeyCode()) {
            case 37:
                yRotLeft = false;
                break;
            case 38:
                xRotUp = false;
                break;
            case 39:
                yRotRight = false;
                break;
            case 40:
                xRotDown = false;
                break;
        }
        }

        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case 37:
                    if (!yRotRight) {
                    yRotLeft = true;
                    } else {
                        yRotRight = false;
                    }
                    break;
                case 38:
                    if (!xRotDown) {
                    xRotUp = true;
                    } else {
                        xRotDown = false;
                    }
                    break;
                case 39:
                    if (!yRotLeft) {
                    yRotRight = true;
                    } else {
                        yRotLeft = false;
                    }
                    break;
                case 40:
                    if (!xRotUp) {
                    xRotDown = true;
                    } else {
                        xRotUp = false;
                    }
                    break;
            }
        }
    });
}

public void rotate() {
    for (int i=0; i<9;i++) {
        px = p[i][x];
        py = p[i][y];
        pz = p[i][z];
        if (xRotUp) {
            p[i][y] = py*Math.cos(angle_x)-pz*Math.sin(angle_x);
            p[i][z] = py*Math.sin(angle_x)+pz*Math.cos(angle_x);

            py = p[i][y];
            pz = p[i][z];
        }

        if (xRotDown) {
             p[i][y] = py*Math.cos(-angle_x)-pz*Math.sin(-angle_x);
                p[i][z] = py*Math.sin(-angle_x)+pz*Math.cos(-angle_x);

                py = p[i][y];
                pz = p[i][z];
        }

        if (yRotRight) {
            p[i][x] = px*Math.cos(angle_y)+pz*Math.sin(angle_y);
            p[i][z] =-px*Math.sin(angle_y)+pz*Math.cos(angle_y);

            px = p[i][x];
        }

        if (yRotLeft) {
            p[i][x] = px*Math.cos(-angle_y)+pz*Math.sin(-angle_y);
            p[i][z] =-px*Math.sin(-angle_y)+pz*Math.cos(-angle_y);

            px = p[i][x];
        }
    }
}

public void init() {
    setBackground(new Color(0,0,0));

    p[1][x] = -100; p[1][y] = -100; p[1][z] = -100;
    p[2][x] = +100; p[2][y] = -100; p[2][z] = -100;
    p[3][x] = +100; p[3][y] = -100; p[3][z] = +100;
    p[4][x] = -100; p[4][y] = -100; p[4][z] = +100;
    p[5][x] = -100; p[5][y] = +100; p[5][z] = -100;
    p[6][x] = +100; p[6][y] = +100; p[6][z] = -100;
    p[7][x] = +100; p[7][y] = +100; p[7][z] = +100;
    p[8][x] = -100; p[8][y] = +100; p[8][z] = +100;

    /*       8 - - - - - 7
           / |         / |
          5 - - - - - 6  |
          |  |        |  |
          |  4 - - - -|- 3
          | /         | /
          1 - - - - - 2
   */
}

Image buffer;
Graphics2D gBuffer;

public void paint(Graphics g) {
    if (buffer==null) {
        buffer=createImage(this.getSize().width, this.getSize().height);
        gBuffer=(Graphics2D)buffer.getGraphics();
    }
    gBuffer.clearRect(0,0,this.getSize().width, this.getSize().height);

    gBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);   

    gBuffer.setColor(Color.LIGHT_GRAY);
    gBuffer.drawLine((int)(p[4][x])+200,(int)(p[4][y])+200,(int)(p[6][x])+200,(int)(p[6][y])+200);
    gBuffer.drawLine((int)(p[2][x])+200,(int)(p[2][y])+200,(int)(p[8][x])+200,(int)(p[8][y])+200);
    gBuffer.drawLine((int)(p[1][x])+200,(int)(p[1][y])+200,(int)(p[7][x])+200,(int)(p[7][y])+200);
    gBuffer.drawLine((int)(p[5][x])+200,(int)(p[5][y])+200,(int)(p[3][x])+200,(int)(p[3][y])+200);

    gBuffer.setColor(Color.DARK_GRAY);
    gBuffer.fillPolygon(new int[] {
            (int)p[1][x]+200,
            (int)p[2][x]+200,
            (int)p[6][x]+200,
            (int)p[5][x]+200},
                new int[] {
            (int)p[1][y]+200,
            (int)p[2][y]+200,
            (int)p[5][y]+200,
            (int)p[6][y]+200
                } , 4);
    gBuffer.setColor(Color.CYAN);
    gBuffer.drawLine((int)(p[1][x])+200,(int)(p[1][y])+200,(int)(p[2][x])+200,(int)(p[2][y])+200);
    gBuffer.drawLine((int)(p[2][x])+200,(int)(p[2][y])+200,(int)(p[3][x])+200,(int)(p[3][y])+200);
    gBuffer.drawLine((int)(p[3][x])+200,(int)(p[3][y])+200,(int)(p[4][x])+200,(int)(p[4][y])+200);
    gBuffer.drawLine((int)(p[4][x])+200,(int)(p[4][y])+200,(int)(p[1][x])+200,(int)(p[1][y])+200);
    gBuffer.drawLine((int)(p[5][x])+200,(int)(p[5][y])+200,(int)(p[6][x])+200,(int)(p[6][y])+200);
    gBuffer.drawLine((int)(p[6][x])+200,(int)(p[6][y])+200,(int)(p[7][x])+200,(int)(p[7][y])+200);
    gBuffer.setColor(Color.RED);
    gBuffer.drawLine((int)(p[7][x])+200,(int)(p[7][y])+200,(int)(p[8][x])+200,(int)(p[8][y])+200);
    gBuffer.setColor(Color.CYAN);
    gBuffer.drawLine((int)(p[8][x])+200,(int)(p[8][y])+200,(int)(p[5][x])+200,(int)(p[5][y])+200);
    gBuffer.drawLine((int)(p[1][x])+200,(int)(p[1][y])+200,(int)(p[5][x])+200,(int)(p[5][y])+200);
    gBuffer.drawLine((int)(p[2][x])+200,(int)(p[2][y])+200,(int)(p[6][x])+200,(int)(p[6][y])+200);
    gBuffer.drawLine((int)(p[3][x])+200,(int)(p[3][y])+200,(int)(p[7][x])+200,(int)(p[7][y])+200);
    gBuffer.setColor(Color.BLUE);
    gBuffer.drawLine((int)(p[4][x])+200,(int)(p[4][y])+200,(int)(p[8][x])+200,(int)(p[8][y])+200);

    g.drawImage (buffer, 0, 0, this);
    try {Thread.sleep(20);}
    catch (InterruptedException e) {}



    //for (int i=1;i<9;i++) { 
        // Rotation um z-Achse

        //p[i][x] = px*Math.cos(angle_z)-py*Math.sin(angle_z);
        //p[i][y] = py*Math.cos(angle_z)+px*Math.sin(angle_z);
   // }

    rotate();

    repaint();
}

public void update(Graphics g) {paint(g);}
}

Upvotes: 0

Views: 207

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347204

You "key" problem is the order of your polygon points...

gBuffer.fillPolygon(new int[]{
    (int) p[1][x] + 200,
    (int) p[2][x] + 200,
    (int) p[6][x] + 200,
    (int) p[5][x] + 200},
        new int[]{
            (int) p[1][y] + 200,
            (int) p[2][y] + 200,
            (int) p[5][y] + 200,
            (int) p[6][y] + 200
        }, 4);

Your x points are in the order of 1, 2, 6, 5, but your y points are in the order of 1, 2, 5, 6, which is wrong

It should be...

gBuffer.fillPolygon(new int[]{
    (int) p[1][x] + 200,
    (int) p[2][x] + 200,
    (int) p[6][x] + 200,
    (int) p[5][x] + 200},
        new int[]{
            (int) p[1][y] + 200,
            (int) p[2][y] + 200,
            (int) p[6][y] + 200,
            (int) p[5][y] + 200
        }, 4);

Now, take a look at Painting in AWT and Swing and Performing Custom Painting. It is highly unrecommended to override paint (or update) generally, but especially when dealing with top level containers. There are lots of reason, double buffering be one of them, but also, 0x0 is actually under the frame border, which is usually unexpected.

Remember, painting is for painting, you should never modify the state of the UI in anyway during a paint cycle and you should never, directly or indirectly, call repaint from within a paint method, when you do this, it's likely you will max out your CPU quickly and your program will become unresponsive.

If you need to continuously check the state of input and update the UI, consider using a Swing Timer instead

Swing components are double buffered by default, it also doesn't lock you into a single use like extending from a JFrame does.

You should also avoid using KeyListeners, mostly because they are fickle when it comes to needing keyboard focus. Generally the Key Bindings API will provide you with a much more robust API

For example...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Cube extends JPanel {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Cube());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    double p[][] = new double[9][4];

    int x = 1, y = 2, z = 3;

    public enum HorizontalRotation {
        LEFT,
        RIGHT,
        NONE
    }
    public enum VerticalRotation {
        UP,
        DOWN,
        NONE
    }

    private HorizontalRotation horizontalRotation = HorizontalRotation.NONE;
    private VerticalRotation verticalRotation = VerticalRotation.NONE;

//  boolean xRotUp = false;
//  boolean yRotRight = false;
//  boolean xRotDown = false;
//  boolean yRotLeft = false;

    double px, py, pz;

    double angle_x = 0.02;
    double angle_y = 0.0150;
    double angle_z = 0.010;

    public Cube() {
        init();
        addKeyBinding("Left", KeyEvent.VK_LEFT, HorizontalRotation.LEFT);
        addKeyBinding("Right", KeyEvent.VK_RIGHT, HorizontalRotation.RIGHT);
        addKeyBinding("Up", KeyEvent.VK_UP, VerticalRotation.UP);
        addKeyBinding("Down", KeyEvent.VK_DOWN, VerticalRotation.DOWN);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(400, 400);
    }

    protected void addKeyBinding(String name, int keyCode, HorizontalRotation rotation) {
        addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(keyCode, 0, false), new HorizontalRotationAction(rotation));
        addKeyBinding(name + ".released", KeyStroke.getKeyStroke(keyCode, 0, true), new HorizontalRotationAction(HorizontalRotation.NONE));
    }

    protected void addKeyBinding(String name, int keyCode, VerticalRotation rotation) {
        addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(keyCode, 0, false), new VerticalRotationAction(rotation));
        addKeyBinding(name + ".released", KeyStroke.getKeyStroke(keyCode, 0, true), new VerticalRotationAction(VerticalRotation.NONE));
    }

    protected void addKeyBinding(String name, KeyStroke keyStroke, Action action) {
        InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
        ActionMap am = getActionMap();

        im.put(keyStroke, name);
        am.put(name, action);
    }

    public void rotate() {
        for (int i = 0; i < 9; i++) {
            px = p[i][x];
            py = p[i][y];
            pz = p[i][z];
            if (verticalRotation == VerticalRotation.UP) {
                p[i][y] = py * Math.cos(angle_x) - pz * Math.sin(angle_x);
                p[i][z] = py * Math.sin(angle_x) + pz * Math.cos(angle_x);

                py = p[i][y];
                pz = p[i][z];
            } else if (verticalRotation == VerticalRotation.DOWN) {
                p[i][y] = py * Math.cos(-angle_x) - pz * Math.sin(-angle_x);
                p[i][z] = py * Math.sin(-angle_x) + pz * Math.cos(-angle_x);

                py = p[i][y];
                pz = p[i][z];
            }

            if (horizontalRotation == HorizontalRotation.RIGHT) {
                p[i][x] = px * Math.cos(angle_y) + pz * Math.sin(angle_y);
                p[i][z] = -px * Math.sin(angle_y) + pz * Math.cos(angle_y);

                px = p[i][x];
            } else if (horizontalRotation == HorizontalRotation.LEFT) {
                p[i][x] = px * Math.cos(-angle_y) + pz * Math.sin(-angle_y);
                p[i][z] = -px * Math.sin(-angle_y) + pz * Math.cos(-angle_y);

                px = p[i][x];
            }
        }
        repaint();
    }

    public void init() {
        setBackground(new Color(0, 0, 0));

        p[1][x] = -100;
        p[1][y] = -100;
        p[1][z] = -100;
        p[2][x] = +100;
        p[2][y] = -100;
        p[2][z] = -100;
        p[3][x] = +100;
        p[3][y] = -100;
        p[3][z] = +100;
        p[4][x] = -100;
        p[4][y] = -100;
        p[4][z] = +100;
        p[5][x] = -100;
        p[5][y] = +100;
        p[5][z] = -100;
        p[6][x] = +100;
        p[6][y] = +100;
        p[6][z] = -100;
        p[7][x] = +100;
        p[7][y] = +100;
        p[7][z] = +100;
        p[8][x] = -100;
        p[8][y] = +100;
        p[8][z] = +100;

        /*       8 - - - - - 7
                     / |         / |
                    5 - - - - - 6  |
                    |  |        |  |
                    |  4 - - - -|- 3
                    | /         | /
                    1 - - - - - 2
         */
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D gBuffer = (Graphics2D) g.create();

        gBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        gBuffer.setColor(Color.LIGHT_GRAY);
        gBuffer.drawLine((int) (p[4][x]) + 200, (int) (p[4][y]) + 200, (int) (p[6][x]) + 200, (int) (p[6][y]) + 200);
        gBuffer.drawLine((int) (p[2][x]) + 200, (int) (p[2][y]) + 200, (int) (p[8][x]) + 200, (int) (p[8][y]) + 200);
        gBuffer.drawLine((int) (p[1][x]) + 200, (int) (p[1][y]) + 200, (int) (p[7][x]) + 200, (int) (p[7][y]) + 200);
        gBuffer.drawLine((int) (p[5][x]) + 200, (int) (p[5][y]) + 200, (int) (p[3][x]) + 200, (int) (p[3][y]) + 200);

        gBuffer.setColor(Color.DARK_GRAY);
        gBuffer.fillPolygon(new int[]{
            (int) p[1][x] + 200,
            (int) p[2][x] + 200,
            (int) p[6][x] + 200,
            (int) p[5][x] + 200},
                new int[]{
                    (int) p[1][y] + 200,
                    (int) p[2][y] + 200,
                    (int) p[6][y] + 200,
                    (int) p[5][y] + 200
                }, 4);
        gBuffer.setColor(Color.CYAN);
        gBuffer.drawLine((int) (p[1][x]) + 200, (int) (p[1][y]) + 200, (int) (p[2][x]) + 200, (int) (p[2][y]) + 200);
        gBuffer.drawLine((int) (p[2][x]) + 200, (int) (p[2][y]) + 200, (int) (p[3][x]) + 200, (int) (p[3][y]) + 200);
        gBuffer.drawLine((int) (p[3][x]) + 200, (int) (p[3][y]) + 200, (int) (p[4][x]) + 200, (int) (p[4][y]) + 200);
        gBuffer.drawLine((int) (p[4][x]) + 200, (int) (p[4][y]) + 200, (int) (p[1][x]) + 200, (int) (p[1][y]) + 200);
        gBuffer.drawLine((int) (p[5][x]) + 200, (int) (p[5][y]) + 200, (int) (p[6][x]) + 200, (int) (p[6][y]) + 200);
        gBuffer.drawLine((int) (p[6][x]) + 200, (int) (p[6][y]) + 200, (int) (p[7][x]) + 200, (int) (p[7][y]) + 200);
        gBuffer.setColor(Color.RED);
        gBuffer.drawLine((int) (p[7][x]) + 200, (int) (p[7][y]) + 200, (int) (p[8][x]) + 200, (int) (p[8][y]) + 200);
        gBuffer.setColor(Color.CYAN);
        gBuffer.drawLine((int) (p[8][x]) + 200, (int) (p[8][y]) + 200, (int) (p[5][x]) + 200, (int) (p[5][y]) + 200);
        gBuffer.drawLine((int) (p[1][x]) + 200, (int) (p[1][y]) + 200, (int) (p[5][x]) + 200, (int) (p[5][y]) + 200);
        gBuffer.drawLine((int) (p[2][x]) + 200, (int) (p[2][y]) + 200, (int) (p[6][x]) + 200, (int) (p[6][y]) + 200);
        gBuffer.drawLine((int) (p[3][x]) + 200, (int) (p[3][y]) + 200, (int) (p[7][x]) + 200, (int) (p[7][y]) + 200);
        gBuffer.setColor(Color.BLUE);
        gBuffer.drawLine((int) (p[4][x]) + 200, (int) (p[4][y]) + 200, (int) (p[8][x]) + 200, (int) (p[8][y]) + 200);
        gBuffer.dispose();
    }

    protected class HorizontalRotationAction extends AbstractAction {

        private HorizontalRotation rotation;

        public HorizontalRotationAction(HorizontalRotation rotation) {
            this.rotation = rotation;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            horizontalRotation = rotation;
            rotate();
        }

    }

    protected class VerticalRotationAction extends AbstractAction {

        private VerticalRotation rotation;

        public VerticalRotationAction(VerticalRotation rotation) {
            this.rotation = rotation;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            verticalRotation = rotation;
            rotate();
        }

    }

}

Upvotes: 1

Related Questions