diff --git a/src/App.java b/src/App.java new file mode 100644 index 0000000..1053058 --- /dev/null +++ b/src/App.java @@ -0,0 +1,36 @@ +import javax.swing.*; + +class App { + + private static void initWindow() { + // create a window frame and set the title in the toolbar + JFrame window = new JFrame("Testing"); + // when we close the window, stop the app + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // create the jpanel to draw on. + // this also initializes the game loop + Screen screen = new Screen(); + // add the jpanel to the window + window.add(screen); + window.addKeyListener(screen); + window.addMouseListener(screen); + window.addMouseMotionListener(screen); + // don't allow the user to resize the window + window.setResizable(false); + // fit the window size around the components (just our jpanel). + // pack() should be called after setResizable() to avoid issues on some platforms + window.pack(); + // open window in the center of the screen + window.setLocationRelativeTo(null); + // display the window + window.setVisible(true); + } + + public static void main(String[] args) { + // invokeLater() is used here to prevent our graphics processing from + // blocking the GUI. https://stackoverflow.com/a/22534931/4655368 + // this is a lot of boilerplate code that you shouldn't be too concerned about. + // just know that when main runs it will call initWindow() once. + SwingUtilities.invokeLater(App::initWindow); + } +} diff --git a/src/Line.java b/src/Line.java new file mode 100644 index 0000000..0968350 --- /dev/null +++ b/src/Line.java @@ -0,0 +1,134 @@ +import java.awt.*; +import java.awt.image.BufferedImage; + +// handles line and line drawing +public class Line { + public Point2D point1; + public Point2D point2; + + // initializer variables + boolean is_initialised = false; + private Point2D realPoint1; private Point2D realPoint2; + private int dx; + private int dy; private int sDy; + private char iterator; + private Point2D xy; + int[] returnVal = new int[]{0,0}; + // drawing progress variables + private int iteratorVal; + private int D; + public Line(Point2D _point1, Point2D _point2){ + point1 = _point1; + point2 = _point2; + } + + /** + * Draws onto a given image + * @param img the image to draw onto + */ + public void draw(BufferedImage img){ + + if (!is_initialised){initialise();} + if (iterator == 'x'){ + for (int i = 0; i <= dx; i++){ + nextPix(); + try { + img.setRGB(returnVal[0], returnVal[1], Color.HSBtoRGB(1, 1, 1)); + } + catch (ArrayIndexOutOfBoundsException ref){ + System.out.println(returnVal[0] + " " + returnVal[1]); + } + } + } else { + for (int i = 0; i <= dy; i++){ + nextPix(); + try { + img.setRGB(returnVal[0], returnVal[1], Color.HSBtoRGB(.5f, 1, 1)); + } + catch (ArrayIndexOutOfBoundsException ref){ + System.out.println(returnVal[0] + " " + returnVal[1]); + } + } + } + /* + debug - draws the start and end of each line in white and green + img.setRGB(realPoint1.intX(), realPoint1.intY(), Color.blue.getRGB()); + img.setRGB(realPoint2.intX(), realPoint2.intY(), Color.green.getRGB()); + */ + + is_initialised = false; + } + + /** + * Performs the initial calculations required to draw the line + * Is automatically called whenever nextpix() or draw() are called when initialise() has not run. + */ + public void initialise(){ + if (point2.x > point1.x) { + realPoint1 = point1; + realPoint2 = point2; + } else { // if dx is less than zero, swap the points around + realPoint1 = point2; + realPoint2 = point1; + } + dx = realPoint2.intX() - realPoint1.intX(); // round all points to the nearest integer + + dy = realPoint2.intY() - realPoint1.intY(); // the line algorithm only works with integer arithmetic + sDy = (int) Math.signum(dy); dy = Math.abs(dy); + + xy = new Point2D(realPoint1.intX(),realPoint1.intY()); // starting point + + // check if dy is greater than or less than dx + if (dy < dx){ + iterator = 'x'; + D = (2*dy) - dx; + }else{ + iterator='y'; + D = (2*dx) - dy; + } + + iteratorVal = 0; + is_initialised = true; + } + + /** + * @return the x and y coordinate of the next pixel in the line. + */ + public int[] nextPix(){ + if(!is_initialised){initialise();} + //OPTIMIZATION - avoid casting. have a different point class that uses integers instead of float + returnVal[0] = xy.intX(); + returnVal[1] = xy.intY(); + if (iterator=='x' && iteratorVal < dx && iteratorVal != -1){ + if (D > 0) { + D += 2 * (dy - dx); + xy.y += sDy; + } else { + D += 2*dy; + } + xy.x += 1; // the line is always drawn left to right + } else if(iterator =='y' && iteratorVal < dy && iteratorVal != -1) { + if (D > 0) { + D += 2 * (dx-dy); + xy.x += 1; // the line is always drawn left to right + } else { + D += 2*dx; + } + xy.y += sDy; + + } + else if(iteratorVal != -1) { + iteratorVal = -1; + returnVal[0] = realPoint2.intX(); + returnVal[1] = realPoint2.intY(); + return returnVal; + } + else { + is_initialised = false; + return null; + } + iteratorVal += 1; + return returnVal; + } + } + diff --git a/src/Player.java b/src/Player.java new file mode 100644 index 0000000..d3c4d71 --- /dev/null +++ b/src/Player.java @@ -0,0 +1,62 @@ +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; + +public class Player { + + // image that represents the player's position on the board + private BufferedImage image; + // current position of the player on the board grid + private Point3D position; + private Point3D rotation; + + // called when the player is initialised + public Player() { + } + + // unused because the player should not be drawn + // I'm leaving it in as a hint to buffered Images + public void draw(Graphics g, ImageObserver observer) { + int width = 100; + int height = 100; + + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB ); + + for ( int py = 0; py < height; py++ ) { + for ( int px = 0; px < width; px++ ) { + // Set the pixel colour of the image n.b. x = cc, y = rc + img.setRGB(px, py, Color.BLACK.getRGB() ); + } + } + g.drawImage(img, 0, 0, observer); + } + + public void keyPressed(int key) { + if (key == KeyEvent.VK_UP) { + } + if (key == KeyEvent.VK_RIGHT) { + } + if (key == KeyEvent.VK_DOWN) { + } + if (key == KeyEvent.VK_LEFT) { + } + } + public void keyReleased(int key){ + + } + public void tick(Point2D mouseRel) { + // System.out.println(mouseRel.x + " " + mouseRel.y); + // gets called once every tick, before the repainting process happens. + } + + // returnValue Functions + public Point3D getPos() { + return position; + } + public Point3D getRot() { + return rotation; + } + +} \ No newline at end of file diff --git a/src/Point2D.java b/src/Point2D.java new file mode 100644 index 0000000..7561047 --- /dev/null +++ b/src/Point2D.java @@ -0,0 +1,21 @@ +import java.util.ArrayList; + +public class Point2D { + public double x; + public double y; + public Point2D(double _x, double _y){ + x = _x; + y = _y; + } + public void set(double _x, double _y){ + x = _x; + y = _y; + } + public double[] get(){ + return new double[]{x, y}; + } + + public int intX(){return (int)Math.round(x);} + public int intY(){return (int)Math.round(y);} + +} diff --git a/src/Point3D.java b/src/Point3D.java new file mode 100644 index 0000000..7aa557d --- /dev/null +++ b/src/Point3D.java @@ -0,0 +1,19 @@ +public class Point3D { + private double x; + private double y; + private double z; + + public Point3D(double _x, double _y, double _z){ + x = _x; + y = _y; + z = _z; + } + public void set(double[] _new){ + x = _new[0]; + y = _new[1]; + z = _new[2]; + } + public double[] get(){ + return new double[]{x, y, z}; + } +} diff --git a/src/Screen.java b/src/Screen.java new file mode 100644 index 0000000..fb2b209 --- /dev/null +++ b/src/Screen.java @@ -0,0 +1,177 @@ +import java.awt.*; +import java.awt.event.*; +import java.awt.image.BufferedImage; +import javax.swing.*; + +public class Screen extends JPanel implements ActionListener, KeyListener, MouseListener, MouseMotionListener { + // __working variables__ + + // mouse + private boolean captured = true; + private final Point2D mouseRel = new Point2D(0d,0d); + //private final Cursor invisibleCursor; + + // testing + private Line line = new Line(new Point2D(200, 200), + new Point2D(1, 1)); + private Triangle Triangle = new Triangle(new Point2D(200, 200), new Point2D(1, 1), new Point2D(400,200)); + double ang = 0; + // __config variables__ + // controls the delay between each tick in ms + private final int DELAY = 25; + // controls the size of the board + public static final int TILE_SIZE = 50; + public static final int ROWS = 12; + public static final int COLUMNS = 18; + // suppress serialization warning + private static final long serialVersionUID = 490905409104883233L; + + // keep a reference to the timer object that triggers actionPerformed() in + // case we need access to it in another method + private final Timer timer; + // objects that appear on the game board + private final Player player; + + public Screen() { + // set the game board size + setPreferredSize(new Dimension(TILE_SIZE * COLUMNS, TILE_SIZE * ROWS)); + // set the game board background color + setBackground(new Color(232, 232, 232)); + // hide the mouse cursor (https://stackoverflow.com/questions/191592/how-do-i-get-rid-of-the-mouse-cursor-in-full-screen-exclusive-mode) + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Point hotSpot = new Point(0,0); + BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT); + //invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor"); + //setCursor(invisibleCursor); + + // initialize the game state + player = new Player(); + + // this timer will call the actionPerformed() method every DELAY ms + timer = new Timer(DELAY, this); + timer.start(); + } + + @Override + public void actionPerformed(ActionEvent e) { + // this method is called by the timer every DELAY ms. + // runs the tick of the player before the board is redrawn + player.tick(get_mouse_rel()); + + // calling repaint() will trigger paintComponent() to run again, + // which will refresh/redraw the graphics. + repaint(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + // when calling g.drawImage() we can use "this" for the ImageObserver + // because Component implements the ImageObserver interface, and JPanel + // extends from Component. So "this" Board instance, as a Component, can + // react to imageUpdate() events triggered by g.drawImage() + + // draw graphics. + drawScreen(g); + // player.draw(g, this); + + // this smooths out animations on some systems + Toolkit.getDefaultToolkit().sync(); + } + private void drawScreen(Graphics g) { + BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB ); + line.point2.x = 100*Math.sin(ang) + 200; + line.point2.y = 100*Math.cos(ang) + 200; + Triangle.point2.x = 100*Math.sin(ang+3) + 200; + Triangle.point2.y = 100*Math.cos(ang+3) + 200; + line.draw(img); + Triangle.draw(img); + ang += 0.02; + g.drawImage(img, 0, 0, this); + + } + + @Override + public void keyTyped(KeyEvent e) {} + + @Override + public void keyPressed(KeyEvent e) { + int key = e.getKeyCode(); + + if (key == KeyEvent.VK_ESCAPE){ + captured = false; + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + // pass key-press to the player, so they can handle it as well + player.keyPressed(key); + } + + @Override + public void keyReleased(KeyEvent e) {} + + // gets the relative position of the mouse since the last time this function was called + public Point2D get_mouse_rel(){ + Point2D rel; + // this doesn't work if the mouse isn't captured. If I need it to, I can + // probably make it, but I don't know what the functionality looks like. + if (captured) { + rel = new Point2D(MouseInfo.getPointerInfo().getLocation().x - getLocationOnScreen().x - getSize().width / 2d + mouseRel.x, + MouseInfo.getPointerInfo().getLocation().y - getLocationOnScreen().y - getSize().height / 2d + mouseRel.y); + mouseRel.set(0, 0); + // set the position of the mouse back to (0,0) + try { + Robot robot = new Robot(); + robot.mouseMove(getLocationOnScreen().x + getSize().width / 2, + getLocationOnScreen().y + getSize().height / 2); + } catch (AWTException ex) { + ex.printStackTrace(); + } + } + else{ + rel = new Point2D(0,0); + } + return rel; + } + @Override + public void mouseClicked(MouseEvent mouseEvent) { + if (mouseEvent.getButton() == MouseEvent.BUTTON1){ + captured = true; + //setCursor(invisibleCursor); + } + } + + @Override + public void mousePressed(MouseEvent mouseEvent) {} + + @Override + public void mouseReleased(MouseEvent mouseEvent) {} + + @Override + public void mouseEntered(MouseEvent mouseEvent) {} + + @Override + public void mouseExited(MouseEvent mouseEvent) { + if (captured) { + // add current mouse relative location to mouseRel so mouse movements are not lost + mouseRel.x += mouseEvent.getX() - getSize().width / 2d; + mouseRel.y += mouseEvent.getY() - getSize().height / 2d; + + // System.out.println(mouseEvent.getX() + " " + mouseEvent.getY()); + // set the position of the mouse back to (0,0) + try { + Robot robot = new Robot(); + robot.mouseMove(getLocationOnScreen().x + getSize().width / 2, + getLocationOnScreen().y + getSize().height / 2); + } catch (AWTException ex) { + ex.printStackTrace(); + } + } + } + + @Override + public void mouseDragged(MouseEvent mouseEvent) {} + + @Override + public void mouseMoved(MouseEvent mouseEvent) { + } +} \ No newline at end of file diff --git a/src/Triangle.java b/src/Triangle.java new file mode 100644 index 0000000..5356e41 --- /dev/null +++ b/src/Triangle.java @@ -0,0 +1,100 @@ +import java.awt.*; +import java.awt.image.BufferedImage; + +public class Triangle{ + public Point2D point1; + public Point2D point2; + public Point2D point3; + + private Line LineLong; + private Line LineA; + private Line LineB; + // initialisation variables + private boolean is_initialised = false; + private Point2D min; + private Point2D max; + // progress variables + + + public Triangle(Point2D _pA, Point2D _pB, Point2D _pC){ + point1 = _pA; + point2 = _pB; + point3 = _pC; + } + public void draw(BufferedImage img){ + if (!is_initialised){initialise();} + int[] point1 = new int[]{0,0}; int[] point2 = new int[]{0, 0}; + int lastX = 0; + int lastXLong = 0; + char currentLine = 'A'; + LineLong.initialise(); + LineA.initialise(); + LineB.initialise(); + LineLong.draw(img); + LineA.draw(img); + LineB.draw(img); + for(int x = min.intX(); x <= max.intX(); x += 1) { + while(lastXLong == point1[0]) { + point1 = LineLong.nextPix(); + } + while(lastX == point2[0]) { + if (currentLine == 'A') { + point2 = LineA.nextPix(); + if (point2 == null) { + currentLine = 'B'; + point2 = LineB.nextPix(); + } + } + else { + point2 = LineB.nextPix(); + } + } + lastXLong = point1[0]; + lastX = point2[0]; + if(point1[1] < point2[1]) { + for (int y = point1[1]; y <= point2[1]; y += 1) {img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); + }} else { + for (int y = point2[1]; y <= point1[1]; y += 1) {img.setRGB(x, y, Color.HSBtoRGB(.5f, 1, 1)); + }} + } + is_initialised = false; + } + public void initialise(){ + min = new Point2D(Math.min(point1.x, Math.min(point2.x, point3.x)), Math.min(point1.y, Math.min(point2.y, point3.y))); + max = new Point2D(Math.max(point1.x, Math.max(point2.x, point3.x)), Math.max(point1.y, Math.max(point2.y, point3.y))); + // woo horrible IFs mess. + // if there are no vertical lines, then we need to find which points touch the edges. + Point2D realPointA; + Point2D realPointB; + Point2D realPointC; + if (point1.x == min.x) { realPointA = point1; + if (point2.x == max.x){ realPointB = point2; + realPointC = point3; + } else { + realPointB = point3; + realPointC = point2; + } + } + else if (point2.x == min.x) { realPointA = point2; + if (point1.x == max.x) { realPointB = point1; + realPointC = point3; + } else { realPointB = point3; + realPointC = point1; + } + } + else { + realPointA = point3; + if (point1.x == max.x) { realPointB = point1; + realPointC = point2; + } else { realPointB = point2; + realPointC = point1; + } + } + // assign points to lines + LineLong = new Line(realPointA, realPointB); + LineA = new Line(realPointA, realPointC); + LineB = new Line(realPointC, realPointB); + + is_initialised = true; + } +}