Skip to content

Commit

Permalink
Merge pull request #201 from Divyeshhhh/reflectionmode
Browse files Browse the repository at this point in the history
Implement reflection mode
  • Loading branch information
creme332 authored Aug 7, 2024
2 parents 542d28d + 8a64086 commit 8521e4f
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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.Reflector;
import com.github.creme332.controller.canvas.transform.Rotator;
import com.github.creme332.controller.canvas.transform.Scaler;
import com.github.creme332.controller.canvas.transform.Shearer;
Expand Down Expand Up @@ -85,6 +86,7 @@ public CanvasController(AppState app, Canvas canvas) {
new Rotator(app, canvas);
new Scaler(app, canvas);
new Shearer(app, canvas);
new Reflector(app, canvas);

// when canvas is resized, update dimensions and reset zoom
canvas.addComponentListener(new ComponentAdapter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.github.creme332.controller.canvas.transform;

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;

/**
* Controller responsible for reflection mode.
*/
public class Reflector extends AbstractTransformer {

public Reflector(AppState app, Canvas canvas) {
super(app, canvas);
}

@Override
public void handleShapeSelection(int shapeIndex) {
// A copy of the shape selected
final ShapeWrapper selectedWrapperCopy = canvasModel.getShapeManager().getShapeByIndex(shapeIndex);

// Request user for line of reflection (gradient and y-intercept)
final double[] data = requestReflectionLine();

if (data.length != 2) {
return; // Cancel operation if user input is invalid
}

double gradient = data[0];
double yIntercept = data[1];

// Reflect the shape using the gradient and y-intercept
selectedWrapperCopy.reflect(gradient, yIntercept);

// Replace old shape with the new one
canvasModel.getShapeManager().editShape(shapeIndex, selectedWrapperCopy);

// Repaint canvas
canvas.repaint();
}

@Override
public boolean shouldDraw() {
return getCanvasMode() == Mode.REFLECT_ABOUT_LINE;
}

/**
* Asks user to enter the gradient and y-intercept of the line of reflection.
* If input values are invalid or if the operation is canceled, null is
* returned.
*
* @return array with gradient and y-intercept [m, b]
*/
private double[] requestReflectionLine() {
JTextField gradientField = new JTextField(5);
JTextField yInterceptField = new JTextField(5);
JPanel panel = new JPanel();
panel.add(new JLabel("Gradient (m):"));
panel.add(gradientField);
panel.add(new JLabel("Y-Intercept (b):"));
panel.add(yInterceptField);

int result = JOptionPane.showConfirmDialog(canvas, panel, "Enter Line of Reflection",
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 {
double m = Double.parseDouble(gradientField.getText());
double b = Double.parseDouble(yInterceptField.getText());
return new double[] { m, b };
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "Invalid input. Please enter numeric values.");
}
}
return new double[] {};
}
}
97 changes: 97 additions & 0 deletions src/main/java/com/github/creme332/model/ShapeWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;

public class ShapeWrapper {
private Shape shape;
Expand Down Expand Up @@ -364,4 +365,100 @@ public void scale(Point2D scalingPoint, double sx, double sy) {
plottedPoints.set(i, new Point2D.Double((int) newX, (int) newY));
}
}

public void reflect(double gradient, double yIntercept) {
if (shape == null) {
return;
}

// Calculate reflection of each plotted point
for (int i = 0; i < plottedPoints.size(); i++) {
Point2D originalPoint = plottedPoints.get(i);
Point2D reflectedPoint = reflectPoint(originalPoint, gradient, yIntercept);
plottedPoints.set(i, reflectedPoint);
}

// Apply the transformation to the shape
if (shape instanceof Polygon) {
Polygon polygon = (Polygon) shape;
int[] xPoints = new int[polygon.npoints];
int[] yPoints = new int[polygon.npoints];

for (int i = 0; i < polygon.npoints; i++) {
Point2D originalPoint = new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]);
Point2D reflectedPoint = reflectPoint(originalPoint, gradient, yIntercept);
xPoints[i] = (int) reflectedPoint.getX();
yPoints[i] = (int) reflectedPoint.getY();
}

shape = new Polygon(xPoints, yPoints, polygon.npoints);
} else if (shape instanceof Path2D.Double) {
Path2D.Double path = (Path2D.Double) shape;
Path2D.Double newPath = new Path2D.Double();

// Reflect each point in the path
float[] coords = new float[6];
for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) {
int type = it.currentSegment(coords);
Point2D point = new Point2D.Double(coords[0], coords[1]);
Point2D reflectedPoint = reflectPoint(point, gradient, yIntercept);
coords[0] = (float) reflectedPoint.getX();
coords[1] = (float) reflectedPoint.getY();

switch (type) {
case PathIterator.SEG_MOVETO:
newPath.moveTo(coords[0], coords[1]);
break;
case PathIterator.SEG_LINETO:
newPath.lineTo(coords[0], coords[1]);
break;
case PathIterator.SEG_QUADTO:
newPath.quadTo(coords[0], coords[1], coords[2], coords[3]);
break;
case PathIterator.SEG_CUBICTO:
newPath.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
break;
case PathIterator.SEG_CLOSE:
newPath.closePath();
break;
}
}

shape = newPath;
}
}

/**
* Reflects a point across a line defined by a gradient and y-intercept.
*
* @param point the point to reflect
* @param gradient the slope of the reflection line
* @param yIntercept the y-intercept of the reflection line
* @return the reflected point
*/
private Point2D reflectPoint(Point2D point, double gradient, double yIntercept) {
double x = point.getX();
double y = point.getY();

if (gradient == 0) {
// When the line is horizontal (gradient is 0)
double reflectedY = 2 * yIntercept - y;
return new Point2D.Double(x, reflectedY);
}

// Calculate the slope and intercept of the perpendicular line
double perpendicularSlope = -1 / gradient;
double perpendicularIntercept = y - perpendicularSlope * x;

// Calculate intersection point of the line and the perpendicular
double intersectionX = (perpendicularIntercept - yIntercept) / (gradient - perpendicularSlope);
double intersectionY = gradient * intersectionX + yIntercept;

// Calculate reflected point
double reflectedX = 2 * intersectionX - x;
double reflectedY = 2 * intersectionY - y;

return new Point2D.Double(reflectedX, reflectedY);
}

}

0 comments on commit 8521e4f

Please sign in to comment.