From ed730e2d30b9b9ef1cdc5c83ad6574d12520c7fb Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:27:38 +0400 Subject: [PATCH 1/6] rename to ellipse calculator --- ...llipseAlgorithm.java => EllipseCalculator.java} | 4 ++-- .../tests/algorithms/EllipseAlgorithmTest.java | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/com/github/creme332/algorithms/{EllipseAlgorithm.java => EllipseCalculator.java} (97%) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java similarity index 97% rename from src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java rename to src/main/java/com/github/creme332/algorithms/EllipseCalculator.java index 6d2ce71e..43cf8f82 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java @@ -3,9 +3,9 @@ import java.util.ArrayList; import java.util.List; -public class EllipseAlgorithm { +public class EllipseCalculator { - private EllipseAlgorithm() { + private EllipseCalculator() { } diff --git a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java index 89b81f75..8ae64696 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import static org.junit.Assert.*; -import com.github.creme332.algorithms.EllipseAlgorithm; +import com.github.creme332.algorithms.EllipseCalculator; import com.github.creme332.tests.utils.TestHelper; import java.util.List; @@ -12,7 +12,7 @@ public class EllipseAlgorithmTest { @Test public void testEllipseCenteredAtOrigin() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); + List pixels = EllipseCalculator.drawEllipse(0, 0, 3, 2); int[][] expectedArray = { // Quadrant 1 @@ -53,14 +53,14 @@ public void testEllipseNotAtOrigin() { expectedArray[i][1] += centerY; } - List pixels = EllipseAlgorithm.drawEllipse(centerX, centerY, 8, 6); + List pixels = EllipseCalculator.drawEllipse(centerX, centerY, 8, 6); TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test public void testZeroRadii() { try { - EllipseAlgorithm.drawEllipse(10, 10, 0, 0); + EllipseCalculator.drawEllipse(10, 10, 0, 0); fail("Expected IllegalArgumentException for zero radii"); } catch (IllegalArgumentException e) { assertEquals("Radii must be positive values.", e.getMessage()); @@ -70,7 +70,7 @@ public void testZeroRadii() { @Test public void testNegativeRadii() { try { - EllipseAlgorithm.drawEllipse(10, 10, -5, -3); + EllipseCalculator.drawEllipse(10, 10, -5, -3); fail("Expected IllegalArgumentException for negative radii"); } catch (IllegalArgumentException e) { assertEquals("Radii must be positive values.", e.getMessage()); @@ -79,7 +79,7 @@ public void testNegativeRadii() { @Test public void testHorizontalEllipseAtOrigin() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); + List pixels = EllipseCalculator.drawEllipse(0, 0, 7, 3); int[][] expectedArray = { // Quadrant 1 @@ -97,7 +97,7 @@ public void testHorizontalEllipseAtOrigin() { @Test public void testVerticalEllipseAtOrigin() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); + List pixels = EllipseCalculator.drawEllipse(0, 0, 3, 7); int[][] expectedArray = { // Quadrant 1 From 3c909f502fff869e64f179887ed9408a7ad19857 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:00:07 +0400 Subject: [PATCH 2/6] refactor getOrderedPoints --- .../creme332/algorithms/CircleCalculator.java | 117 +++++++----------- 1 file changed, 43 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/CircleCalculator.java b/src/main/java/com/github/creme332/algorithms/CircleCalculator.java index 45fe0421..7ac40dc5 100644 --- a/src/main/java/com/github/creme332/algorithms/CircleCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/CircleCalculator.java @@ -20,9 +20,42 @@ public CircleCalculator() { cache = new HashMap<>(); } + /** + * Maps a point (x, y) in first octant to some other octant using 8-way symmetry + * of circle. + * + * @param x x-coordinate of a point in first octant. + * @param y y-coordinate of a point in the first octant. + * @param destinationOctant index of some octant (one-based index). + * @return x and y coordinates of point in new octant. + */ + public static int[] transformPoint(int x, int y, int destinationOctant) { + switch (destinationOctant) { + case 1: + return new int[] { x, y }; + case 2: + return new int[] { y, x }; + case 3: + return new int[] { y, -x }; + case 4: + return new int[] { x, -y }; + case 5: + return new int[] { -x, -y }; + case 6: + return new int[] { -y, -x }; + case 7: + return new int[] { -y, x }; + case 8: + return new int[] { -x, y }; + default: + break; + } + throw new IllegalArgumentException("Octant must be between 1 and 8."); + } + /** * Calculates coordinates of circle starting from top and moving clockwise. - * Points are ordered clockwise. + * Points are ordered clockwise. Duplicate points may occur at x==0 and x==y. * * @param centerX x-coordinate of circle center * @param centerY y-coordinate of circle center @@ -44,80 +77,16 @@ public int[][] getOrderedPoints(int centerX, int centerY, int radius) { List xPoints = new ArrayList<>(); List yPoints = new ArrayList<>(); - // add first octant - for (int i = 0; i < pixelList.size(); i++) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - xPoints.add(x); - yPoints.add(y); - } - - // add second octant - for (int i = pixelList.size() - 1; i > -1; i--) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == y) - continue; - xPoints.add(y); - yPoints.add(x); - } - - // add third octant - for (int i = 0; i < pixelList.size(); i++) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == 0 || x == y) - continue; - xPoints.add(y); - yPoints.add(-x); - } - - // add fourth octant - for (int i = pixelList.size() - 1; i > -1; i--) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - xPoints.add(x); - yPoints.add(-y); - } - - // add 5th octant - for (int i = 0; i < pixelList.size(); i++) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == 0) - continue; - xPoints.add(-x); - yPoints.add(-y); - } + for (int octant = 1; octant <= 8; octant++) { + for (int i = 0; i < pixelList.size(); i++) { + int pixelIndex = (octant % 2 == 0) ? pixelList.size() - i - 1 : i; + int x = pixelList.get(pixelIndex)[0]; + int y = pixelList.get(pixelIndex)[1]; - // add 6th octant - for (int i = pixelList.size() - 1; i > -1; i--) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == 0 || x == y) - continue; - xPoints.add(-y); - yPoints.add(-x); - } - - // add 7th octant - for (int i = 0; i < pixelList.size(); i++) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == y) - continue; - xPoints.add(-y); - yPoints.add(x); - } - - // add 8th octant - for (int i = pixelList.size() - 1; i > -1; i--) { - int x = pixelList.get(i)[0]; - int y = pixelList.get(i)[1]; - if (x == 0) - continue; - xPoints.add(-x); - yPoints.add(y); + int[] transformedPoint = transformPoint(x, y, octant); + xPoints.add(transformedPoint[0]); + yPoints.add(transformedPoint[1]); + } } List> res = new ArrayList<>(); From 6dd7f5545603a8d25c9a3c9542464cfc18f790a4 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:21:16 +0400 Subject: [PATCH 3/6] cache only first octant, refactor --- .../creme332/algorithms/CircleCalculator.java | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/CircleCalculator.java b/src/main/java/com/github/creme332/algorithms/CircleCalculator.java index 7ac40dc5..62df58fb 100644 --- a/src/main/java/com/github/creme332/algorithms/CircleCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/CircleCalculator.java @@ -7,17 +7,18 @@ public class CircleCalculator { /** - * A map that stores calculated coordinates for circles of different radii + * A map that stores calculated coordinates of pixels in first octant of circles + * of different radii * centered at origin. * * The key is the radius and the value is a list with 2 elements. The first * element is a list of x-coordinates while the second element is a list of * y-coordinates. */ - private HashMap>> cache; + private HashMap>> firstOctantCache; public CircleCalculator() { - cache = new HashMap<>(); + firstOctantCache = new HashMap<>(); } /** @@ -64,24 +65,24 @@ public static int[] transformPoint(int x, int y, int destinationOctant) { * array is a list of y-coordinates. */ public int[][] getOrderedPoints(int centerX, int centerY, int radius) { - // check if result is already available and return it - if (cache.containsKey(radius)) { - List xPoints = cache.get(radius).get(0); - List yPoints = cache.get(radius).get(1); - int[] xArray = xPoints.stream().mapToInt(i -> i + centerX).toArray(); - int[] yArray = yPoints.stream().mapToInt(i -> i + centerY).toArray(); - return new int[][] { xArray, yArray }; - } + /** + * Pixels in first octant of circle centered at origin. + */ + final List> firstOctantList = getFirstOctantPoints(radius); + + /** + * Number of pixels in first octant + */ + final int firstOctantSize = firstOctantList.get(0).size(); - final List pixelList = getFirstOctantPoints(radius); List xPoints = new ArrayList<>(); List yPoints = new ArrayList<>(); for (int octant = 1; octant <= 8; octant++) { - for (int i = 0; i < pixelList.size(); i++) { - int pixelIndex = (octant % 2 == 0) ? pixelList.size() - i - 1 : i; - int x = pixelList.get(pixelIndex)[0]; - int y = pixelList.get(pixelIndex)[1]; + for (int i = 0; i < firstOctantSize; i++) { + int pixelIndex = (octant % 2 == 0) ? firstOctantSize - i - 1 : i; + int x = firstOctantList.get(0).get(pixelIndex); + int y = firstOctantList.get(1).get(pixelIndex); int[] transformedPoint = transformPoint(x, y, octant); xPoints.add(transformedPoint[0]); @@ -89,12 +90,12 @@ public int[][] getOrderedPoints(int centerX, int centerY, int radius) { } } - List> res = new ArrayList<>(); - res.add(xPoints); - res.add(yPoints); - cache.put(radius, res); + // convert arrays to primitive arrays and apply translation based on circle + // center coordinates + int[] xArray = xPoints.stream().mapToInt(i -> i + centerX).toArray(); + int[] yArray = yPoints.stream().mapToInt(i -> i + centerY).toArray(); - return getOrderedPoints(centerX, centerY, radius); + return new int[][] { xArray, yArray }; } /** @@ -102,22 +103,29 @@ public int[][] getOrderedPoints(int centerX, int centerY, int radius) { * origin. Bresenham algorithm is used. * * @param radius Radius of circle - * @return A 2D array where each element is an array {x, y} representing the x - * and y coordinates of a pixel. + * @return A 2D array where the first list contains all the x-coordinates and + * the second list contains all the y-coordinates. */ - public static List getFirstOctantPoints(int radius) { + public List> getFirstOctantPoints(int radius) { if (radius <= 0) { throw new IllegalArgumentException("Radius must be positive"); } - List pixelList = new ArrayList<>(); + // check if result is already available in cache and return it + if (firstOctantCache.containsKey(radius)) { + return firstOctantCache.get(radius); + } + + List xPoints = new ArrayList<>(); + List yPoints = new ArrayList<>(); int x = 0; int y = radius; int decisionParameter = 1 - radius; while (x <= y) { - pixelList.add(new int[] { x, y }); + xPoints.add(x); + yPoints.add(y); x++; if (decisionParameter < 0) { @@ -128,7 +136,13 @@ public static List getFirstOctantPoints(int radius) { } } - return pixelList; + // save result to cache + List> result = new ArrayList<>(); + result.add(xPoints); + result.add(yPoints); + firstOctantCache.put(radius, result); + + return result; } /** From 1b80a0229deda4c632d3fb49f43d0bc8f08fd60e Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:08:38 +0400 Subject: [PATCH 4/6] rename test file --- ...lgorithmTest.java => CircleCalculatorTest.java} | 2 +- ...gorithmTest.java => EllipseCalculatorTest.java} | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) rename src/test/java/com/github/creme332/tests/algorithms/{CircleAlgorithmTest.java => CircleCalculatorTest.java} (98%) rename src/test/java/com/github/creme332/tests/algorithms/{EllipseAlgorithmTest.java => EllipseCalculatorTest.java} (89%) diff --git a/src/test/java/com/github/creme332/tests/algorithms/CircleAlgorithmTest.java b/src/test/java/com/github/creme332/tests/algorithms/CircleCalculatorTest.java similarity index 98% rename from src/test/java/com/github/creme332/tests/algorithms/CircleAlgorithmTest.java rename to src/test/java/com/github/creme332/tests/algorithms/CircleCalculatorTest.java index a9037147..9eb48554 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/CircleAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/CircleCalculatorTest.java @@ -6,7 +6,7 @@ import static org.junit.Assert.*; -public class CircleAlgorithmTest { +public class CircleCalculatorTest { @Test public void testValidCircleCenteredOrigin() { diff --git a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java b/src/test/java/com/github/creme332/tests/algorithms/EllipseCalculatorTest.java similarity index 89% rename from src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java rename to src/test/java/com/github/creme332/tests/algorithms/EllipseCalculatorTest.java index 8ae64696..c0b1ba85 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseCalculatorTest.java @@ -8,11 +8,11 @@ import java.util.List; -public class EllipseAlgorithmTest { +public class EllipseCalculatorTest { @Test public void testEllipseCenteredAtOrigin() { - List pixels = EllipseCalculator.drawEllipse(0, 0, 3, 2); + List pixels = EllipseCalculator.getAllPoints(0, 0, 3, 2); int[][] expectedArray = { // Quadrant 1 @@ -53,14 +53,14 @@ public void testEllipseNotAtOrigin() { expectedArray[i][1] += centerY; } - List pixels = EllipseCalculator.drawEllipse(centerX, centerY, 8, 6); + List pixels = EllipseCalculator.getAllPoints(centerX, centerY, 8, 6); TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test public void testZeroRadii() { try { - EllipseCalculator.drawEllipse(10, 10, 0, 0); + EllipseCalculator.getAllPoints(10, 10, 0, 0); fail("Expected IllegalArgumentException for zero radii"); } catch (IllegalArgumentException e) { assertEquals("Radii must be positive values.", e.getMessage()); @@ -70,7 +70,7 @@ public void testZeroRadii() { @Test public void testNegativeRadii() { try { - EllipseCalculator.drawEllipse(10, 10, -5, -3); + EllipseCalculator.getAllPoints(10, 10, -5, -3); fail("Expected IllegalArgumentException for negative radii"); } catch (IllegalArgumentException e) { assertEquals("Radii must be positive values.", e.getMessage()); @@ -79,7 +79,7 @@ public void testNegativeRadii() { @Test public void testHorizontalEllipseAtOrigin() { - List pixels = EllipseCalculator.drawEllipse(0, 0, 7, 3); + List pixels = EllipseCalculator.getAllPoints(0, 0, 7, 3); int[][] expectedArray = { // Quadrant 1 @@ -97,7 +97,7 @@ public void testHorizontalEllipseAtOrigin() { @Test public void testVerticalEllipseAtOrigin() { - List pixels = EllipseCalculator.drawEllipse(0, 0, 3, 7); + List pixels = EllipseCalculator.getAllPoints(0, 0, 3, 7); int[][] expectedArray = { // Quadrant 1 From 4c6909959b80dac53a2c9f5dc693a869f15856ff Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:38:22 +0400 Subject: [PATCH 5/6] add method to calculate ordered points with caching --- .../algorithms/EllipseCalculator.java | 159 +++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java b/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java index 43cf8f82..a3f67a82 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseCalculator.java @@ -1,12 +1,167 @@ package com.github.creme332.algorithms; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class EllipseCalculator { - private EllipseCalculator() { + /** + * A map that stores calculated coordinates of pixels in first quadrant of + * ellipse of different radii centered at origin. + * + */ + private HashMap, List>> firstQuadrantCache; + + public EllipseCalculator() { + firstQuadrantCache = new HashMap<>(); + } + + /** + * Maps a point (x, y) in first quadrant to some other quadrant using 4-way + * symmetry of ellipse. + * + * @param x x-coordinate of a point in first quadrant. + * @param y y-coordinate of a point in the first quadrant. + * @param destinationQuadrant index of some other quadrant (one-based index). + * @return x and y coordinates of point in new quadrant. + */ + public static int[] transformPoint(int x, int y, int destinationQuadrant) { + switch (destinationQuadrant) { + case 1: + return new int[] { x, y }; + case 2: + return new int[] { x, -y }; + case 3: + return new int[] { -x, -y }; + case 4: + return new int[] { -x, y }; + default: + break; + } + throw new IllegalArgumentException("Quadrant must be between 1 and 4."); + } + + /** + * Calculates coordinates of ellipse starting from top and moving clockwise. + * Points are ordered clockwise. Duplicate points may occur at x==0 and y==0. + * + * @param centerX x-coordinate of circle center + * @param centerY y-coordinate of circle center + * @param radius radius of circle + * @return 2 arrays. The first array is the list of x-coordinates and the second + * array is a list of y-coordinates. + */ + public int[][] getOrderedPoints(int centerX, int centerY, int rx, int ry) { + /** + * Pixels in first octant of circle centered at origin. + */ + final List> firstQuadrantList = getFirstQuadrantPoints(rx, ry); + + /** + * Number of pixels in first quadrant + */ + final int firstQuadrantSize = firstQuadrantList.get(0).size(); + + List xPoints = new ArrayList<>(); + List yPoints = new ArrayList<>(); + + for (int quadrant = 1; quadrant <= 4; quadrant++) { + for (int i = 0; i < firstQuadrantSize; i++) { + int pixelIndex = (quadrant % 2 == 0) ? firstQuadrantSize - i - 1 : i; + int x = firstQuadrantList.get(0).get(pixelIndex); + int y = firstQuadrantList.get(1).get(pixelIndex); + + int[] transformedPoint = transformPoint(x, y, quadrant); + xPoints.add(transformedPoint[0]); + yPoints.add(transformedPoint[1]); + } + } + + // convert arrays to primitive arrays and apply translation based on circle + // center coordinates + int[] xArray = xPoints.stream().mapToInt(i -> i + centerX).toArray(); + int[] yArray = yPoints.stream().mapToInt(i -> i + centerY).toArray(); + + return new int[][] { xArray, yArray }; + } + + /** + * Calculates coordinates of circle starting from top and moving clockwise. + * Points are ordered clockwise. Duplicate points may occur at x==0 and x==y. + * + * @param centerX x-coordinate of circle center + * @param centerY y-coordinate of circle center + * @param radius radius of circle + * @return 2 arrays. The first array is the list of x-coordinates and the second + * array is a list of y-coordinates. + */ + public List> getFirstQuadrantPoints(int rx, int ry) { + if (rx <= 0 || ry <= 0) { + throw new IllegalArgumentException("Radii must be positive values."); + } + + if (firstQuadrantCache.containsKey(Map.entry(rx, ry))) { + return firstQuadrantCache.get(Map.entry(rx, ry)); + } + + List xPoints = new ArrayList<>(); + List yPoints = new ArrayList<>(); + + int x = 0; + int y = ry; + + int rx2 = rx * rx; + int ry2 = ry * ry; + int tworx2 = 2 * rx2; + int twory2 = 2 * ry2; + + int p = (int) (ry2 - (rx2 * ry) + (0.25 * rx2)); // Initial decision parameter + + int px = 0; + int py = tworx2 * y; + + // Region 1 + while (px < py) { + xPoints.add(x); + yPoints.add(y); + + x++; + px += twory2; + if (p < 0) { + p += ry2 + px; + } else { + y--; + py -= tworx2; + p += ry2 + px - py; + } + } + + // Region 2 + p = (int) (ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2); + while (y >= 0) { + xPoints.add(x); + yPoints.add(y); + + y--; + py -= tworx2; + if (p > 0) { + p += rx2 - py; + } else { + x++; + px += twory2; + p += rx2 - py + px; + } + } + + // save result to cache + List> result = new ArrayList<>(); + result.add(xPoints); + result.add(yPoints); + firstQuadrantCache.put(Map.entry(rx, ry), result); + return result; } /** @@ -21,7 +176,7 @@ private EllipseCalculator() { * pixel of the ellipse * @throws IllegalArgumentException if rx or ry are non-positive */ - public static List drawEllipse(int centerX, int centerY, int rx, int ry) { + public static List getAllPoints(int centerX, int centerY, int rx, int ry) { if (rx <= 0 || ry <= 0) { throw new IllegalArgumentException("Radii must be positive values."); } From b0ef5f84542a624237c7e1f35d7b639339326616 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:38:41 +0400 Subject: [PATCH 6/6] implement draw ellipse --- .../creme332/controller/CanvasController.java | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/creme332/controller/CanvasController.java b/src/main/java/com/github/creme332/controller/CanvasController.java index 0e786da1..6e18a6b1 100644 --- a/src/main/java/com/github/creme332/controller/CanvasController.java +++ b/src/main/java/com/github/creme332/controller/CanvasController.java @@ -22,6 +22,7 @@ import java.awt.image.BufferedImage; import com.github.creme332.algorithms.CircleCalculator; +import com.github.creme332.algorithms.EllipseCalculator; import com.github.creme332.model.AppState; import com.github.creme332.model.CanvasModel; import com.github.creme332.model.Mode; @@ -32,6 +33,7 @@ public class CanvasController implements PropertyChangeListener { private Canvas canvas; private CircleCalculator circleCalculator = new CircleCalculator(); + private EllipseCalculator ellipseCalculator = new EllipseCalculator(); /** * Used to store coordinate where mouse drag started */ @@ -101,7 +103,25 @@ public void mouseMoved(MouseEvent e) { currentWrapper.setShape(getCircle((int) center.getX(), (int) center.getY(), roundedRadius)); canvas.repaint(); return; + } + + if (app.getMode() == Mode.DRAW_ELLIPSE && currentWrapper != null + && currentWrapper.getPlottedPoints().size() == 2) { + // create an ellipse + Point2D center = currentWrapper.getPlottedPoints().get(0); + int ry = (int) Math.abs(currentWrapper.getPlottedPoints().get(1).distance(center)); + int rx = (int) Math.abs(polySpaceMousePosition.distance(center)); + if (rx == 0 || ry == 0) { + return; + } + + int[][] coordinates = ellipseCalculator.getOrderedPoints((int) center.getX(), (int) center.getY(), + rx, ry); + Polygon ellipse = new Polygon(coordinates[0], coordinates[1], coordinates[0].length); + currentWrapper.setShape(ellipse); + canvas.repaint(); + return; } } @@ -235,7 +255,7 @@ private void handleMousePressed(MouseEvent e) { if (app.getMode() == Mode.DRAW_ELLIPSE) { if (currentWrapper == null) { - // first foci has been selected + // center of ellipse has been selected // create a shape wrapper currentWrapper = new ShapeWrapper(model.getFillColor(), model.getFillColor(), model.getLineType(), @@ -246,15 +266,25 @@ private void handleMousePressed(MouseEvent e) { model.getShapes().add(currentWrapper); } else { - // second foci has now been selected if (currentWrapper.getPlottedPoints().size() == 1) { + // second point has now been selected currentWrapper.getPlottedPoints().add(polySpaceMousePosition); } else { // third point has been selected currentWrapper.getPlottedPoints().add(polySpaceMousePosition); // create an ellipse - Polygon ellipse = new Polygon(); + Point2D center = currentWrapper.getPlottedPoints().get(0); + int ry = (int) Math.abs(currentWrapper.getPlottedPoints().get(1).distance(center)); + int rx = (int) Math.abs(currentWrapper.getPlottedPoints().get(2).distance(center)); + + if (rx == 0 || ry == 0) { + return; + } + + int[][] coordinates = ellipseCalculator.getOrderedPoints((int) center.getX(), (int) center.getY(), + rx, ry); + Polygon ellipse = new Polygon(coordinates[0], coordinates[1], coordinates[0].length); currentWrapper.setShape(ellipse);