From b6b5f78247775eaa7911ab35ef73101e2e7bba38 Mon Sep 17 00:00:00 2001 From: jgsteplujr Date: Sun, 18 Jun 2017 21:10:09 -0400 Subject: [PATCH] Abstract Class, Circle Class, Dimensioning System --- src/ca/wtcs/ics3u/gc/BufferedCanvas.java | 8 +- src/ca/wtcs/ics3u/gc/Circle.java | 102 +++++++++++ src/ca/wtcs/ics3u/gc/Geometry.java | 70 ++++++-- src/ca/wtcs/ics3u/gc/Geometry1D.java | 25 --- src/ca/wtcs/ics3u/gc/GeometryConstructor.java | 24 +-- src/ca/wtcs/ics3u/gc/GraphView.java | 163 ++++++++++++------ src/ca/wtcs/ics3u/gc/Point.java | 93 ++-------- src/ca/wtcs/ics3u/gc/ViewPort.java | 18 +- 8 files changed, 314 insertions(+), 189 deletions(-) create mode 100644 src/ca/wtcs/ics3u/gc/Circle.java delete mode 100644 src/ca/wtcs/ics3u/gc/Geometry1D.java diff --git a/src/ca/wtcs/ics3u/gc/BufferedCanvas.java b/src/ca/wtcs/ics3u/gc/BufferedCanvas.java index efa2874..7d1913d 100644 --- a/src/ca/wtcs/ics3u/gc/BufferedCanvas.java +++ b/src/ca/wtcs/ics3u/gc/BufferedCanvas.java @@ -6,6 +6,10 @@ * ICS3U - Culminating Project - A compass-and-straightedge construction tool * * More information at https://github.com/yuliu2016/GeometryConstructor + * + * The code in this file is from an answer on Stack Overflow. + * It is not written by me, however I made some modifications. + * The source is found at https://stackoverflow.com/a/10508090 */ @@ -39,13 +43,15 @@ class BufferedCanvas extends Canvas { public void update(Graphics g) { Image bufferedImage = createImage(getWidth(), getHeight()); + Graphics bufferedGraphics = bufferedImage.getGraphics(); bufferedGraphics.setColor(getBackground()); + bufferedGraphics.fillRect(0, 0, getWidth(), getHeight()); - bufferedGraphics.setColor(getForeground()); paint(bufferedGraphics); + g.drawImage(bufferedImage, 0, 0, this); } diff --git a/src/ca/wtcs/ics3u/gc/Circle.java b/src/ca/wtcs/ics3u/gc/Circle.java new file mode 100644 index 0000000..3b215a2 --- /dev/null +++ b/src/ca/wtcs/ics3u/gc/Circle.java @@ -0,0 +1,102 @@ +package ca.wtcs.ics3u.gc; + + +/* + * Created by Yu Liu on 2017-06-15 + * ICS3U - Culminating Project - A compass-and-straightedge construction tool + * + * More information at https://github.com/yuliu2016/GeometryConstructor + */ + + +import java.awt.*; + + +/** + * A Circle is a {@link Geometry} + * object that represents a circle on a plane + * with the center at one point through another, + * with the distance between these points the radius + * of the circle. The position of a circle must be + * dynamically computed, meaning the two {@link Point} + * that defines the object must also have computed + * or fixed values. + */ + + +public class Circle + extends Geometry { + + /** + * The width of the circle's stroke when it's hovered + */ + + private static final int THICK_STROKE_WIDTH; + + + static { + THICK_STROKE_WIDTH = 4; + } + + /** + * The two points that defines the circle + */ + + private Point center, throughPoint; + + + /** + * + */ + + Circle(Point center, Point throughPoint) { + this.center = center; + this.throughPoint = throughPoint; + } + + + private double radius(ViewPort viewPort) { + return ViewPort.mag(viewPort.computeX(throughPoint.getX()) - viewPort.computeX(center.getX()), + viewPort.computeY(throughPoint.getY()) - viewPort.computeY(center.getY())); + } + + /** + * Paints the geometry entity + * + * @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 (!isHidden()) { + int cx, cy, r; + + cx = viewPort.computeX(center.getX()); + cy = viewPort.computeY(center.getY()); + r = (int) radius(viewPort); + + g.setColor(getColor()); + g.drawOval(cx - r, cy - r, r * 2, r * 2); + + } + } + + + /** + * 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 + */ + + @Override + public boolean isMouseOver(int mouseX, int mouseY, ViewPort viewPort) { + return Math.abs(radius(viewPort) - ViewPort.mag(mouseX - viewPort.computeX(center.getX()), + mouseY - viewPort.computeY(center.getY()))) < THICK_STROKE_WIDTH; + } + +} diff --git a/src/ca/wtcs/ics3u/gc/Geometry.java b/src/ca/wtcs/ics3u/gc/Geometry.java index 2d50718..a90e0ea 100644 --- a/src/ca/wtcs/ics3u/gc/Geometry.java +++ b/src/ca/wtcs/ics3u/gc/Geometry.java @@ -19,11 +19,31 @@ * also be able to add itself into a {@link GraphView} * * @author Yu Liu - * @see Geometry1D */ -public interface Geometry { +abstract class Geometry { + + + /** + * The selected state of the geometry + */ + + private boolean selected; + + + /** + * The hidden state of the geometry + */ + + private boolean hidden; + + + /** + * The hovered state of the geometry + */ + + private boolean hovered; /** @@ -33,7 +53,7 @@ public interface Geometry { * @param viewPort the viewport context to determine position */ - void paint(Graphics g, ViewPort viewPort); + abstract void paint(Graphics g, ViewPort viewPort); /** @@ -45,7 +65,7 @@ public interface Geometry { * @return if the mouse is over geometry */ - boolean isMouseOver(int mouseX, int mouseY, ViewPort viewPort); + abstract boolean isMouseOver(int mouseX, int mouseY, ViewPort viewPort); /** @@ -54,7 +74,9 @@ public interface Geometry { * @return if the geometry is selected */ - boolean getSelected(); + boolean isSelected() { + return selected; + } /** * Sets the selected state of geometry @@ -62,7 +84,9 @@ public interface Geometry { * @param selected the selected state of geometry */ - void setSelected(boolean selected); + void setSelected(boolean selected) { + this.selected = selected; + } /** * Determines if the geometry is hidden @@ -70,16 +94,29 @@ public interface Geometry { * @return if the geometry is hidden */ - boolean getHidden(); + boolean isHidden() { + return hidden; + } /** * Sets the hidden state of geometry * - * @param hidden the hidden state of geometry + * @param hidden the hidden state of the geometry */ - void setHidden(boolean hidden); + void setHidden(boolean hidden) { + this.hidden = hidden; + } + + /** + * Determines if the geometry is on mouseover + * + * @return if the geometry is hidden + */ + boolean isHovered() { + return hovered; + } /** * Sets the hovered state of geometry @@ -87,6 +124,19 @@ public interface Geometry { * @param hovered the hovered state of geometry */ - void setHovered(boolean hovered); + void setHovered(boolean hovered) { + this.hovered = hovered; + } + + /** + * Determines the color of the geometric object according to its state. + * Overriding paint method should call this method to determine color + * + * @return The unified colour of the object + */ + + Color getColor() { + return hovered || selected ? Color.BLUE : Color.BLACK; + } } \ No newline at end of file diff --git a/src/ca/wtcs/ics3u/gc/Geometry1D.java b/src/ca/wtcs/ics3u/gc/Geometry1D.java deleted file mode 100644 index 69127f1..0000000 --- a/src/ca/wtcs/ics3u/gc/Geometry1D.java +++ /dev/null @@ -1,25 +0,0 @@ -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 index f92ba2b..b4d8a3c 100644 --- a/src/ca/wtcs/ics3u/gc/GeometryConstructor.java +++ b/src/ca/wtcs/ics3u/gc/GeometryConstructor.java @@ -41,7 +41,7 @@ public class GeometryConstructor { static { - DEFAULT_DIMENSION = new Dimension(600, 450); + DEFAULT_DIMENSION = new Dimension(800, 600); TITLE = "Geometry Constructor"; } @@ -54,33 +54,33 @@ public class GeometryConstructor { public static void main(String[] args) { - Frame application = new Frame(TITLE); + Frame app = new Frame(TITLE); GraphView graphView = new GraphView(); - application.setSize(DEFAULT_DIMENSION); - application.setLayout(null); - application.setResizable(true); + app.setSize(DEFAULT_DIMENSION); + app.setLayout(null); + app.setResizable(true); - application.add(graphView); + app.add(graphView); - application.addWindowListener(new WindowAdapter() { + app.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { - application.dispose(); + app.dispose(); System.exit(0); } }); - application.addComponentListener(new ComponentAdapter() { + app.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { - Dimension newSize = application.getSize(); + Dimension newSize = app.getSize(); graphView.setSize(newSize.width, newSize.height); } }); - application.setMinimumSize(GraphView.MINIMUM_DIMENSION); - application.setVisible(true); + app.setMinimumSize(GraphView.MINIMUM_DIMENSION); + app.setVisible(true); } } diff --git a/src/ca/wtcs/ics3u/gc/GraphView.java b/src/ca/wtcs/ics3u/gc/GraphView.java index 66ee77f..69aa2e2 100644 --- a/src/ca/wtcs/ics3u/gc/GraphView.java +++ b/src/ca/wtcs/ics3u/gc/GraphView.java @@ -25,13 +25,12 @@ * 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 + * interactions with this component * The class is declared final because much of the code in this design * are not reusable for extensions *

@@ -90,23 +89,24 @@ public final class GraphView private ViewPort viewPort = new ViewPort(); + /** - * The list of geometric objects that have been fixed or constructed + * The list of all the points on the graph */ - private LinkedList entities = new LinkedList<>(); + private LinkedList points = new LinkedList<>(); /** - * The X coordinate of the mouse(if mouse is inside the component) + * The list of geometric objects that have been constructed */ - private int mouseX; + private LinkedList shapes = new LinkedList<>(); /** - * The Y coordinate of the mouse(if mouse is inside the component) + * The x and y coordinate of the mouse(if mouse is inside the component) */ - private int mouseY; + private int mouseX, mouseY; /** @@ -117,9 +117,21 @@ public final class GraphView private int tool; + + /** + * Indicates whether the component has reset the view for the first time + */ + private boolean viewHasReset; + /** + * Counts the selected and hidden entities + */ + + private int selected, hidden; + + /** * Initializes the component and start the timer *

@@ -135,7 +147,6 @@ public final class GraphView GraphView() { - addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { @@ -154,11 +165,20 @@ public void componentResized(ComponentEvent e) { setMinimumSize(MINIMUM_DIMENSION); - entities.clear(); - entities.add(new Point(0.5, 0)); - entities.add(new Point(-0.5, 0)); + Point p1 = new Point(0.5, 0); + Point p2 = new Point(-0.5, 0); + + points.add(p1); + points.add(p2); + + shapes.add(new Circle(p1, p2)); + shapes.add(new Circle(p2, p1)); + + selected = 0; + hidden = 0; Timer timer = new Timer(true); + timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { @@ -167,7 +187,8 @@ public void run() { } }, 0, FRAME_RATE); - setVisible(true); + requestFocus(); + requestFocusInWindow(); } @@ -183,6 +204,8 @@ private String getToolLabel() { switch (tool) { case 1: return "Point Tool"; + case 2: + return "Circle Tool"; default: return "Selection Tool"; } @@ -210,16 +233,6 @@ private String getViewLabel() { */ private String getNumericalLabel() { - - int selected = 0, hidden = 0; - - for (Geometry entity : entities) { - if (entity.getSelected()) - selected++; - if (entity.getHidden()) - hidden++; - } - return selected + " Selected, " + hidden + " Hidden"; } @@ -230,12 +243,16 @@ private String getNumericalLabel() { * @return if the mouse is hovering on any entities */ - private boolean mouseIsOverEntities() { + private boolean anyEntityHovered() { - for (Geometry entity : entities) + for (Geometry entity : shapes) if (entity.isMouseOver(mouseX, mouseY, viewPort)) return true; + for (Point point : points) + if (point.isMouseOver(mouseX, mouseY, viewPort)) + return true; + return false; } @@ -246,10 +263,14 @@ private boolean mouseIsOverEntities() { * @return if any entities are selected */ - private boolean hasSelection() { + private boolean anyEntitySelected() { - for (Geometry entity : entities) - if (entity.getSelected()) + for (Geometry entity : shapes) + if (entity.isSelected()) + return true; + + for (Point point : points) + if (point.isSelected()) return true; return false; @@ -267,11 +288,28 @@ private boolean hasSelection() { private void selectEntities() { - boolean over = mouseIsOverEntities(); + boolean over = anyEntityHovered(); + selected = 0; + + for (Point point : points) { + point.setSelected(over && point.isSelected() ^ point.isMouseOver(mouseX, mouseY, viewPort)); + selected += point.isSelected() ? 1 : 0; + } + + for (Geometry entity : shapes) { + entity.setSelected(over && entity.isSelected() ^ entity.isMouseOver(mouseX, mouseY, viewPort)); + selected += entity.isSelected() ? 1 : 0; + } + + + } - for (Geometry entity : entities) - entity.setSelected(over && entity.getSelected() ^ entity.isMouseOver(mouseX, mouseY, viewPort)); + /** + * Adds a fixed point onto the graph + */ + private void addFixedPoint(double x, double y) { + points.add(new Point(x, y)); } @@ -281,26 +319,26 @@ private void selectEntities() { private void insertPoint() { - if (!mouseIsOverEntities()) - entities.add(new Point(viewPort.computeFromX(mouseX), viewPort.computeFromY(mouseY))); + if (!anyEntityHovered()) + addFixedPoint(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 + * then subtract it from the {@link GraphView#shapes} list */ private void deleteSelected() { LinkedList toBeRemoved = new LinkedList<>(); - for (Geometry entity : entities) - if (entity.getSelected()) + for (Geometry entity : shapes) + if (entity.isSelected()) toBeRemoved.add(entity); - entities.removeAll(toBeRemoved); + shapes.removeAll(toBeRemoved); } @@ -315,23 +353,30 @@ private void deleteSelected() { private void hideSelectedOrShowAll() { - boolean selected = hasSelection(); + boolean anySelected = anyEntitySelected(); + boolean either; + hidden = 0; - for (Geometry entity : entities) { + for (Point point : points) { - if (selected) { - if (entity.getSelected()) { - entity.setHidden(true); - } - entity.setSelected(false); - } else { + either = point.isSelected() || point.isHidden(); - if (entity.getHidden()) { - entity.setSelected(true); - } - entity.setHidden(false); - } + point.setHidden(anySelected && either); + point.setSelected(!anySelected && either); + + hidden += anySelected && either ? 1 : 0; } + + for (Geometry entity : shapes) { + + either = entity.isSelected() || entity.isHidden(); + + entity.setHidden(anySelected && either); + entity.setSelected(!anySelected && either); + + hidden += anySelected && either ? 1 : 0; + } + } @@ -420,6 +465,8 @@ public void keyReleased(KeyEvent e) { case KeyEvent.VK_P: tool = 1; break; + case KeyEvent.VK_C: + tool = 2; default: } @@ -509,11 +556,23 @@ public void mouseMoved(MouseEvent e) { @Override public void paint(Graphics g) { - for (Geometry entity : entities) { + LinkedList paintLast = new LinkedList<>(); + + for (Geometry entity : shapes) { entity.setHovered(entity.isMouseOver(mouseX, mouseY, viewPort)); - entity.paint(g, viewPort); + if (entity.isSelected() || entity.isHovered()) paintLast.add(entity); + else entity.paint(g, viewPort); + } + + for (Point point : points) { + point.setHovered(point.isMouseOver(mouseX, mouseY, viewPort)); + if (point.isSelected() || point.isHovered()) paintLast.add(point); + point.paint(g, viewPort); } + for (Geometry entity : paintLast) + entity.paint(g, viewPort); + g.setColor(Color.BLUE); g.drawString(getToolLabel(), 10, getHeight() - 50); diff --git a/src/ca/wtcs/ics3u/gc/Point.java b/src/ca/wtcs/ics3u/gc/Point.java index a3371b5..1517009 100644 --- a/src/ca/wtcs/ics3u/gc/Point.java +++ b/src/ca/wtcs/ics3u/gc/Point.java @@ -19,12 +19,11 @@ * * @author Yu Liu * @see Geometry - * @see Geometry1D */ public class Point - implements Geometry1D { + extends Geometry { /** @@ -39,11 +38,11 @@ public class Point * is over the point */ - private static final int RANGE_RADIUS; + private static final int RANGE; static { RADIUS = 5; - RANGE_RADIUS = 8; + RANGE = 8; } /** @@ -52,14 +51,6 @@ public class Point private double x, y; - - /** - * The states of the point - */ - - private boolean hovered, selected, hidden; - - /** * Initializes a point with coordinates * @@ -72,8 +63,16 @@ public class Point this.y = y; } + double getX() { + return x; + } + + double getY() { + return y; + } + /** - * Paints the point according to size specified in RADIUS and RANGE_RADIUS + * Paints the point according to size specified in RADIUS and RANGE * and position computed from the viewport. * Its colour depends on if it is selected/hovered. * If hovered paint an extra ring outside the point @@ -85,23 +84,23 @@ public class Point @Override public void paint(Graphics g, ViewPort viewPort) { - if (!hidden) { + if (!isHidden()) { int vx = viewPort.computeX(x) - RADIUS; int vy = viewPort.computeY(y) - RADIUS; - g.setColor(hovered || selected ? Color.BLUE : Color.BLACK); + g.setColor(getColor()); g.fillOval(vx, vy, RADIUS * 2, RADIUS * 2); - if (hovered) - g.drawOval(vx - RANGE_RADIUS + RADIUS, vy - RANGE_RADIUS + RADIUS, RANGE_RADIUS * 2, RANGE_RADIUS * 2); + if (isHovered()) + g.drawOval(vx - RANGE + RADIUS, vy - RANGE + RADIUS, RANGE * 2, RANGE * 2); } } /** * Determines if a mouse position is hovering over the point - * (i.e. mouse is within distance of RANGE_RADIUS) + * (i.e. mouse is within distance of RANGE) * * @param mouseX the X position of the mouse * @param mouseY the Y position of the mouse @@ -111,63 +110,7 @@ public void paint(Graphics g, ViewPort viewPort) { @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)) < RANGE_RADIUS; - } - - - /** - * Determines if the point is selected - * - * @return if the point is selected - */ - - @Override - public boolean getSelected() { - return selected; - } - - /** - * Sets the selected state of point - * - * @param selected the selected state of point - */ - - @Override - public void setSelected(boolean selected) { - this.selected = selected; - } - - /** - * Determines if the point is hidden - * - * @return if the point is hidden - */ - - @Override - public boolean getHidden() { - return hidden; - } - - /** - * Sets the hidden state of point - * - * @param hidden the hidden state of point - */ - - @Override - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - - /** - * Sets the hovered state of point - * - * @param hovered the hovered state of point - */ - - @Override - public void setHovered(boolean hovered) { - this.hovered = hovered; + return ViewPort.mag(mouseX - viewPort.computeX(x), mouseY - viewPort.computeY(y)) < RANGE; } } \ No newline at end of file diff --git a/src/ca/wtcs/ics3u/gc/ViewPort.java b/src/ca/wtcs/ics3u/gc/ViewPort.java index dc39eb3..1ad707f 100644 --- a/src/ca/wtcs/ics3u/gc/ViewPort.java +++ b/src/ca/wtcs/ics3u/gc/ViewPort.java @@ -43,55 +43,45 @@ 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; + static double mag(double dx, double dy) { + return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + } /** * Computes the absolute position of a relative x value according to the view @@ -153,7 +143,7 @@ int computeY(double y) { /** - * Resizes the size of the window + * Resize the window * * @param width width of the window * @param height height of the window