From 0717754bab342962a74a03a054e6fa577cb2bd9a Mon Sep 17 00:00:00 2001 From: jgsteplujr Date: Sun, 11 Jun 2017 17:07:15 -0400 Subject: [PATCH] Initial commit --- README.txt | 23 + src/ca/wtcs/ics3u/gc/Const.java | 43 ++ src/ca/wtcs/ics3u/gc/Geometry.java | 89 ++++ src/ca/wtcs/ics3u/gc/Geometry1D.java | 23 + src/ca/wtcs/ics3u/gc/GeometryConstructor.java | 28 + src/ca/wtcs/ics3u/gc/GraphView.java | 499 ++++++++++++++++++ src/ca/wtcs/ics3u/gc/Point.java | 152 ++++++ src/ca/wtcs/ics3u/gc/ViewPort.java | 239 +++++++++ 8 files changed, 1096 insertions(+) create mode 100755 README.txt create mode 100644 src/ca/wtcs/ics3u/gc/Const.java create mode 100644 src/ca/wtcs/ics3u/gc/Geometry.java create mode 100644 src/ca/wtcs/ics3u/gc/Geometry1D.java create mode 100644 src/ca/wtcs/ics3u/gc/GeometryConstructor.java create mode 100644 src/ca/wtcs/ics3u/gc/GraphView.java create mode 100644 src/ca/wtcs/ics3u/gc/Point.java create mode 100644 src/ca/wtcs/ics3u/gc/ViewPort.java diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..5c5a977 --- /dev/null +++ b/README.txt @@ -0,0 +1,23 @@ +Welcome to the Geometric Constructor + +You can use this program to model compass-and-straightedge geometric constructions +Read more about it here: https://en.wikipedia.org/wiki/Compass-and-straightedge_construction + +The program provides you with two fixed points to start the construction + +You can use the following keyboard commands: + +Select: Select or deselect entities by clicking on them +Delete: Delete selected entities + +0 / H : Hide selected entities or show all +1 / P : Construct a new point on the intersection of two existing geometries +2 / C : Construct a circle on two existing points. First select the center point, then select the radius +3 / L : Construct a line on two existing points by selecting them + ++ / - : Zoom in or out of view +Arrows: Pan the view + +Made for the ICS3U programming course, June 2017 + +Source code at https://github.com/yuliu2016/geometryconstructor \ No newline at end of file diff --git a/src/ca/wtcs/ics3u/gc/Const.java b/src/ca/wtcs/ics3u/gc/Const.java new file mode 100644 index 0000000..edc981a --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/Const.java @@ -0,0 +1,43 @@ +package ca.wtcs.ics3u.gc; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * Contains all the constants used in this application + * + * @author Yu Liu + */ + + +class Const { + + static final int DEFAULT_WIDTH = 600; + + static final int DEFAULT_HEIGHT = 450; + + static final int MINIMUM_WIDTH = 300; + + static final int MINIMUM_HEIGHT = 300; + + static final int PAN_RATE = 10; + + static final double ZOOM_RATE = 1.05; + + static final int FRAME_PERIOD = 15; + + static final int POINT_DIAMETER = 10; + + static final int POINT_OUTLINE = 14; + + static final double MIN_ZOOM_RATIO = 0.2; + + static final double MAX_ZOOM_RATIO = 5; + +} diff --git a/src/ca/wtcs/ics3u/gc/Geometry.java b/src/ca/wtcs/ics3u/gc/Geometry.java new file mode 100644 index 0000000..3428083 --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/Geometry.java @@ -0,0 +1,89 @@ +package ca.wtcs.ics3u.gc; + + +import java.awt.*; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * Geometry is a top-level interface for + * all geometric entities used in this application. An + * object that implements this interface will be able to + * select/deselect , show/hide, and paint itself, and would + * also be able to add itself into a {@link GraphView} + * + * @author Yu Liu + * @see Geometry1D + */ + + +public interface Geometry { + + + /** + * Paints the geometry entity + * @param g the graphic context to be drawn on + * @param viewPort the viewport context to determine position + */ + + void paint (Graphics g, ViewPort viewPort); + + + /** + * Determines if a mouse position is hovering over the geometry + * @param mouseX the X position of the mouse + * @param mouseY the Y position of the mouse + * @param viewPort the viewport context to determine position + * @return if the mouse is over geometry + */ + + boolean isMouseOver(int mouseX, int mouseY, ViewPort viewPort); + + + /** + * Determines if the geometry is selected + * @return if the geometry is selected + */ + + boolean isSelected(); + + + /** + * Determines if the geometry is hidden + * @return if the geometry is hidden + */ + + boolean isHidden(); + + + /** + * Sets the hovered state of geometry + * @param hovered the hovered state of geometry + */ + + void setHovered(boolean hovered); + + + /** + * Sets the selected state of geometry + * @param selected the selected state of geometry + */ + + void setSelected(boolean selected); + + + /** + * Sets the hidden state of geometry + * @param hidden the hidden state of geometry + */ + + void setHidden(boolean hidden); + +} \ No newline at end of file diff --git a/src/ca/wtcs/ics3u/gc/Geometry1D.java b/src/ca/wtcs/ics3u/gc/Geometry1D.java new file mode 100644 index 0000000..16719db --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/Geometry1D.java @@ -0,0 +1,23 @@ +package ca.wtcs.ics3u.gc; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * Geometry1D is a currently empty interface that represent + * a 1 dimensional object. Useful later when a geometry can only be a point + * + * @author Yu Liu + * @see Geometry + */ + +interface Geometry1D + extends Geometry { + +} diff --git a/src/ca/wtcs/ics3u/gc/GeometryConstructor.java b/src/ca/wtcs/ics3u/gc/GeometryConstructor.java new file mode 100644 index 0000000..5ed8dfa --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/GeometryConstructor.java @@ -0,0 +1,28 @@ +package ca.wtcs.ics3u.gc; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * Contains the wrapper main method to initiate a GraphView + * + * @author Yu Liu + * @see GraphView + */ + + +public class GeometryConstructor { + + public static void main(String[] args) { + + new GraphView(); + + } + +} diff --git a/src/ca/wtcs/ics3u/gc/GraphView.java b/src/ca/wtcs/ics3u/gc/GraphView.java new file mode 100644 index 0000000..1291c50 --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/GraphView.java @@ -0,0 +1,499 @@ +package ca.wtcs.ics3u.gc; + + +import java.awt.*; +import java.awt.event.*; +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * A GraphView is a frame component designed to provide a geometric construction tool. + *

+ * This component may be placed anywhere that allows a java.awt.Frame, + * provided it gets a minimum size specified in {@link Const}; + * or it can be created as standalone by instantiating this class. + * The component can be resized and would update accordingly, + * including a new default scale. See {@link ViewPort} for more details. + * This component receives events from its window, component, keyboard, and mouse; + * it is not tested how it will respond to these events when it is used in another container. + *

+ *

+ * This class keeps track of objects relating to the viewport, + * values relating to the mouse and key events, and a list of objects that implement + * {@link Geometry} to be created, displayed, and modified by the user through + * interations with this component + * The class is declared final because much of the code in this design + * are not reusable for extensions + *

+ * + * @author Yu Liu + * @see Const + * @see ViewPort + * @see Geometry + */ + + +public final class GraphView + extends Frame + implements KeyListener, MouseListener, MouseMotionListener { + + + /** + * The viewport of the component, updated when view changes with resize, zoom, or pan + */ + + private ViewPort viewPort = new ViewPort(); + + /** + * The list of geometric objects that have been fixed or constructed + */ + + private LinkedList entities = new LinkedList<>(); + + /** + * The X coordinate of the mouse(if mouse is inside the component) + */ + + private int mouseX; + + /** + * The Y coordinate of the mouse(if mouse is inside the component) + */ + + private int mouseY; + + + /** + * The current tool selected with the keyboard
+ * tool = 0 : Selection tool
+ * tool = 1 : Point tool
+ */ + + private int tool; + + + /** + * Initializes the component and start the timer + *

+ * Calls super's constructor with title
+ * Assigns window, component, key, mouse, and mouse motion event listeners to this component
+ * Sets the default and minimum bounds of the window
+ * Reset the viewPort
+ * Inserts the default point entities
+ * Starts the update timer
+ * Sets window to visible
+ *

+ */ + + GraphView() { + + super("Geometry Constructor"); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + dispose(); + } + }); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + viewPort.resize(getWidth(), getHeight()); + viewPort.setDefaultScale(getWidth() / 8); + } + }); + + addKeyListener(this); + addMouseListener(this); + addMouseMotionListener(this); + + setBounds(100, 100, Const.DEFAULT_WIDTH, Const.DEFAULT_HEIGHT); + setMinimumSize(new Dimension(Const.MINIMUM_WIDTH, Const.MINIMUM_HEIGHT)); + + viewPort.resetView(); + + entities.clear(); + + entities.add(new Point(0.5, 0)); + entities.add(new Point(-0.5, 0)); + + Timer timer = new Timer(true); + + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + viewPort.update(); + repaint(); + } + }, 0, Const.FRAME_PERIOD); + + setVisible(true); + + } + + + /** + * Gets the tool label + * + * @return The label to be displayed of the current tool + */ + + private String getToolLabel() { + + switch (tool) { + case 1: + return "Point Tool"; + default: + return "Selection Tool"; + } + + } + + + /** + * Gets the view label + * + * @return The label to be displayed of the current view, + * consisting of the width and height of the screen + * as well as the zoom ratio + */ + + private String getViewLabel() { + return getWidth() + " × " + getHeight() + " : " + (int) (viewPort.ratio() * 100) + "%"; + } + + + /** + * Counts the number of entities selected and hidden + * + * @return The label to be displayed of the entities selected and hidden + */ + + private String getNumericalLabel() { + + int selected = 0, hidden = 0; + + for (Geometry entity : entities) { + if (entity.isSelected()) + selected++; + if (entity.isHidden()) + hidden++; + } + + return selected + " Selected, " + hidden + " Hidden"; + } + + + /** + * Determines if the mouse is hovering on any entities + * + * @return if the mouse is hovering on any entities + */ + + private boolean mouseIsOverEntities() { + + for (Geometry entity : entities) + if (entity.isMouseOver(mouseX, mouseY, viewPort)) + return true; + + return false; + } + + + /** + * Determines if any entities are selected + * + * @return if any entities are selected + */ + + private boolean hasSelection() { + + for (Geometry entity : entities) + if (entity.isSelected()) + return true; + + return false; + } + + + /** + * Executes the select action + * + */ + + private void selectEntities() { + + boolean over = mouseIsOverEntities(); + + for (Geometry entity : entities) + entity.setSelected(over && entity.isSelected() ^ entity.isMouseOver(mouseX, mouseY, viewPort)); + + } + + + /** + * Inserts a point by computing the absolute position from the viewport + */ + + private void insertPoint() { + + if (!mouseIsOverEntities()) + entities.add(new Point(viewPort.computeFromX(mouseX), viewPort.computeFromY(mouseY))); + + } + + + /** + * Creates a list that include all item selected to be removed, + * then subtract it from the {@link GraphView#entities} list + */ + + private void deleteSelected() { + + LinkedList toBeRemoved = new LinkedList<>(); + + for (Geometry entity : entities) + if (entity.isSelected()) + toBeRemoved.add(entity); + + entities.removeAll(toBeRemoved); + + } + + + /** + * Executes the hide action + *
    + *
  • If no selection is made, show everything and select the entities that are unhidden
  • + *
  • If some selection is made, set selected to hidden
  • + *
+ */ + + private void hideSelectedOrShowAll() { + + boolean selected = hasSelection(); + + for (Geometry entity : entities) { + + if (selected) { + if (entity.isSelected()) { + entity.setHidden(true); + } + entity.setSelected(false); + } else { + + if (entity.isHidden()) { + entity.setSelected(true); + } + entity.setHidden(false); + } + } + } + + + /** + * Receives the event when user presses a key, + * used for pan and zoom where the program + * continues as long as the key is held down + * + * @param e the event that triggered this method + */ + + @Override + public void keyPressed(KeyEvent e) { + + int key = e.getKeyCode(); + + switch (key) { + + case KeyEvent.VK_UP: + viewPort.setPan(0, -Const.PAN_RATE); + break; + case KeyEvent.VK_DOWN: + viewPort.setPan(0, Const.PAN_RATE); + break; + case KeyEvent.VK_LEFT: + viewPort.setPan(-Const.PAN_RATE, 0); + break; + case KeyEvent.VK_RIGHT: + viewPort.setPan(Const.PAN_RATE, 0); + break; + + case KeyEvent.VK_EQUALS: + viewPort.setZoom(Const.ZOOM_RATE); + break; + case KeyEvent.VK_MINUS: + viewPort.setZoom(1 / Const.ZOOM_RATE); + break; + + default: + + } + } + + + /** + * Receives the event when user releases a key, + * used to release + * the continuous functions and trigger tool changes + * + * @param e the event that triggered this method + */ + + @Override + public void keyReleased(KeyEvent e) { + + int key = e.getKeyCode(); + + switch (key) { + + case KeyEvent.VK_UP: + case KeyEvent.VK_DOWN: + case KeyEvent.VK_RIGHT: + case KeyEvent.VK_LEFT: + viewPort.setPan(0, 0); + break; + + case KeyEvent.VK_EQUALS: + case KeyEvent.VK_MINUS: + viewPort.setZoom(1); + break; + + case KeyEvent.VK_BACK_SPACE: + deleteSelected(); + break; + + case KeyEvent.VK_0: + viewPort.resetView(); + break; + case KeyEvent.VK_H: + hideSelectedOrShowAll(); + break; + + case KeyEvent.VK_S: + tool = 0; + break; + case KeyEvent.VK_P: + tool = 1; + break; + + default: + } + } + + + @Override + public void keyTyped(KeyEvent e) { + } + + + @Override + public void mouseClicked(MouseEvent e) { + + } + + + @Override + public void mouseEntered(MouseEvent e) { + + } + + + @Override + public void mouseExited(MouseEvent e) { + + } + + + /** + * Receives the event when user presses the mouse, + * used to perform selections. + * Sends the event to the appropriate methods depending on the tool + * + * @param e the event that triggered this method + */ + + @Override + public void mousePressed(MouseEvent e) { + + switch (tool) { + case 1: + insertPoint(); + default: + selectEntities(); + } + } + + + @Override + public void mouseReleased(MouseEvent e) { + + } + + + @Override + public void mouseDragged(MouseEvent e) { + + } + + + /** + * Receives the event when user moves the mouse. + * Sets the class variables mouseX and mouseY + * to be further processed. + * + * @param e the event that triggered this method + */ + + @Override + public void mouseMoved(MouseEvent e) { + + mouseX = e.getX(); + mouseY = e.getY(); + + } + + + /** + * Paints each of the object by calling its paint method, + * along with the Graphics object and the viewport. + * Then paints the labels on the bottom-left corner + * + * @param g the graphic context to be drawn on + */ + + + @Override + public void paint(Graphics g) { + + for (Geometry entity : entities) { + entity.setHovered(entity.isMouseOver(mouseX, mouseY, viewPort)); + entity.paint(g, viewPort); + } + + g.setColor(Color.BLUE); + + g.drawString(getToolLabel(), 10, getHeight() - 50); + + g.setColor(Color.GRAY); + + g.drawString(getViewLabel(), 10, getHeight() - 30); + + g.drawString(getNumericalLabel(), 10, getHeight() - 10); + } +} diff --git a/src/ca/wtcs/ics3u/gc/Point.java b/src/ca/wtcs/ics3u/gc/Point.java new file mode 100644 index 0000000..aceab28 --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/Point.java @@ -0,0 +1,152 @@ +package ca.wtcs.ics3u.gc; + + +import java.awt.*; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * A Point represents a point on the plane that has a x and y coordinate. + * + * In the future it will be changed into more dynamic class + * + * @author Yu Liu + * @see Geometry + * @see Geometry1D + */ + + +public class Point + implements Geometry1D { + + + /** + * The coordinates of the point + */ + + private double x, y; + + + /** + * The states of the point + */ + + private boolean hovered, selected, hidden; + + + /** + * Initializes a point with coordinates + * @param x the X absolute coordinate + * @param y the Y absolute coordinate + */ + + Point(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * Paints the point according to size specified in {@link Const} + * and position computed from the viewport. + * Its colour depends on if it is selected/hovered. + * If hovered paint an extra ring outside the point + * @param g the graphic context to be drawn on + * @param viewPort the viewport context to determine position + */ + + @Override + public void paint(Graphics g, ViewPort viewPort) { + + if (!hidden) { + + int vx = viewPort.computeX(x) - Const.POINT_DIAMETER / 2; + int vy = viewPort.computeY(y) - Const.POINT_DIAMETER / 2; + + g.setColor(hovered || selected ? Color.BLUE : Color.GRAY); + + g.fillOval(vx, vy, Const.POINT_DIAMETER, Const.POINT_DIAMETER); + + if (hovered) + g.drawOval(vx - (Const.POINT_OUTLINE - Const.POINT_DIAMETER) / 2, + vy - (Const.POINT_OUTLINE - Const.POINT_DIAMETER) / 2, + Const.POINT_OUTLINE, Const.POINT_OUTLINE); + } + } + + + /** + * Determines if a mouse position is hovering over the point + * @param mouseX the X position of the mouse + * @param mouseY the Y position of the mouse + * @param viewPort the viewport context to determine position + * @return if the mouse is over the point + */ + + @Override + public boolean isMouseOver(int mouseX, int mouseY, ViewPort viewPort) { + return Math.sqrt(Math.pow(mouseX - viewPort.computeX(x), 2) + Math.pow(mouseY - viewPort.computeY(y), 2)) < Const.POINT_OUTLINE / 2; + } + + + /** + * Determines if the point is selected + * @return if the point is selected + */ + + @Override + public boolean isSelected() { + return selected; + } + + + /** + * Determines if the point is hidden + * @return if the point is hidden + */ + + @Override + public boolean isHidden() { + return hidden; + } + + + /** + * Sets the hovered state of point + * @param hovered the hovered state of point + */ + + @Override + public void setHovered(boolean hovered) { + this.hovered = hovered; + } + + + /** + * Sets the selected state of point + * @param selected the selected state of point + */ + + @Override + public void setSelected(boolean selected) { + this.selected = selected; + } + + + /** + * Sets the hidden state of point + * @param hidden the hidden state of point + */ + + @Override + public void setHidden(boolean hidden) { + this.hidden = hidden; + } + +} \ No newline at end of file diff --git a/src/ca/wtcs/ics3u/gc/ViewPort.java b/src/ca/wtcs/ics3u/gc/ViewPort.java new file mode 100644 index 0000000..c9dedf1 --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/ViewPort.java @@ -0,0 +1,239 @@ +package ca.wtcs.ics3u.gc; + + +/* + * Created by Yu Liu on 2017-06-06 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +/** + * ViewPort is a position model of a window, + * and enables pan, zoom, and resize, + * while calculating the actual window coordinates from an an + * absolute position or vice versa. + * + * @author Yu Liu + * @see GraphView + */ + + +class ViewPort { + + + /** + * The position of the view at absolute (0, 0) + */ + + private double centerX, centerY; + + + /** + * The position of the window's center + */ + + private double windowCenterX, windowCenterY; + + + /** + * The rate of pan per update + */ + + private double panX, panY; + + + /** + * The default scale of the view + */ + + private double defaultScale; + + + /** + * The current scale of the view + */ + + private double scale; + + + /** + * The zoom ratio + */ + + private double zoom; + + + /** + * Whether the view should reset + */ + + private boolean viewShouldReset; + + + /** + * Computes the absolute position of a relative x value according to the view + * + * @param x relative x value + * @return the absolute x position on the window + */ + + int computeX(double x) { + return (int) (centerX + x * scale); + } + + + /** + * Computes the absolute position of a relative x value according to the view + * + * @param y relative x value + * @return the absolute y position on the window + */ + + int computeY(double y) { + return (int) (centerY + y * scale); + } + + + /** + * Computes the relative position of an absolute x value according the the view + * + * @param x absolute x value + * @return the relative x position in the model + */ + + double computeFromX(int x) { + return (x - centerX) / scale; + } + + + /** + * Computes the relative position of an absolute y value according the the view + * + * @param y absolute y value + * @return the relative y position in the model + */ + + double computeFromY(int y) { + return (y - centerY) / scale; + } + + + /** + * Computes the ratio of the scale to the default scale + * + * @return the ratio of the scale to the default scale + */ + + double ratio() { + return scale / defaultScale; + } + + + /** + * Resizes the size of the window + * + * @param width width of the window + * @param height height of the window + */ + + void resize(int width, int height) { + windowCenterX = width / 2.0; + windowCenterY = height / 2.0; + } + + + /** + * Resets the viewpoint to the center without zooming in or out + */ + + void resetView() { + viewShouldReset = true; + } + + + /** + * Sets the default scale of the viewport + * + * @param s the default scale of the viewport + */ + + void setDefaultScale(double s) { + defaultScale = s; + } + + + /** + * Sets the rate of pan when updating + * + * @param x change in x + * @param y change in y + */ + + void setPan(int x, int y) { + panX = x; + panY = y; + } + + + /** + * Sets the rate of zoom when updating + * + * @param zoomRate the rate of zoom increase or decrease + */ + + void setZoom(double zoomRate) { + zoom = zoomRate; + } + + + /** + * Updates the view according to various variables.
+ * Computes the new center using the zoom, then the pan.
+ * Keeps the scale in bounds, specified by {@link Const}.
+ * Resets everything if viewShouldReset is true.
+ */ + + void update() { + + if (zoom != 1) { + + scale = scale * zoom; + + centerX = windowCenterX + (centerX - windowCenterX) * zoom; + centerY = windowCenterY + (centerY - windowCenterY) * zoom; + + } + + if (scale < Const.MIN_ZOOM_RATIO * defaultScale) + scale = Const.MIN_ZOOM_RATIO * defaultScale; + + if (scale > Const.MAX_ZOOM_RATIO * defaultScale) + scale = Const.MAX_ZOOM_RATIO * defaultScale; + + + if (panX != 0 || panY != 0) { + + centerX += panX; + centerY += panY; + + } + + if (viewShouldReset) { + + scale = defaultScale; + + centerX = windowCenterX; + centerY = windowCenterY; + + panX = 0; + panY = 0; + + zoom = 1; + + viewShouldReset = false; + + } + } +} \ No newline at end of file