Skip to content

Commit

Permalink
Merge pull request #37 from Divyesh000/ellipse
Browse files Browse the repository at this point in the history
implementation of midpoint ellipse algorithm and tests for the algorithm
  • Loading branch information
creme332 authored Jun 10, 2024
2 parents 9b1e28d + 72f3aa0 commit f427367
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 0 deletions.
103 changes: 103 additions & 0 deletions src/main/java/com/github/creme332/algorithms/EllipseAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.github.creme332.algorithms;

import java.util.ArrayList;
import java.util.List;

public class EllipseAlgorithm {

private EllipseAlgorithm() {

}

/**
* 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 x-y coordinates of each
* pixel of the ellipse
* @throws IllegalArgumentException if rx or ry are non-positive
*/
public static List<int[]> drawEllipse(int centerX, int centerY, int rx, int ry) {
if (rx <= 0 || ry <= 0) {
throw new IllegalArgumentException("Radii must be positive values.");
}

List<int[]> pixels = 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) {
addPixels(pixels, centerX, centerY, x, 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) {
addPixels(pixels, centerX, centerY, x, y);
y--;
py -= tworx2;
if (p > 0) {
p += rx2 - py;
} else {
x++;
px += twory2;
p += rx2 - py + px;
}
}
return pixels;
}

private static void addPixels(List<int[]> 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);
}

private static void addPixel(List<int[]> pixels, int x, int y) {
pixels.add(new int[] { x, y });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.github.creme332.tests.algorithms;

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.List;

public class EllipseAlgorithmTest {

@Test
public void testEllipseCenteredAtOrigin() {
List<int[]> 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 }
};
// perform translation
for (int i = 0; i < expectedArray.length; i++) {
expectedArray[i][0] += centerX;
expectedArray[i][1] += centerY;
}

List<int[]> pixels = EllipseAlgorithm.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);
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 testHorizontalEllipseAtOrigin() {
List<int[]> 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 }
};

TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][]));
}

@Test
public void testVerticalEllipseAtOrigin() {
List<int[]> 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 }
};

TestHelper.assert2DArrayEquals(expectedArray, pixels.toArray(new int[pixels.size()][]));
}

}

0 comments on commit f427367

Please sign in to comment.