diff --git a/src/main/java/com/github/creme332/controller/canvas/CanvasController.java b/src/main/java/com/github/creme332/controller/canvas/CanvasController.java index 37f00c0d..1acb1b21 100644 --- a/src/main/java/com/github/creme332/controller/canvas/CanvasController.java +++ b/src/main/java/com/github/creme332/controller/canvas/CanvasController.java @@ -35,6 +35,7 @@ import com.github.creme332.controller.canvas.drawing.DrawIrregularPolygon; import com.github.creme332.controller.canvas.drawing.DrawLine; import com.github.creme332.controller.canvas.drawing.DrawRegularPolygon; +import com.github.creme332.controller.canvas.transform.Translator; import com.github.creme332.model.AppState; import com.github.creme332.model.CanvasModel; import com.github.creme332.model.Mode; @@ -75,6 +76,9 @@ public CanvasController(AppState app, Canvas canvas) { drawControllers.add(new DrawRegularPolygon(app, canvas)); drawControllers.add(new DrawIrregularPolygon(app, canvas)); + // initialize other canvas sub-controllers + new Translator(app, canvas); + // when canvas is resized, update dimensions and reset zoom canvas.addComponentListener(new ComponentAdapter() { @Override diff --git a/src/main/java/com/github/creme332/controller/canvas/transform/AbstractTransformer.java b/src/main/java/com/github/creme332/controller/canvas/transform/AbstractTransformer.java new file mode 100644 index 00000000..ae9e4c38 --- /dev/null +++ b/src/main/java/com/github/creme332/controller/canvas/transform/AbstractTransformer.java @@ -0,0 +1,87 @@ +package com.github.creme332.controller.canvas.transform; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.List; + +import com.github.creme332.model.AppState; +import com.github.creme332.model.CanvasModel; +import com.github.creme332.model.Mode; +import com.github.creme332.model.ShapeWrapper; +import com.github.creme332.view.Canvas; + +public abstract class AbstractTransformer { + protected Canvas canvas; + protected CanvasModel canvasModel; + private AppState app; + + protected AbstractTransformer(AppState app, Canvas canvas) { + this.app = app; + this.canvas = canvas; + this.canvasModel = app.getCanvasModel(); + + canvas.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (!shouldDraw()) + return; + Point2D polyspaceMousePosition = canvasModel.toPolySpace(e.getPoint()); + int selectedShapeIndex = getSelectedShapeIndex(polyspaceMousePosition); + if (selectedShapeIndex < 0) + return; + handleShapeSelection(selectedShapeIndex); + } + }); + } + + /** + * + * @param polyspaceMousePosition Coordinate of point lying inside shape + * @return Index of first shape that contains polyspaceMousePosition. -1 if no + * such shape found. + */ + private int getSelectedShapeIndex(Point2D polyspaceMousePosition) { + List shapes = canvasModel.getShapeManager().getShapes(); + for (int i = 0; i < shapes.size(); i++) { + ShapeWrapper wrapper = shapes.get(i); + if (wrapper.getShape().contains(polyspaceMousePosition)) { + return i; + } + } + return -1; + } + + /** + * + * @return Current canvas mode + */ + public Mode getCanvasMode() { + return app.getMode(); + } + + /** + * This method only gets called when shouldDraw() returns true and mouse + * clicked. + * + * @param shapeIndex A valid index representing selected shape + */ + public abstract void handleShapeSelection(int shapeIndex); + + /** + * Determines whether current controller should be allowed to handle event or + * not. + * + * @return + */ + public abstract boolean shouldDraw(); + + /** + * Reset current controller to its initial state. For example, this method will + * be invoked when mode changes while drawing was ongoing. + */ + public void disposePreview() { + // delete any preview shape + canvasModel.getShapeManager().setShapePreview(null); + } +} diff --git a/src/main/java/com/github/creme332/controller/canvas/transform/Translator.java b/src/main/java/com/github/creme332/controller/canvas/transform/Translator.java new file mode 100644 index 00000000..4538f863 --- /dev/null +++ b/src/main/java/com/github/creme332/controller/canvas/transform/Translator.java @@ -0,0 +1,95 @@ +package com.github.creme332.controller.canvas.transform; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.github.creme332.model.AppState; +import com.github.creme332.model.Mode; +import com.github.creme332.model.ShapeWrapper; +import com.github.creme332.view.Canvas; + +public class Translator extends AbstractTransformer { + + public Translator(AppState app, Canvas canvas) { + super(app, canvas); + } + + @Override + public void handleShapeSelection(int shapeIndex) { + /** + * A copy of the shape selected + */ + final ShapeWrapper selectedWrapperCopy = canvasModel.getShapeManager().getShapes().get(shapeIndex); + + // request user for translation vector + final Point2D translationVector = requestTranslationVector(); + + // apply transformation on shape + final AffineTransform transform = new AffineTransform(); + transform.translate(translationVector.getX(), translationVector.getY()); + final Shape transformedShape = transform.createTransformedShape(selectedWrapperCopy.getShape()); + + // save transformed shape + selectedWrapperCopy.setShape(transformedShape); + canvasModel.getShapeManager().editShape(shapeIndex, selectedWrapperCopy); + + // translate plotted points + final List originalPlottedPoints = selectedWrapperCopy.getPlottedPoints(); + for (int i = 0; i < originalPlottedPoints.size(); i++) { + Point2D oldPoint = originalPlottedPoints.get(i); + originalPlottedPoints.set(i, + new Point2D.Double(oldPoint.getX() + translationVector.getX(), + oldPoint.getY() + translationVector.getY())); + } + + // repaint canvas + canvas.repaint(); + } + + @Override + public boolean shouldDraw() { + return getCanvasMode() == Mode.TRANSLATION; + } + + /** + * Asks user to enter the radii for the ellipse. If input values are invalid + * or if the operation is cancelled, null is returned. + * + * @return array with radii [rx, ry] + */ + private Point2D requestTranslationVector() { + final Point2D zeroVector = new Point2D.Double(0, 0); + + JTextField rxField = new JTextField(5); + JTextField ryField = new JTextField(5); + JPanel panel = new JPanel(); + panel.add(new JLabel("X:")); + panel.add(rxField); + panel.add(new JLabel("Y:")); + panel.add(ryField); + + int result = JOptionPane.showConfirmDialog(null, panel, "Enter translation vector", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.PLAIN_MESSAGE); + + // Request focus again otherwise keyboard shortcuts will not work + canvas.getTopLevelAncestor().requestFocus(); + + if (result == JOptionPane.OK_OPTION) { + try { + return new Point2D.Double(Integer.parseInt(rxField.getText()), Integer.parseInt(ryField.getText())); + } catch (NumberFormatException e) { + return zeroVector; + } + } + return zeroVector; + } + +}