From 70e6f5fcb80905a404d37b36c9fcc432175b18ef Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Wed, 16 Oct 2024 12:44:09 +0545 Subject: [PATCH 1/5] feat: add midpoint ellipse algorithm --- .../geometry/MidpointEllipse.java | 130 ++++++++++++++++++ .../geometry/MidpointEllipseTest.java | 91 ++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 src/main/java/com/thealgorithms/geometry/MidpointEllipse.java create mode 100644 src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java diff --git a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java new file mode 100644 index 000000000000..1a267b60b520 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java @@ -0,0 +1,130 @@ +package com.thealgorithms.geometry; + +import java.util.ArrayList; +import java.util.List; + +/** + * The MidpointEllipse class implements the Midpoint Ellipse Drawing Algorithm. + * This algorithm efficiently computes the points on an ellipse by dividing it into two regions + * and using decision parameters to determine the next point to plot. + */ +public final class MidpointEllipse { + + private MidpointEllipse() { + // Private constructor to prevent instantiation + } + + /** + * Draws an ellipse using the Midpoint Ellipse Algorithm. + * + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis (horizontal radius) + * @param b the length of the semi-minor axis (vertical radius) + * @return a list of points (represented as int arrays) that form the ellipse + */ + public static List drawEllipse(int centerX, int centerY, int a, int b) { + List points = new ArrayList<>(); + + // Handle degenerate cases with early returns + if (a == 0 && b == 0) { + points.add(new int[] {centerX, centerY}); // Only the center point + return points; + } + + if (a == 0) { + // Semi-major axis is zero, create a vertical line + for (int y = centerY - b; y <= centerY + b; y++) { + points.add(new int[] {centerX, y}); + } + return points; // Early return + } + + if (b == 0) { + // Semi-minor axis is zero, create a horizontal line + for (int x = centerX - a; x <= centerX + a; x++) { + points.add(new int[] {x, centerY}); + } + return points; // Early return + } + + // Normal case: Non-degenerate ellipse + computeEllipsePoints(points, centerX, centerY, a, b); + + return points; // Return all calculated points of the ellipse + } + + /** + * Computes points of a non-degenerate ellipse using the Midpoint Ellipse Algorithm. + * + * @param points the list to which points will be added + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis (horizontal radius) + * @param b the length of the semi-minor axis (vertical radius) + */ + private static void computeEllipsePoints(List points, int centerX, int centerY, int a, int b) { + int x = 0; // Initial x-coordinate + int y = b; // Initial y-coordinate + + // Region 1: Initial decision parameter + double d1 = (b * b) - (a * a * b) + (0.25 * a * a); // Decision variable for region 1 + double dx = 2.0 * b * b * x; // Change in x + double dy = 2.0 * a * a * y; // Change in y + + // Region 1: When the slope is less than 1 + while (dx < dy) { + addEllipsePoints(points, centerX, centerY, x, y); + + // Update decision parameter and variables + if (d1 < 0) { + x++; + dx += (2 * b * b); // Update x change + d1 += dx + (b * b); // Update decision parameter + } else { + x++; + y--; + dx += (2 * b * b); // Update x change + dy -= (2 * a * a); // Update y change + d1 += dx - dy + (b * b); // Update decision parameter + } + } + + // Region 2: Initial decision parameter for the second region + double d2 = ((b * b) * ((x + 0.5) * (x + 0.5))) + ((a * a) * ((y - 1) * (y - 1))) - (a * a * b * b); + + // Region 2: When the slope is greater than or equal to 1 + while (y >= 0) { + addEllipsePoints(points, centerX, centerY, x, y); + + // Update decision parameter and variables + if (d2 > 0) { + y--; + dy -= (2 * a * a); // Update y change + d2 += (a * a) - dy; // Update decision parameter + } else { + y--; + x++; + dx += (2 * b * b); // Update x change + dy -= (2 * a * a); // Update y change + d2 += dx - dy + (a * a); // Update decision parameter + } + } + } + + /** + * Adds points for all four quadrants of the ellipse based on symmetry. + * + * @param points the list to which points will be added + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param x the x-coordinate relative to the center + * @param y the y-coordinate relative to the center + */ + private static void addEllipsePoints(List points, int centerX, int centerY, int x, int y) { + points.add(new int[] {centerX + x, centerY + y}); + points.add(new int[] {centerX - x, centerY + y}); + points.add(new int[] {centerX + x, centerY - y}); + points.add(new int[] {centerX - x, centerY - y}); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java new file mode 100644 index 000000000000..e32a971b2fac --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java @@ -0,0 +1,91 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class MidpointEllipseTest { + + /** + * Provides test cases for the drawEllipse method. + * Each argument contains: centerX, centerY, a, b, and expected points. + * + * @return a stream of arguments for parameterized testing + */ + static Stream ellipseTestProvider() { + return Stream.of( + Arguments.of(0, 0, 5, 3, new int[][] {{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, 2}, {-3, 2}, {3, -2}, {-3, -2}, {4, 2}, {-4, 2}, {4, -2}, {-4, -2}, {5, 1}, {-5, 1}, {5, -1}, {-5, -1}, {5, 0}, {-5, 0}, {5, 0}, {-5, 0}}), + Arguments.of(0, 0, 0, 5, + new int[][] { + {0, -5}, {0, -4}, {0, -3}, {0, -2}, {0, -1}, {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5} // Only vertical line points and center + }), + Arguments.of(0, 0, 5, 0, + new int[][] { + {-5, 0}, {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0} // Only horizontal line points and center + }), + Arguments.of(0, 0, 0, 0, + new int[][] { + {0, 0} // Only center point + }), + Arguments.of(0, 0, 4, 4, + new int[][] { + {0, 4}, + {0, 4}, + {0, -4}, + {0, -4}, + {1, 4}, + {-1, 4}, + {1, -4}, + {-1, -4}, + {2, 3}, + {-2, 3}, + {2, -3}, + {-2, -3}, + {3, 3}, + {-3, 3}, + {3, -3}, + {-3, -3}, + {3, 2}, + {-3, 2}, + {3, -2}, + {-3, -2}, + {4, 1}, + {-4, 1}, + {4, -1}, + {-4, -1}, + {4, 0}, + {-4, 0}, + {4, 0}, + {-4, 0}, + })); + } + + /** + * Tests the drawEllipse method with various parameters. + * + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis + * @param b the length of the semi-minor axis + * @param expectedPoints the expected points forming the ellipse + */ + @ParameterizedTest + @MethodSource("ellipseTestProvider") + @DisplayName("Test drawing ellipses with various parameters") + void testDrawEllipse(int centerX, int centerY, int a, int b, int[][] expectedPoints) { + List points = MidpointEllipse.drawEllipse(centerX, centerY, a, b); + + // Validate the number of points and the specific points + assertEquals(expectedPoints.length, points.size(), "Number of points should match expected."); + + for (int i = 0; i < expectedPoints.length; i++) { + assertArrayEquals(expectedPoints[i], points.get(i), "Point mismatch at index " + i); + } + } +} From 03eda2c2e4dc278c272f8d6bfd603502c2264cfe Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Wed, 16 Oct 2024 12:48:09 +0545 Subject: [PATCH 2/5] fix: change list to collection --- src/main/java/com/thealgorithms/geometry/MidpointEllipse.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java index 1a267b60b520..990a3f315d17 100644 --- a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java +++ b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java @@ -1,6 +1,7 @@ package com.thealgorithms.geometry; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -121,7 +122,7 @@ private static void computeEllipsePoints(List points, int centerX, int ce * @param x the x-coordinate relative to the center * @param y the y-coordinate relative to the center */ - private static void addEllipsePoints(List points, int centerX, int centerY, int x, int y) { + private static void addEllipsePoints(Collection points, int centerX, int centerY, int x, int y) { points.add(new int[] {centerX + x, centerY + y}); points.add(new int[] {centerX - x, centerY + y}); points.add(new int[] {centerX + x, centerY - y}); From ea26be490619e613b7368bad8f5d78a6f44f589d Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Wed, 16 Oct 2024 12:52:00 +0545 Subject: [PATCH 3/5] fix: change List to Collection in compute --- src/main/java/com/thealgorithms/geometry/MidpointEllipse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java index 990a3f315d17..08ce1f5d37a8 100644 --- a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java +++ b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java @@ -64,7 +64,7 @@ public static List drawEllipse(int centerX, int centerY, int a, int b) { * @param a the length of the semi-major axis (horizontal radius) * @param b the length of the semi-minor axis (vertical radius) */ - private static void computeEllipsePoints(List points, int centerX, int centerY, int a, int b) { + private static void computeEllipsePoints(Collection points, int centerX, int centerY, int a, int b) { int x = 0; // Initial x-coordinate int y = b; // Initial y-coordinate From e68124ff9a50fe7150ee6fce98166b057ba42d1f Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Wed, 16 Oct 2024 12:56:12 +0545 Subject: [PATCH 4/5] fix: remove useless parentheses --- src/main/java/com/thealgorithms/geometry/MidpointEllipse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java index 08ce1f5d37a8..fbd53c679960 100644 --- a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java +++ b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java @@ -92,7 +92,7 @@ private static void computeEllipsePoints(Collection points, int centerX, } // Region 2: Initial decision parameter for the second region - double d2 = ((b * b) * ((x + 0.5) * (x + 0.5))) + ((a * a) * ((y - 1) * (y - 1))) - (a * a * b * b); + double d2 = b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b; // Region 2: When the slope is greater than or equal to 1 while (y >= 0) { From 8cf0d9cfcebd90c482ae80f12069151e5ca7b843 Mon Sep 17 00:00:00 2001 From: saahil-mahato Date: Wed, 16 Oct 2024 12:58:49 +0545 Subject: [PATCH 5/5] doc: document test class --- .../com/thealgorithms/geometry/MidpointEllipseTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java index e32a971b2fac..9d03909c60ca 100644 --- a/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java +++ b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java @@ -10,6 +10,14 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +/** + * The {@code MidpointEllipseTest} class contains unit tests for the + * {@code MidpointEllipse} class, specifically testing the + * {@code drawEllipse} method. + * + *

This class uses parameterized tests to validate the output of + * Midpoint Ellipse algorithm for various input points.

+ */ class MidpointEllipseTest { /**