From 2bf40e0b7b8609801b0a6ec3b314da558c419fd9 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Tue, 4 Jun 2024 20:10:53 +0400 Subject: [PATCH 1/8] implementation of midpoint ellipse algorithm and tests for the algorithm --- .../creme332/algorithms/EllipseAlgorithm.java | 62 ++++++++++++++ .../github/creme332/EllipseAlgorithmTest.java | 84 +++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java create mode 100644 src/test/java/com/github/creme332/EllipseAlgorithmTest.java diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java new file mode 100644 index 00000000..e43fc818 --- /dev/null +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -0,0 +1,62 @@ +package com.github.creme332.algorithms; + +public class EllipseAlgorithm { + + /** + * This method draws an ellipse using the Midpoint Ellipse Algorithm. + * + * @param centerX X-coordinate of the ellipse's center + * @param centerY Y-coordinate of the ellipse's center + * @param rx Radius of the ellipse along the X-axis (horizontal radius) + * @param ry Radius of the ellipse along the Y-axis (vertical radius) + * @return A 2D integer array representing the pixels of the ellipse + */ + public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { + int width = 2 * rx + 1; + int height = 2 * ry + 1; + int[][] pixels = new int[height][width]; + + // Handle special cases: zero radius or negative radius + if (rx <= 0 || ry <= 0) { + return pixels; // Empty array for invalid radius + } + + int x = 0; + int y = ry; + + int p = ry * ry - rx * rx * ry + rx * rx / 2; // Initial decision parameter + + // Region 1 - Iterate based on y until decision parameter changes sign + while (ry * ry >= x * x) { + pixels[centerY + y][centerX + x] = 1; // Set pixel + pixels[centerY - y][centerX + x] = 1; // Reflect across y-axis + pixels[centerY + y][centerX - x] = 1; // Reflect across x-axis + pixels[centerY - y][centerX - x] = 1; // Reflect across both axes + + x++; + if (p < 0) { + p += 2 * ry * x; + } else { + p += 2 * ry * x - 2 * rx * rx; + y--; + } + } + + // Region 2 - Iterate based on x until decision parameter changes sign again + while (x < rx) { + pixels[centerY + y][centerX + x] = 1; // Set pixel + pixels[centerY - y][centerX + x] = 1; // Reflect across y-axis + pixels[centerY + y][centerX - x] = 1; // Reflect across x-axis + pixels[centerY - y][centerX - x] = 1; // Reflect across both axes + + y--; + if (p > 0) { + p -= 2 * rx * x; + } else { + p -= 2 * rx * x + 2 * ry * ry; + x++; + } + } + return pixels; + } +} diff --git a/src/test/java/com/github/creme332/EllipseAlgorithmTest.java b/src/test/java/com/github/creme332/EllipseAlgorithmTest.java new file mode 100644 index 00000000..0b7661dc --- /dev/null +++ b/src/test/java/com/github/creme332/EllipseAlgorithmTest.java @@ -0,0 +1,84 @@ +package com.github.creme332; + +import org.junit.Test; +import static org.junit.Assert.*; +import com.github.creme332.algorithms.EllipseAlgorithm; + + +public class EllipseAlgorithmTest { + + @Test + public void testValidEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 5, 3); + + // Verify pixels along the major axis (horizontal in this case) + for (int y = 7; y <= 13; y++) { + assertEquals(1, pixels[y][10]); // Check pixels at center X + } + + // Verify pixels along the minor axis (vertical) + for (int x = 8; x <= 12; x++) { + assertEquals(1, pixels[10][x]); // Check pixels at center Y + } + + } + + @Test + public void testZeroRadii() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 0, 0); + assertEquals(0, pixels.length); + } + + @Test + public void testNegativeRadii() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, -5, -3); + assertEquals(0, pixels.length); + } + + @Test + public void testHorizontalEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 7, 3); + + // Verify wider spread along X-axis compared to Y-axis + int xSpread = 0; + for (int y = 7; y <= 13; y++) { + if (pixels[y][10] == 1) { + xSpread++; + } + } + assertTrue(xSpread > pixels.length / 2); // More pixels set on X-axis + } + + @Test + public void testVerticalEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 3, 7); + + // Verify wider spread along Y-axis compared to X-axis + int ySpread = 0; + for (int x = 8; x <= 12; x++) { + if (pixels[10][x] == 1) { + ySpread++; + } + } + assertTrue(ySpread > pixels.length / 2); // More pixels set on Y-axis + } + + @Test + public void testCenteredEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(5, 7, 3, 2); + + // Verify pixels are set relative to the given center coordinates + assertEquals(1, pixels[9][5]); // Check pixel at (5, 7) + assertEquals(1, pixels[7][7]); // Check pixel at (5, 7) with Y offset + } + + @Test + public void testSmallRadii() { + int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 1, 1); + + // Verify single pixel set at the center + assertEquals(1, pixels[10][10]); + assertEquals(1, pixels.length); // Ensure single row and column + } + +} From 0a1a8ebe441d1e2f559d9747e5cb0c658fae208f Mon Sep 17 00:00:00 2001 From: Divyesh000 Date: Wed, 5 Jun 2024 11:21:38 +0400 Subject: [PATCH 2/8] refactor code in EllipseAlgorithm and rework on the tests case of EllipseAlgorithm --- .../creme332/algorithms/EllipseAlgorithm.java | 57 +++++++------ .../github/creme332/EllipseAlgorithmTest.java | 84 ------------------- .../algorithms/EllipseAlgorithmTest.java | 79 +++++++++++++++++ 3 files changed, 111 insertions(+), 109 deletions(-) delete mode 100644 src/test/java/com/github/creme332/EllipseAlgorithmTest.java create mode 100644 src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index e43fc818..14f37a59 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -1,62 +1,69 @@ package com.github.creme332.algorithms; public class EllipseAlgorithm { - + /** * This method draws an ellipse using the Midpoint Ellipse Algorithm. * * @param centerX X-coordinate of the ellipse's center * @param centerY Y-coordinate of the ellipse's center - * @param rx Radius of the ellipse along the X-axis (horizontal radius) - * @param ry Radius of the ellipse along the Y-axis (vertical radius) + * @param rx Radius of the ellipse along the X-axis (horizontal radius) + * @param ry Radius of the ellipse along the Y-axis (vertical radius) * @return A 2D integer array representing the pixels of the ellipse + * @throws IllegalArgumentException if rx or ry are non-positive */ public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { + if (rx <= 0 || ry <= 0) { + throw new IllegalArgumentException("Radii must be positive values."); + } + int width = 2 * rx + 1; int height = 2 * ry + 1; int[][] pixels = new int[height][width]; - // Handle special cases: zero radius or negative radius - if (rx <= 0 || ry <= 0) { - return pixels; // Empty array for invalid radius - } - int x = 0; int y = ry; - int p = ry * ry - rx * rx * ry + rx * rx / 2; // Initial decision parameter + int p = ry * ry - rx * rx * ry + (rx * rx) / 4; // Initial decision parameter // Region 1 - Iterate based on y until decision parameter changes sign - while (ry * ry >= x * x) { - pixels[centerY + y][centerX + x] = 1; // Set pixel - pixels[centerY - y][centerX + x] = 1; // Reflect across y-axis - pixels[centerY + y][centerX - x] = 1; // Reflect across x-axis - pixels[centerY - y][centerX - x] = 1; // Reflect across both axes - + while (ry * ry * x <= rx * rx * y) { + setPixel(pixels, rx, ry, x, y); x++; if (p < 0) { - p += 2 * ry * x; + p += 2 * ry * ry * x + ry * ry; } else { - p += 2 * ry * x - 2 * rx * rx; y--; + p += 2 * ry * ry * x - 2 * rx * rx * y + ry * ry; } } // Region 2 - Iterate based on x until decision parameter changes sign again - while (x < rx) { - pixels[centerY + y][centerX + x] = 1; // Set pixel - pixels[centerY - y][centerX + x] = 1; // Reflect across y-axis - pixels[centerY + y][centerX - x] = 1; // Reflect across x-axis - pixels[centerY - y][centerX - x] = 1; // Reflect across both axes - + p = (int) Math.round(ry * ry * (x + 0.5) * (x + 0.5) + rx * rx * (y - 1) * (y - 1) - rx * rx * ry * ry); + while (y >= 0) { + setPixel(pixels, rx, ry, x, y); y--; if (p > 0) { - p -= 2 * rx * x; + p -= 2 * rx * rx * y + rx * rx; } else { - p -= 2 * rx * x + 2 * ry * ry; x++; + p += 2 * ry * ry * x - 2 * rx * rx * y + rx * rx; } } return pixels; } + + private static void setPixel(int[][] pixels, int rx, int ry, int x, int y) { + int width = pixels[0].length; + int height = pixels.length; + + if (isValidPixel(ry + y, rx + x, height, width)) pixels[ry + y][rx + x] = 1; + if (isValidPixel(ry - y, rx + x, height, width)) pixels[ry - y][rx + x] = 1; + if (isValidPixel(ry + y, rx - x, height, width)) pixels[ry + y][rx - x] = 1; + if (isValidPixel(ry - y, rx - x, height, width)) pixels[ry - y][rx - x] = 1; + } + + private static boolean isValidPixel(int y, int x, int height, int width) { + return y >= 0 && y < height && x >= 0 && x < width; + } } diff --git a/src/test/java/com/github/creme332/EllipseAlgorithmTest.java b/src/test/java/com/github/creme332/EllipseAlgorithmTest.java deleted file mode 100644 index 0b7661dc..00000000 --- a/src/test/java/com/github/creme332/EllipseAlgorithmTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.github.creme332; - -import org.junit.Test; -import static org.junit.Assert.*; -import com.github.creme332.algorithms.EllipseAlgorithm; - - -public class EllipseAlgorithmTest { - - @Test - public void testValidEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 5, 3); - - // Verify pixels along the major axis (horizontal in this case) - for (int y = 7; y <= 13; y++) { - assertEquals(1, pixels[y][10]); // Check pixels at center X - } - - // Verify pixels along the minor axis (vertical) - for (int x = 8; x <= 12; x++) { - assertEquals(1, pixels[10][x]); // Check pixels at center Y - } - - } - - @Test - public void testZeroRadii() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 0, 0); - assertEquals(0, pixels.length); - } - - @Test - public void testNegativeRadii() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, -5, -3); - assertEquals(0, pixels.length); - } - - @Test - public void testHorizontalEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 7, 3); - - // Verify wider spread along X-axis compared to Y-axis - int xSpread = 0; - for (int y = 7; y <= 13; y++) { - if (pixels[y][10] == 1) { - xSpread++; - } - } - assertTrue(xSpread > pixels.length / 2); // More pixels set on X-axis - } - - @Test - public void testVerticalEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 3, 7); - - // Verify wider spread along Y-axis compared to X-axis - int ySpread = 0; - for (int x = 8; x <= 12; x++) { - if (pixels[10][x] == 1) { - ySpread++; - } - } - assertTrue(ySpread > pixels.length / 2); // More pixels set on Y-axis - } - - @Test - public void testCenteredEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(5, 7, 3, 2); - - // Verify pixels are set relative to the given center coordinates - assertEquals(1, pixels[9][5]); // Check pixel at (5, 7) - assertEquals(1, pixels[7][7]); // Check pixel at (5, 7) with Y offset - } - - @Test - public void testSmallRadii() { - int[][] pixels = EllipseAlgorithm.drawEllipse(10, 10, 1, 1); - - // Verify single pixel set at the center - assertEquals(1, pixels[10][10]); - assertEquals(1, pixels.length); // Ensure single row and column - } - -} diff --git a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java new file mode 100644 index 00000000..bdaeb5b9 --- /dev/null +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -0,0 +1,79 @@ +package com.github.creme332.tests.algorithms; + +import org.junit.Test; +import static org.junit.Assert.*; +import com.github.creme332.algorithms.EllipseAlgorithm; + +public class EllipseAlgorithmTest { + + @Test + public void testValidEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(3, 2, 3, 2); + + // Verify pixels along the major axis (horizontal in this case) + assertEquals(1, pixels[2][6]); // Center at (3, 2), 3 units right + assertEquals(1, pixels[2][0]); // Center at (3, 2), 3 units left + + // Verify pixels along the minor axis (vertical) + assertEquals(1, pixels[4][3]); // Center at (3, 2), 2 units up + assertEquals(1, pixels[0][3]); // Center at (3, 2), 2 units down + } + + @Test + public void testZeroRadii() { + try { + EllipseAlgorithm.drawEllipse(10, 10, 0, 0); + fail("Expected IllegalArgumentException for zero radii"); + } catch (IllegalArgumentException e) { + assertEquals("Radii must be positive values.", e.getMessage()); + } + } + + @Test + public void testNegativeRadii() { + try { + EllipseAlgorithm.drawEllipse(10, 10, -5, -3); + fail("Expected IllegalArgumentException for negative radii"); + } catch (IllegalArgumentException e) { + assertEquals("Radii must be positive values.", e.getMessage()); + } + } + + @Test + public void testHorizontalEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(7, 3, 7, 3); + + // Verify wider spread along X-axis compared to Y-axis + int xSpread = 0; + for (int y = 0; y <= 6; y++) { + if (pixels[y][7] == 1) { + xSpread++; + } + } + assertTrue(xSpread > 0); // More pixels set on X-axis + } + + @Test + public void testVerticalEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(3, 7, 3, 7); + + // Verify wider spread along Y-axis compared to X-axis + int ySpread = 0; + for (int x = 0; x <= 6; x++) { + if (pixels[7][x] == 1) { + ySpread++; + } + } + assertTrue(ySpread > 0); // More pixels set on Y-axis + } + + @Test + public void testCenteredEllipse() { + int[][] pixels = EllipseAlgorithm.drawEllipse(3, 2, 3, 2); + + // Verify pixels are set relative to the given center coordinates + assertEquals(1, pixels[2 + 2][3]); // Check pixel at (3, 4) + assertEquals(1, pixels[2 - 2][3]); // Check pixel at (3, 0) + } + +} From a5f6e1d49564336ca6ca28406b2d146cec0de7cb Mon Sep 17 00:00:00 2001 From: Divyesh000 Date: Wed, 5 Jun 2024 15:29:27 +0400 Subject: [PATCH 3/8] modify the drawEllipse method to return the coordinates of the pixels forming the ellipse rather than a 2D array of pixels --- .../creme332/algorithms/EllipseAlgorithm.java | 34 +++++----- .../algorithms/EllipseAlgorithmTest.java | 62 ++++++++++--------- 2 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index 14f37a59..0122f95c 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -1,25 +1,26 @@ package com.github.creme332.algorithms; +import java.util.ArrayList; +import java.util.List; + public class EllipseAlgorithm { - /** + /** * This method draws an ellipse using the Midpoint Ellipse Algorithm. * * @param centerX X-coordinate of the ellipse's center * @param centerY Y-coordinate of the ellipse's center * @param rx Radius of the ellipse along the X-axis (horizontal radius) * @param ry Radius of the ellipse along the Y-axis (vertical radius) - * @return A 2D integer array representing the pixels of the ellipse + * @return A list of integer arrays representing the pixels of the ellipse * @throws IllegalArgumentException if rx or ry are non-positive */ - public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { + public static List drawEllipse(int centerX, int centerY, int rx, int ry) { if (rx <= 0 || ry <= 0) { throw new IllegalArgumentException("Radii must be positive values."); } - int width = 2 * rx + 1; - int height = 2 * ry + 1; - int[][] pixels = new int[height][width]; + List pixels = new ArrayList<>(); int x = 0; int y = ry; @@ -28,7 +29,7 @@ public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { // Region 1 - Iterate based on y until decision parameter changes sign while (ry * ry * x <= rx * rx * y) { - setPixel(pixels, rx, ry, x, y); + setPixel(pixels, centerX, centerY, x, y); x++; if (p < 0) { p += 2 * ry * ry * x + ry * ry; @@ -41,7 +42,7 @@ public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { // Region 2 - Iterate based on x until decision parameter changes sign again p = (int) Math.round(ry * ry * (x + 0.5) * (x + 0.5) + rx * rx * (y - 1) * (y - 1) - rx * rx * ry * ry); while (y >= 0) { - setPixel(pixels, rx, ry, x, y); + setPixel(pixels, centerX, centerY, x, y); y--; if (p > 0) { p -= 2 * rx * rx * y + rx * rx; @@ -53,17 +54,10 @@ public static int[][] drawEllipse(int centerX, int centerY, int rx, int ry) { return pixels; } - private static void setPixel(int[][] pixels, int rx, int ry, int x, int y) { - int width = pixels[0].length; - int height = pixels.length; - - if (isValidPixel(ry + y, rx + x, height, width)) pixels[ry + y][rx + x] = 1; - if (isValidPixel(ry - y, rx + x, height, width)) pixels[ry - y][rx + x] = 1; - if (isValidPixel(ry + y, rx - x, height, width)) pixels[ry + y][rx - x] = 1; - if (isValidPixel(ry - y, rx - x, height, width)) pixels[ry - y][rx - x] = 1; - } - - private static boolean isValidPixel(int y, int x, int height, int width) { - return y >= 0 && y < height && x >= 0 && x < width; + private static void setPixel(List pixels, int centerX, int centerY, int x, int y) { + pixels.add(new int[] {centerX + x, centerY + y}); + pixels.add(new int[] {centerX - x, centerY + y}); + pixels.add(new int[] {centerX + x, centerY - y}); + pixels.add(new int[] {centerX - x, centerY - y}); } } 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 bdaeb5b9..9c1805eb 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -3,20 +3,23 @@ import org.junit.Test; import static org.junit.Assert.*; import com.github.creme332.algorithms.EllipseAlgorithm; +import java.util.List; public class EllipseAlgorithmTest { @Test public void testValidEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(3, 2, 3, 2); + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 8, 6); - // Verify pixels along the major axis (horizontal in this case) - assertEquals(1, pixels[2][6]); // Center at (3, 2), 3 units right - assertEquals(1, pixels[2][0]); // Center at (3, 2), 3 units left + int[][] expectedArray = { + {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 5}, {5, 5}, {6, 4}, {7, 3}, + {8, 2}, {8, 1}, {8, 0}, {0, -6}, {-1, 6}, {-2, 6}, {-3, 6}, + {-4, 5}, {-5, 5}, {-6, 4}, {-7, 3}, {-8, 2}, {-8, 1}, {-8, 0}, + {0, -6}, {1, -6}, {2, -6}, {3, -6}, {4, -5}, {5, -5}, {6, -4}, + {7, -3}, {8, -2}, {8, -1}, {-8, -2}, {-8, -1}, {-8, -0} + }; - // Verify pixels along the minor axis (vertical) - assertEquals(1, pixels[4][3]); // Center at (3, 2), 2 units up - assertEquals(1, pixels[0][3]); // Center at (3, 2), 2 units down + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test @@ -41,39 +44,38 @@ public void testNegativeRadii() { @Test public void testHorizontalEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(7, 3, 7, 3); + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); + int[][] expectedArray = { + {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 2}, {5, 1}, {6, 0}, {-7, 0}, + {7, 0}, {-6, 0}, {6, 0}, {-5, 1}, {5, 1}, {-4, 2}, {4, 2}, + {-3, 3}, {3, 3}, {-2, 3}, {2, 3}, {-1, 3}, {1, 3}, {0, -3} + }; - // Verify wider spread along X-axis compared to Y-axis - int xSpread = 0; - for (int y = 0; y <= 6; y++) { - if (pixels[y][7] == 1) { - xSpread++; - } - } - assertTrue(xSpread > 0); // More pixels set on X-axis + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test public void testVerticalEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(3, 7, 3, 7); + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); + int[][] expectedArray = { + {0, 7}, {1, 6}, {2, 6}, {3, 5}, {-3, 5}, {3, 5}, {-2, 6}, {2, 6}, + {-1, 6}, {1, 6}, {-0, 7}, {0, -7}, {1, -6}, {2, -6}, {3, -5}, + {-3, -5}, {3, -5}, {-2, -6}, {2, -6}, {-1, -6}, {1, -6}, {0, -7} + }; - // Verify wider spread along Y-axis compared to X-axis - int ySpread = 0; - for (int x = 0; x <= 6; x++) { - if (pixels[7][x] == 1) { - ySpread++; - } - } - assertTrue(ySpread > 0); // More pixels set on Y-axis + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test public void testCenteredEllipse() { - int[][] pixels = EllipseAlgorithm.drawEllipse(3, 2, 3, 2); + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); + int[][] expectedArray = { + {0, 2}, {1, 2}, {2, 1}, {3, 0}, {3, 0}, {3, 0}, {-3, 0}, + {2, -1}, {1, -2}, {0, -2}, {0, 2}, {-1, 2}, {-2, 1}, + {-3, 0}, {-2, -1}, {-1, -2}, {0, -2} + }; - // Verify pixels are set relative to the given center coordinates - assertEquals(1, pixels[2 + 2][3]); // Check pixel at (3, 4) - assertEquals(1, pixels[2 - 2][3]); // Check pixel at (3, 0) + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } - + } From 94c1e887724b8739fcfb81a85f013dc3194e2ae1 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Thu, 6 Jun 2024 20:27:08 +0400 Subject: [PATCH 4/8] refactor EllipseAlgorithm.java to optimize ellipse drawing and update corresponding test cases in EllipseAlgorithmTest.java --- .../creme332/algorithms/EllipseAlgorithm.java | 44 ++++++++++------- .../algorithms/EllipseAlgorithmTest.java | 48 ++++++++++--------- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index 0122f95c..da665d3c 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -25,39 +25,51 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) int x = 0; int y = ry; - int p = ry * ry - rx * rx * ry + (rx * rx) / 4; // Initial decision parameter + int rx2 = rx * rx; + int ry2 = ry * ry; + int tworx2 = 2 * rx2; + int twory2 = 2 * ry2; - // Region 1 - Iterate based on y until decision parameter changes sign - while (ry * ry * x <= rx * rx * y) { - setPixel(pixels, centerX, centerY, x, y); + int p = (int)(ry2 - (rx2 * ry) + (0.25 * rx2)); // Initial decision parameter + + int px = 0; + int py = tworx2 * y; + + // Region 1 + while (px < py) { + addPixels(pixels, centerX, centerY, x, y); x++; + px += twory2; if (p < 0) { - p += 2 * ry * ry * x + ry * ry; + p += ry2 + px; } else { y--; - p += 2 * ry * ry * x - 2 * rx * rx * y + ry * ry; + py -= tworx2; + p += ry2 + px - py; } } - // Region 2 - Iterate based on x until decision parameter changes sign again - p = (int) Math.round(ry * ry * (x + 0.5) * (x + 0.5) + rx * rx * (y - 1) * (y - 1) - rx * rx * ry * ry); + // Region 2 + p = (int)(ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2); while (y >= 0) { - setPixel(pixels, centerX, centerY, x, y); + addPixels(pixels, centerX, centerY, x, y); y--; + py -= tworx2; if (p > 0) { - p -= 2 * rx * rx * y + rx * rx; + p += rx2 - py; } else { x++; - p += 2 * ry * ry * x - 2 * rx * rx * y + rx * rx; + px += twory2; + p += rx2 - py + px; } } return pixels; } - private static void setPixel(List pixels, int centerX, int centerY, int x, int y) { - pixels.add(new int[] {centerX + x, centerY + y}); - pixels.add(new int[] {centerX - x, centerY + y}); - pixels.add(new int[] {centerX + x, centerY - y}); - pixels.add(new int[] {centerX - x, centerY - y}); + private static void addPixels(List pixels, int centerX, int centerY, int x, int y) { + pixels.add(new int[]{centerX + x, centerY + y}); + pixels.add(new int[]{centerX - x, centerY + y}); + pixels.add(new int[]{centerX + x, centerY - y}); + pixels.add(new int[]{centerX - x, centerY - y}); } } 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 9c1805eb..68e2db2c 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -3,6 +3,8 @@ import org.junit.Test; import static org.junit.Assert.*; import com.github.creme332.algorithms.EllipseAlgorithm; + +import java.util.Arrays; import java.util.List; public class EllipseAlgorithmTest { @@ -11,13 +13,11 @@ public class EllipseAlgorithmTest { public void testValidEllipse() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 8, 6); - int[][] expectedArray = { - {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 5}, {5, 5}, {6, 4}, {7, 3}, - {8, 2}, {8, 1}, {8, 0}, {0, -6}, {-1, 6}, {-2, 6}, {-3, 6}, - {-4, 5}, {-5, 5}, {-6, 4}, {-7, 3}, {-8, 2}, {-8, 1}, {-8, 0}, - {0, -6}, {1, -6}, {2, -6}, {3, -6}, {4, -5}, {5, -5}, {6, -4}, - {7, -3}, {8, -2}, {8, -1}, {-8, -2}, {-8, -1}, {-8, -0} - }; + int[][] expectedArray = { {0, 6}, {0, 6}, {0, -6}, {0, -6}, {1, 6}, {-1, 6}, {1, -6}, {-1, -6}, {2, 6}, {-2, 6}, {2, -6}, + {-2, -6}, {3, 6}, {-3, 6}, {3, -6}, {-3, -6}, {4, 5}, {-4, 5}, {4, -5}, {-4, -5}, {5, 5}, {-5, 5}, + {5, -5}, {-5, -5}, {6, 4}, {-6, 4}, {6, -4}, {-6, -4}, {7, 3}, {-7, 3}, {7, -3}, {-7, -3}, {8, 2}, + {-8, 2}, {8, -2}, {-8, -2}, {8, 1}, {-8, 1}, {8, -1}, {-8, -1}, {8, 0}, {-8, 0}, {8, 0}, {-8, 0} + }; assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @@ -45,11 +45,13 @@ public void testNegativeRadii() { @Test public void testHorizontalEllipse() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); - int[][] expectedArray = { - {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 2}, {5, 1}, {6, 0}, {-7, 0}, - {7, 0}, {-6, 0}, {6, 0}, {-5, 1}, {5, 1}, {-4, 2}, {4, 2}, - {-3, 3}, {3, 3}, {-2, 3}, {2, 3}, {-1, 3}, {1, 3}, {0, -3} - }; + int[][] expectedArray = { {0, 3}, {0, 3}, {0, -3}, {0, -3}, {1, 3}, {-1, 3}, + {1, -3}, {-1, -3}, {2, 3}, {-2, 3}, {2, -3}, {-2, -3}, + {3, 3}, {-3, 3}, {3, -3}, {-3, -3}, {4, 2}, {-4, 2}, + {4, -2}, {-4, -2}, {5, 2}, {-5, 2}, {5, -2}, {-5, -2}, + {6, 2}, {-6, 2}, {6, -2}, {-6, -2}, {7, 1}, {-7, 1}, + {7, -1}, {-7, -1}, {7, 0}, {-7, 0}, {7, 0}, {-7, 0} + }; assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @@ -57,11 +59,11 @@ public void testHorizontalEllipse() { @Test public void testVerticalEllipse() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); - int[][] expectedArray = { - {0, 7}, {1, 6}, {2, 6}, {3, 5}, {-3, 5}, {3, 5}, {-2, 6}, {2, 6}, - {-1, 6}, {1, 6}, {-0, 7}, {0, -7}, {1, -6}, {2, -6}, {3, -5}, - {-3, -5}, {3, -5}, {-2, -6}, {2, -6}, {-1, -6}, {1, -6}, {0, -7} - }; + int[][] expectedArray = { {0, 7}, {0, 7}, {0, -7}, {0, -7}, {1, 7}, {-1, 7}, {1, -7}, {-1, -7},{2, 6}, + {-2, 6}, {2, -6}, {-2, -6}, {2, 5}, {-2, 5}, {2, -5}, {-2, -5}, {2, 4}, {-2, 4}, + {2, -4}, {-2, -4}, {3, 3}, {-3, 3}, {3, -3}, {-3, -3}, {3, 2}, {-3, 2}, {3, -2}, + {-3, -2}, {3, 1}, {-3, 1}, {3, -1}, {-3, -1}, {3, 0}, {-3, 0}, {3, 0}, {-3, 0} + }; assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @@ -69,13 +71,13 @@ public void testVerticalEllipse() { @Test public void testCenteredEllipse() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); - int[][] expectedArray = { - {0, 2}, {1, 2}, {2, 1}, {3, 0}, {3, 0}, {3, 0}, {-3, 0}, - {2, -1}, {1, -2}, {0, -2}, {0, 2}, {-1, 2}, {-2, 1}, - {-3, 0}, {-2, -1}, {-1, -2}, {0, -2} - }; + int[][] expectedArray = { {0, 2}, {0, 2}, {0, -2}, {0, -2}, {1, 2}, {-1, 2}, {1, -2}, {-1, -2}, + {2, 1}, {-2, 1}, {2, -1}, {-2, -1}, {3, 0}, {-3, 0}, {3, 0}, {-3, 0} + }; + for (int[] is : pixels) { + System.out.println(Arrays.toString(is)); + } assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } - } From bd827a0edda44bbfa780c62942be59447badde50 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sat, 8 Jun 2024 10:22:33 +0400 Subject: [PATCH 5/8] refactore EllipseAlgorithm to use a Set to avoid duplicate pixels --- .../creme332/algorithms/EllipseAlgorithm.java | 37 +++-- .../algorithms/EllipseAlgorithmTest.java | 155 ++++++++++-------- 2 files changed, 111 insertions(+), 81 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index da665d3c..73e271ae 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -1,17 +1,19 @@ package com.github.creme332.algorithms; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class EllipseAlgorithm { /** * This method draws an ellipse using the Midpoint Ellipse Algorithm. * - * @param centerX X-coordinate of the ellipse's center - * @param centerY Y-coordinate of the ellipse's center - * @param rx Radius of the ellipse along the X-axis (horizontal radius) - * @param ry Radius of the ellipse along the Y-axis (vertical radius) + * @param centerX X-coordinate of the ellipse's center + * @param centerY Y-coordinate of the ellipse's center + * @param rx Radius of the ellipse along the X-axis (horizontal radius) + * @param ry Radius of the ellipse along the Y-axis (vertical radius) * @return A list of integer arrays representing the pixels of the ellipse * @throws IllegalArgumentException if rx or ry are non-positive */ @@ -21,6 +23,7 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) } List pixels = new ArrayList<>(); + Set pixelSet = new HashSet<>(); int x = 0; int y = ry; @@ -30,14 +33,14 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) int tworx2 = 2 * rx2; int twory2 = 2 * ry2; - int p = (int)(ry2 - (rx2 * ry) + (0.25 * rx2)); // Initial decision parameter + int p = (int) (ry2 - (rx2 * ry) + (0.25 * rx2)); // Initial decision parameter int px = 0; int py = tworx2 * y; // Region 1 while (px < py) { - addPixels(pixels, centerX, centerY, x, y); + addPixels(pixels, pixelSet, centerX, centerY, x, y); x++; px += twory2; if (p < 0) { @@ -50,9 +53,9 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) } // Region 2 - p = (int)(ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2); + p = (int) (ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2); while (y >= 0) { - addPixels(pixels, centerX, centerY, x, y); + addPixels(pixels, pixelSet, centerX, centerY, x, y); y--; py -= tworx2; if (p > 0) { @@ -66,10 +69,18 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) return pixels; } - private static void addPixels(List pixels, int centerX, int centerY, int x, int y) { - pixels.add(new int[]{centerX + x, centerY + y}); - pixels.add(new int[]{centerX - x, centerY + y}); - pixels.add(new int[]{centerX + x, centerY - y}); - pixels.add(new int[]{centerX - x, centerY - y}); + private static void addPixels(List pixels, Set pixelSet, int centerX, int centerY, int x, int y) { + addPixel(pixels, pixelSet, centerX + x, centerY + y); + addPixel(pixels, pixelSet, centerX - x, centerY + y); + addPixel(pixels, pixelSet, centerX + x, centerY - y); + addPixel(pixels, pixelSet, centerX - x, centerY - y); + } + + private static void addPixel(List pixels, Set pixelSet, int x, int y) { + String key = x + "," + y; + if (!pixelSet.contains(key)) { + pixels.add(new int[]{x, y}); + pixelSet.add(key); + } } } 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 68e2db2c..d56db897 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -4,80 +4,99 @@ import static org.junit.Assert.*; import com.github.creme332.algorithms.EllipseAlgorithm; -import java.util.Arrays; import java.util.List; public class EllipseAlgorithmTest { - @Test - public void testValidEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 8, 6); - - int[][] expectedArray = { {0, 6}, {0, 6}, {0, -6}, {0, -6}, {1, 6}, {-1, 6}, {1, -6}, {-1, -6}, {2, 6}, {-2, 6}, {2, -6}, - {-2, -6}, {3, 6}, {-3, 6}, {3, -6}, {-3, -6}, {4, 5}, {-4, 5}, {4, -5}, {-4, -5}, {5, 5}, {-5, 5}, - {5, -5}, {-5, -5}, {6, 4}, {-6, 4}, {6, -4}, {-6, -4}, {7, 3}, {-7, 3}, {7, -3}, {-7, -3}, {8, 2}, - {-8, 2}, {8, -2}, {-8, -2}, {8, 1}, {-8, 1}, {8, -1}, {-8, -1}, {8, 0}, {-8, 0}, {8, 0}, {-8, 0} - }; - - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); - } - - @Test - public void testZeroRadii() { - try { - EllipseAlgorithm.drawEllipse(10, 10, 0, 0); - fail("Expected IllegalArgumentException for zero radii"); - } catch (IllegalArgumentException e) { - assertEquals("Radii must be positive values.", e.getMessage()); + @Test + public void testValidEllipse() { + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 8, 6); + + int[][] expectedArray = { + // Quadrant 1 + {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 5}, {5, 5}, {6, 4}, {7, 3}, {8, 2}, {8, 1}, {8, 0}, + // Quadrant 2 + {8, -1}, {8, -2}, {7, -3}, {6, -4}, {5, -5}, {4, -5}, {3, -6}, {2, -6}, {1, -6}, {0, -6}, + // Quadrant 3 + {-1, -6}, {-2, -6}, {-3, -6}, {-4, -5}, {-5, -5}, {-6, -4}, {-7, -3}, {-8, -2}, {-8, -1}, {-8, 0}, + // Quadrant 4 + {-8, 1}, {-8, 2}, {-7, 3}, {-6, 4}, {-5, 5}, {-4, 5}, {-3, 6}, {-2, 6}, {-1, 6} + }; + + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + } + + @Test + public void testZeroRadii() { + try { + EllipseAlgorithm.drawEllipse(10, 10, 0, 0); + fail("Expected IllegalArgumentException for zero radii"); + } catch (IllegalArgumentException e) { + assertEquals("Radii must be positive values.", e.getMessage()); + } } - } - - @Test - public void testNegativeRadii() { - try { - EllipseAlgorithm.drawEllipse(10, 10, -5, -3); - fail("Expected IllegalArgumentException for negative radii"); - } catch (IllegalArgumentException e) { - assertEquals("Radii must be positive values.", e.getMessage()); + + @Test + public void testNegativeRadii() { + try { + EllipseAlgorithm.drawEllipse(10, 10, -5, -3); + fail("Expected IllegalArgumentException for negative radii"); + } catch (IllegalArgumentException e) { + assertEquals("Radii must be positive values.", e.getMessage()); + } } - } - - @Test - public void testHorizontalEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); - int[][] expectedArray = { {0, 3}, {0, 3}, {0, -3}, {0, -3}, {1, 3}, {-1, 3}, - {1, -3}, {-1, -3}, {2, 3}, {-2, 3}, {2, -3}, {-2, -3}, - {3, 3}, {-3, 3}, {3, -3}, {-3, -3}, {4, 2}, {-4, 2}, - {4, -2}, {-4, -2}, {5, 2}, {-5, 2}, {5, -2}, {-5, -2}, - {6, 2}, {-6, 2}, {6, -2}, {-6, -2}, {7, 1}, {-7, 1}, - {7, -1}, {-7, -1}, {7, 0}, {-7, 0}, {7, 0}, {-7, 0} - }; - - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); - } - - @Test - public void testVerticalEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); - int[][] expectedArray = { {0, 7}, {0, 7}, {0, -7}, {0, -7}, {1, 7}, {-1, 7}, {1, -7}, {-1, -7},{2, 6}, - {-2, 6}, {2, -6}, {-2, -6}, {2, 5}, {-2, 5}, {2, -5}, {-2, -5}, {2, 4}, {-2, 4}, - {2, -4}, {-2, -4}, {3, 3}, {-3, 3}, {3, -3}, {-3, -3}, {3, 2}, {-3, 2}, {3, -2}, - {-3, -2}, {3, 1}, {-3, 1}, {3, -1}, {-3, -1}, {3, 0}, {-3, 0}, {3, 0}, {-3, 0} - }; - - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); - } - - @Test - public void testCenteredEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); - int[][] expectedArray = { {0, 2}, {0, 2}, {0, -2}, {0, -2}, {1, 2}, {-1, 2}, {1, -2}, {-1, -2}, - {2, 1}, {-2, 1}, {2, -1}, {-2, -1}, {3, 0}, {-3, 0}, {3, 0}, {-3, 0} - }; - for (int[] is : pixels) { - System.out.println(Arrays.toString(is)); + + @Test + public void testHorizontalEllipse() { + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); + + int[][] expectedArray = { + // Quadrant 1 + {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 2}, {5, 2}, {6, 2}, {7, 1}, + // Quadrant 2 + {7, 0}, {7, -1}, {6, -2}, {5, -2}, {4, -2}, {3, -3}, {2, -3}, {1, -3}, {0, -3}, + // Quadrant 3 + {-1, -3}, {-2, -3}, {-3, -3}, {-4, -2}, {-5, -2}, {-6, -2}, {-7, -1}, + // Quadrant 4 + {-7, 0}, {-7, 1}, {-6, 2}, {-5, 2}, {-4, 2}, {-3, 3}, {-2, 3}, {-1, 3} + }; + + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); - } + @Test + public void testVerticalEllipse() { + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); + + int[][] expectedArray = { + // Quadrant 1 + {0, 7}, {1, 7}, {2, 6}, {3, 5}, {3, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, + // Quadrant 2 + {3, -1}, {3, -2}, {3, -3}, {3, -4}, {3, -5}, {2, -6}, {1, -7}, {0, -7}, + // Quadrant 3 + {-1, -7}, {-2, -6}, {-3, -5}, {-3, -4}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0}, + // Quadrant 4 + {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4}, {-3, 5}, {-2, 6}, {-1, 7} + }; + + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + } + + @Test + public void testCenteredEllipse() { + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); + + int[][] expectedArray = { + // Quadrant 1 + {0, 2}, {1, 2}, {2, 1}, + // Quadrant 2 + {3, 0}, {2, -1}, {1, -2}, + // Quadrant 3 + {0, -2}, {-1, -2}, {-2, -1}, + // Quadrant 4 + {-3, 0}, {-2, 1}, {-1, 2} + }; + + assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + } } From 96f5dee2eeaceb501939c8357568d21b4e036c08 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sat, 8 Jun 2024 15:03:16 +0400 Subject: [PATCH 6/8] remove HashSet from EllipseAlgorithm, replaced with manual duplicate checking in addPixel method; update EllipseAlgorithmTest to use TestHelper.assert2DArrayEquals --- .../creme332/algorithms/EllipseAlgorithm.java | 29 +++++++++---------- .../algorithms/EllipseAlgorithmTest.java | 18 +++++++----- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index 73e271ae..e3660c0f 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -1,9 +1,7 @@ package com.github.creme332.algorithms; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class EllipseAlgorithm { @@ -23,7 +21,6 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) } List pixels = new ArrayList<>(); - Set pixelSet = new HashSet<>(); int x = 0; int y = ry; @@ -40,7 +37,7 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) // Region 1 while (px < py) { - addPixels(pixels, pixelSet, centerX, centerY, x, y); + addPixels(pixels, centerX, centerY, x, y); x++; px += twory2; if (p < 0) { @@ -55,7 +52,7 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) // Region 2 p = (int) (ry2 * (x + 0.5) * (x + 0.5) + rx2 * (y - 1) * (y - 1) - rx2 * ry2); while (y >= 0) { - addPixels(pixels, pixelSet, centerX, centerY, x, y); + addPixels(pixels, centerX, centerY, x, y); y--; py -= tworx2; if (p > 0) { @@ -69,18 +66,20 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) return pixels; } - private static void addPixels(List pixels, Set pixelSet, int centerX, int centerY, int x, int y) { - addPixel(pixels, pixelSet, centerX + x, centerY + y); - addPixel(pixels, pixelSet, centerX - x, centerY + y); - addPixel(pixels, pixelSet, centerX + x, centerY - y); - addPixel(pixels, pixelSet, centerX - x, centerY - y); + private static void addPixels(List pixels, int centerX, int centerY, int x, int y) { + addPixel(pixels, centerX + x, centerY + y); + addPixel(pixels, centerX - x, centerY + y); + addPixel(pixels, centerX + x, centerY - y); + addPixel(pixels, centerX - x, centerY - y); } - private static void addPixel(List pixels, Set pixelSet, int x, int y) { - String key = x + "," + y; - if (!pixelSet.contains(key)) { - pixels.add(new int[]{x, y}); - pixelSet.add(key); + private static void addPixel(List pixels, int x, int y) { + // Check if the pixel already exists in the list + for (int[] pixel : pixels) { + if (pixel[0] == x && pixel[1] == y) { + return; + } } + pixels.add(new int[]{x, y}); } } 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 d56db897..896ff5da 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,9 @@ import org.junit.Test; import static org.junit.Assert.*; import com.github.creme332.algorithms.EllipseAlgorithm; +import com.github.creme332.tests.utils.TestHelper; +import java.util.Arrays; import java.util.List; public class EllipseAlgorithmTest { @@ -23,7 +25,7 @@ public void testValidEllipse() { {-8, 1}, {-8, 2}, {-7, 3}, {-6, 4}, {-5, 5}, {-4, 5}, {-3, 6}, {-2, 6}, {-1, 6} }; - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test @@ -61,7 +63,7 @@ public void testHorizontalEllipse() { {-7, 0}, {-7, 1}, {-6, 2}, {-5, 2}, {-4, 2}, {-3, 3}, {-2, 3}, {-1, 3} }; - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test @@ -70,16 +72,16 @@ public void testVerticalEllipse() { int[][] expectedArray = { // Quadrant 1 - {0, 7}, {1, 7}, {2, 6}, {3, 5}, {3, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, + {0, 7}, {1, 7}, {2, 6}, {2, 5}, {2, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, // Quadrant 2 - {3, -1}, {3, -2}, {3, -3}, {3, -4}, {3, -5}, {2, -6}, {1, -7}, {0, -7}, + {3, -1}, {3, -2}, {3, -3}, {2, -4}, {2, -5}, {2, -6}, {1, -7}, {0, -7}, // Quadrant 3 - {-1, -7}, {-2, -6}, {-3, -5}, {-3, -4}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0}, + {-1, -7}, {-2, -6}, {-2, -5}, {-2, -4}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0}, // Quadrant 4 - {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4}, {-3, 5}, {-2, 6}, {-1, 7} + {-3, 1}, {-3, 2}, {-3, 3}, {-2, 4}, {-2, 5}, {-2, 6}, {-1, 7} }; - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test @@ -97,6 +99,6 @@ public void testCenteredEllipse() { {-3, 0}, {-2, 1}, {-1, 2} }; - assertArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } } From be1da82d5080925896e2d50623f3edb587e47790 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:33:41 +0400 Subject: [PATCH 7/8] optimize addPixels by removing loop, add private constructor --- .../creme332/algorithms/EllipseAlgorithm.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java index e3660c0f..6d2ce71e 100644 --- a/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java +++ b/src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java @@ -5,14 +5,20 @@ public class EllipseAlgorithm { + private EllipseAlgorithm() { + + } + /** - * This method draws an ellipse using the Midpoint Ellipse Algorithm. + * This method calculates the pixel coordinates of an ellipse using the Midpoint + * Ellipse Algorithm. * * @param centerX X-coordinate of the ellipse's center * @param centerY Y-coordinate of the ellipse's center * @param rx Radius of the ellipse along the X-axis (horizontal radius) * @param ry Radius of the ellipse along the Y-axis (vertical radius) - * @return A list of integer arrays representing the pixels of the ellipse + * @return A list of integer arrays representing the x-y coordinates of each + * 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) { @@ -67,19 +73,31 @@ public static List drawEllipse(int centerX, int centerY, int rx, int ry) } private static void addPixels(List pixels, int centerX, int centerY, int x, int y) { + + // when x = 0, (-x, y)= (x, y) and (-x, -y) = (x, -y) + // plot only pixels in 2 quadrants + if (x == 0) { + addPixel(pixels, centerX + x, centerY + y); + addPixel(pixels, centerX + x, centerY - y); + return; + } + + // when y = 0, (x, -y)= (x, y) and (-x, y) = (-x, y) + // plot only pixels in 2 quadrants + if (y == 0) { + addPixel(pixels, centerX + x, centerY + y); + addPixel(pixels, centerX - x, centerY + y); + return; + } + + // else plot a pixel in each quadrant addPixel(pixels, centerX + x, centerY + y); - addPixel(pixels, centerX - x, centerY + y); addPixel(pixels, centerX + x, centerY - y); + addPixel(pixels, centerX - x, centerY + y); addPixel(pixels, centerX - x, centerY - y); } private static void addPixel(List pixels, int x, int y) { - // Check if the pixel already exists in the list - for (int[] pixel : pixels) { - if (pixel[0] == x && pixel[1] == y) { - return; - } - } - pixels.add(new int[]{x, y}); + pixels.add(new int[] { x, y }); } } From 72f3aa09d50863f81d9171725922f86b7e63874a Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:34:15 +0400 Subject: [PATCH 8/8] add test where ellipse is not centered at origin, format file --- .../algorithms/EllipseAlgorithmTest.java | 103 ++++++++++-------- 1 file changed, 57 insertions(+), 46 deletions(-) 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 896ff5da..9cceb404 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/EllipseAlgorithmTest.java @@ -5,26 +5,54 @@ import com.github.creme332.algorithms.EllipseAlgorithm; import com.github.creme332.tests.utils.TestHelper; -import java.util.Arrays; import java.util.List; public class EllipseAlgorithmTest { @Test - public void testValidEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 8, 6); + public void testEllipseCenteredAtOrigin() { + List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); + + int[][] expectedArray = { + // Quadrant 1 + { 0, 2 }, { 1, 2 }, { 2, 1 }, + // Quadrant 2 + { 3, 0 }, { 2, -1 }, { 1, -2 }, + // Quadrant 3 + { 0, -2 }, { -1, -2 }, { -2, -1 }, + // Quadrant 4 + { -3, 0 }, { -2, 1 }, { -1, 2 } + }; + + TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); + } + + @Test + public void testEllipseNotAtOrigin() { + int centerX = 5; + int centerY = 7; + // coordinates if ellipse with rx = 8 and ry = 6 was centered at origin int[][] expectedArray = { - // Quadrant 1 - {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 5}, {5, 5}, {6, 4}, {7, 3}, {8, 2}, {8, 1}, {8, 0}, - // Quadrant 2 - {8, -1}, {8, -2}, {7, -3}, {6, -4}, {5, -5}, {4, -5}, {3, -6}, {2, -6}, {1, -6}, {0, -6}, - // Quadrant 3 - {-1, -6}, {-2, -6}, {-3, -6}, {-4, -5}, {-5, -5}, {-6, -4}, {-7, -3}, {-8, -2}, {-8, -1}, {-8, 0}, - // Quadrant 4 - {-8, 1}, {-8, 2}, {-7, 3}, {-6, 4}, {-5, 5}, {-4, 5}, {-3, 6}, {-2, 6}, {-1, 6} + // Quadrant 1 + { 0, 6 }, { 1, 6 }, { 2, 6 }, { 3, 6 }, { 4, 5 }, { 5, 5 }, { 6, 4 }, { 7, 3 }, { 8, 2 }, { 8, 1 }, + { 8, 0 }, + // Quadrant 2 + { 8, -1 }, { 8, -2 }, { 7, -3 }, { 6, -4 }, { 5, -5 }, { 4, -5 }, { 3, -6 }, { 2, -6 }, { 1, -6 }, + { 0, -6 }, + // Quadrant 3 + { -1, -6 }, { -2, -6 }, { -3, -6 }, { -4, -5 }, { -5, -5 }, { -6, -4 }, { -7, -3 }, { -8, -2 }, + { -8, -1 }, { -8, 0 }, + // Quadrant 4 + { -8, 1 }, { -8, 2 }, { -7, 3 }, { -6, 4 }, { -5, 5 }, { -4, 5 }, { -3, 6 }, { -2, 6 }, { -1, 6 } }; + // perform translation + for (int i = 0; i < expectedArray.length; i++) { + expectedArray[i][0] += centerX; + expectedArray[i][1] += centerY; + } + List pixels = EllipseAlgorithm.drawEllipse(centerX, centerY, 8, 6); TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @@ -49,56 +77,39 @@ public void testNegativeRadii() { } @Test - public void testHorizontalEllipse() { + public void testHorizontalEllipseAtOrigin() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 7, 3); int[][] expectedArray = { - // Quadrant 1 - {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 2}, {5, 2}, {6, 2}, {7, 1}, - // Quadrant 2 - {7, 0}, {7, -1}, {6, -2}, {5, -2}, {4, -2}, {3, -3}, {2, -3}, {1, -3}, {0, -3}, - // Quadrant 3 - {-1, -3}, {-2, -3}, {-3, -3}, {-4, -2}, {-5, -2}, {-6, -2}, {-7, -1}, - // Quadrant 4 - {-7, 0}, {-7, 1}, {-6, 2}, {-5, 2}, {-4, 2}, {-3, 3}, {-2, 3}, {-1, 3} + // Quadrant 1 + { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 2 }, { 5, 2 }, { 6, 2 }, { 7, 1 }, + // Quadrant 2 + { 7, 0 }, { 7, -1 }, { 6, -2 }, { 5, -2 }, { 4, -2 }, { 3, -3 }, { 2, -3 }, { 1, -3 }, { 0, -3 }, + // Quadrant 3 + { -1, -3 }, { -2, -3 }, { -3, -3 }, { -4, -2 }, { -5, -2 }, { -6, -2 }, { -7, -1 }, + // Quadrant 4 + { -7, 0 }, { -7, 1 }, { -6, 2 }, { -5, 2 }, { -4, 2 }, { -3, 3 }, { -2, 3 }, { -1, 3 } }; TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } @Test - public void testVerticalEllipse() { + public void testVerticalEllipseAtOrigin() { List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 7); int[][] expectedArray = { - // Quadrant 1 - {0, 7}, {1, 7}, {2, 6}, {2, 5}, {2, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, - // Quadrant 2 - {3, -1}, {3, -2}, {3, -3}, {2, -4}, {2, -5}, {2, -6}, {1, -7}, {0, -7}, - // Quadrant 3 - {-1, -7}, {-2, -6}, {-2, -5}, {-2, -4}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0}, - // Quadrant 4 - {-3, 1}, {-3, 2}, {-3, 3}, {-2, 4}, {-2, 5}, {-2, 6}, {-1, 7} + // Quadrant 1 + { 0, 7 }, { 1, 7 }, { 2, 6 }, { 2, 5 }, { 2, 4 }, { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 }, + // Quadrant 2 + { 3, -1 }, { 3, -2 }, { 3, -3 }, { 2, -4 }, { 2, -5 }, { 2, -6 }, { 1, -7 }, { 0, -7 }, + // Quadrant 3 + { -1, -7 }, { -2, -6 }, { -2, -5 }, { -2, -4 }, { -3, -3 }, { -3, -2 }, { -3, -1 }, { -3, 0 }, + // Quadrant 4 + { -3, 1 }, { -3, 2 }, { -3, 3 }, { -2, 4 }, { -2, 5 }, { -2, 6 }, { -1, 7 } }; TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); } - @Test - public void testCenteredEllipse() { - List pixels = EllipseAlgorithm.drawEllipse(0, 0, 3, 2); - - int[][] expectedArray = { - // Quadrant 1 - {0, 2}, {1, 2}, {2, 1}, - // Quadrant 2 - {3, 0}, {2, -1}, {1, -2}, - // Quadrant 3 - {0, -2}, {-1, -2}, {-2, -1}, - // Quadrant 4 - {-3, 0}, {-2, 1}, {-1, 2} - }; - - TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][])); - } }