From 3d99658ac97e8a3a838ca4a0ada7edf3673d791a Mon Sep 17 00:00:00 2001 From: triya Date: Sun, 9 Jun 2024 17:17:52 +0300 Subject: [PATCH 01/12] 2 changes.No test yet --- src/main/algorithms/LineAlgorithm.java | 8 ++++++++ src/test/algorithms/LineAlgorithmTest.java | 3 +++ 2 files changed, 11 insertions(+) create mode 100644 src/main/algorithms/LineAlgorithm.java create mode 100644 src/test/algorithms/LineAlgorithmTest.java diff --git a/src/main/algorithms/LineAlgorithm.java b/src/main/algorithms/LineAlgorithm.java new file mode 100644 index 00000000..371757a3 --- /dev/null +++ b/src/main/algorithms/LineAlgorithm.java @@ -0,0 +1,8 @@ +public class LineAlgorithm { + public static int[][] drawDDA(int x0, int y0, int x1, int y1) { + return new int[0][0]; + } + public static int[][] drawBresenham(int x0, int y0, int x1, int y1) { + return new int[0][0]; + } +} \ No newline at end of file diff --git a/src/test/algorithms/LineAlgorithmTest.java b/src/test/algorithms/LineAlgorithmTest.java new file mode 100644 index 00000000..fc13bef4 --- /dev/null +++ b/src/test/algorithms/LineAlgorithmTest.java @@ -0,0 +1,3 @@ +public class LineAlgorithmTest { + +} From c6e06220215efc9db8acb485d6af31ea40e1c098 Mon Sep 17 00:00:00 2001 From: triya Date: Thu, 4 Jul 2024 22:27:46 +0400 Subject: [PATCH 02/12] Reworked --- src/main/algorithms/LineAlgorithm.java | 8 - .../creme332/algorithms/LineCalculator.java | 25 ++- src/test/algorithms/LineAlgorithmTest.java | 3 - .../tests/algorithms/LineCalculatorTest.java | 210 ++++++++++++++++-- 4 files changed, 214 insertions(+), 32 deletions(-) delete mode 100644 src/main/algorithms/LineAlgorithm.java delete mode 100644 src/test/algorithms/LineAlgorithmTest.java diff --git a/src/main/algorithms/LineAlgorithm.java b/src/main/algorithms/LineAlgorithm.java deleted file mode 100644 index 371757a3..00000000 --- a/src/main/algorithms/LineAlgorithm.java +++ /dev/null @@ -1,8 +0,0 @@ -public class LineAlgorithm { - public static int[][] drawDDA(int x0, int y0, int x1, int y1) { - return new int[0][0]; - } - public static int[][] drawBresenham(int x0, int y0, int x1, int y1) { - return new int[0][0]; - } -} \ No newline at end of file diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index 863f92d6..e43f1d89 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -19,8 +19,8 @@ public static int[][] dda(int x0, int y0, int x1, int y1) { int[][] pixelCoords = new int[steps + 1][2]; for (int i = 0; i <= steps; i++) { - pixelCoords[i][0] = Math.round(x); - pixelCoords[i][1] = Math.round(y); + pixelCoords[i][0]= Math.round(x); + pixelCoords[i][1]= Math.round(y); x += xInc; y += yInc; } @@ -32,17 +32,28 @@ public static int[][] bresenham(int x0, int y0, int x1, int y1) { int dx = Math.abs(x1 - x0); int dy = Math.abs(y1 - y0); - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; + int sx; + if (x0 < x1) { + sx = 1; + } else { + sx = -1; + } + + int sy; + if (y0 < y1) { + sy = 1; + } else { + sy = -1; + } int err = dx - dy; - int[][] pixelCoords = new int[dx + dy + 1][2]; + int[][] pixelCoords = new int [dx + dy + 1][2]; int index = 0; while (true) { - pixelCoords[index][0] = x0; - pixelCoords[index][1] = y0; + pixelCoords [index][0] = x0; + pixelCoords[index][1]= y0; index++; if (x0 == x1 && y0 == y1) diff --git a/src/test/algorithms/LineAlgorithmTest.java b/src/test/algorithms/LineAlgorithmTest.java deleted file mode 100644 index fc13bef4..00000000 --- a/src/test/algorithms/LineAlgorithmTest.java +++ /dev/null @@ -1,3 +0,0 @@ -public class LineAlgorithmTest { - -} diff --git a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java index 257cf063..1ca2dbfa 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java @@ -1,6 +1,4 @@ package com.github.creme332.tests.algorithms; - -import org.junit.Ignore; import org.junit.Test; import com.github.creme332.algorithms.LineCalculator; @@ -8,24 +6,39 @@ public class LineCalculatorTest { - @Ignore("Failing test to be fixed later") + @Test public void testDrawDDA() { int x0 = 2, y0 = 3, x1 = 10, y1 = 8; int[][] expected = { - { 2, 3 }, { 3, 3 }, { 4, 4 }, { 5, 4 }, { 6, 5 }, { 7, 5 }, { 8, 6 }, { 9, 7 }, { 10, 8 } + {2, 3}, + {3, 3}, + {4, 4}, + {5, 4}, + {6, 5}, + {7, 5}, + {8, 6}, + {9, 7}, + {10, 8} }; int[][] result = LineCalculator.dda(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); } - @Ignore("Failing test to be fixed later") + @Test public void testDrawBresenham() { int x0 = 2, y0 = 3, x1 = 10, y1 = 8; int[][] expected = { - { 2, 3 }, { 3, 3 }, { 4, 4 }, { 5, 4 }, { 6, 5 }, { 7, 5 }, { 8, 6 }, { 9, 7 }, { 10, 8 } + {2, 3}, + {3, 3}, + {4, 4}, + {5, 4}, + {6, 5}, + {7, 5}, + {8, 6}, + {9, 7}, + {10, 8} }; - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); } @@ -34,8 +47,12 @@ public void testDrawBresenham() { public void testDrawDDAHorizontal() { int x0 = 1, y0 = 1, x1 = 5, y1 = 1; int[][] expected = { - { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 } - }; + {1, 1}, + {2, 1}, + {3, 1}, + {4, 1}, + {5, 1} + }; int[][] result = LineCalculator.dda(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); @@ -45,8 +62,12 @@ public void testDrawDDAHorizontal() { public void testDrawBresenhamHorizontal() { int x0 = 1, y0 = 1, x1 = 5, y1 = 1; int[][] expected = { - { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 } - }; + {1, 1}, + {2, 1}, + {3, 1}, + {4, 1}, + {5, 1} + }; int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); @@ -56,8 +77,12 @@ public void testDrawBresenhamHorizontal() { public void testDrawDDAVertical() { int x0 = 1, y0 = 1, x1 = 1, y1 = 5; int[][] expected = { - { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 } - }; + {1, 1}, + {1, 2}, + {1, 3}, + {1, 4}, + {1, 5} + }; int[][] result = LineCalculator.dda(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); @@ -67,10 +92,167 @@ public void testDrawDDAVertical() { public void testDrawBresenhamVertical() { int x0 = 1, y0 = 1, x1 = 1, y1 = 5; int[][] expected = { - { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 } + {1, 1}, + {1, 2}, + {1, 3}, + {1, 4}, + {1, 5} + }; + + int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawDDANegativeGradient() { + int x0 = 10, y0 = 10, x1 = 2, y1 = 3; + int[][] expected = { + {10, 10}, + {9, 9}, + {8, 8}, + {7, 7}, + {6, 6}, + {5, 5}, + {4, 4}, + {3, 3}, + {2, 3} + }; + + int[][] result = LineCalculator.dda(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawBresenhamNegativeGradient() { + int x0 = 10, y0 = 10, x1 = 2, y1 = 3; + int[][] expected = { + {10, 10}, + {9, 9}, + {8, 8}, + {7, 7}, + {6, 6}, + {5, 5}, + {4, 4}, + {3, 3}, + {2, 3} + }; + + int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawDDASecondQuadrant() { + int x0 = -2, y0 = 3, x1 = -10, y1 = 8; + int[][] expected = { + {-2, 3}, + {-3, 3}, + {-4, 4}, + {-5, 4}, + {-6, 5}, + {-7, 5}, + {-8, 6}, + {-9, 7}, + {-10, 8} + }; + + int[][] result = LineCalculator.dda(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawBresenhamSecondQuadrant() { + int x0 = -2, y0 = 3, x1 = -10, y1 = 8; + int[][] expected = { + {-2, 3}, + {-3, 3}, + {-4, 4}, + {-5, 4}, + {-6, 5}, + {-7, 5}, + {-8, 6}, + {-9, 7}, + {-10, 8} + }; + + int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawDDAThirdQuadrant() { + int x0 = -2, y0 = -3, x1 = -10, y1 = -8; + int[][] expected = { + {-2, -3}, + {-3, -3}, + {-4, -4}, + {-5, -4}, + {-6, -5}, + {-7, -5}, + {-8, -6}, + {-9, -7}, + {-10, -8} + }; + + int[][] result = LineCalculator.dda(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawBresenhamThirdQuadrant() { + int x0 = -2, y0 = -3, x1 = -10, y1 = -8; + int[][] expected = { + {-2, -3}, + {-3, -3}, + {-4, -4}, + {-5, -4}, + {-6, -5}, + {-7, -5}, + {-8, -6}, + {-9, -7}, + {-10, -8} + }; + + int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawDDAFourthQuadrant() { + int x0 = 2, y0 = -3, x1 = 10, y1 = -8; + int[][] expected = { + {2, -3}, + {3, -4}, + {4, -4}, + {5, -5}, + {6, -5}, + {7, -6}, + {8, -6}, + {9, -7}, + {10, -8} + }; + + int[][] result = LineCalculator.dda(x0, y0, x1, y1); + TestHelper.assert2DArrayEquals(expected, result); + } + + @Test + public void testDrawBresenhamFourthQuadrant() { + int x0 = 2, y0 = -3, x1 = 10, y1 = -8; + int[][] expected = { + {2, -3}, + {3, -4}, + {4, -4}, + {5, -5}, + {6, -5}, + {7, -6}, + {8, -6}, + {9, -7}, + {10, -8} }; int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); TestHelper.assert2DArrayEquals(expected, result); } } + From 029681fba43d841706e4ca0204902b1a3d739f3f Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 5 Jul 2024 18:48:25 +0400 Subject: [PATCH 03/12] fix return values of functions --- .../creme332/algorithms/LineCalculator.java | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index e43f1d89..2eb1a0e6 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -1,10 +1,24 @@ package com.github.creme332.algorithms; +import java.util.ArrayList; +import java.util.List; + public class LineCalculator { private LineCalculator() { } + /** + * Calculates pixels between any 2 points (x0, y0) and (x1, y1) using the DDA + * line algorithm. + * + * @param x0 x-coordinate of start point + * @param y0 y-coordinate of tart point + * @param x1 x-coordinate of end point + * @param y1 y-coordinate of end point + * @return A 2D array with 2 elements. The first element is the array of + * x-coordinates and the second element is the array of y-coordinates. + */ public static int[][] dda(int x0, int y0, int x1, int y1) { int dx = x1 - x0; int dy = y1 - y0; @@ -16,45 +30,35 @@ public static int[][] dda(int x0, int y0, int x1, int y1) { float x = x0; float y = y0; - int[][] pixelCoords = new int[steps + 1][2]; + int[] xpoints = new int[steps + 1]; + int[] ypoints = new int[steps + 1]; for (int i = 0; i <= steps; i++) { - pixelCoords[i][0]= Math.round(x); - pixelCoords[i][1]= Math.round(y); + xpoints[i] = Math.round(x); + ypoints[i] = Math.round(y); + x += xInc; y += yInc; } - return pixelCoords; + return new int[][] { xpoints, ypoints }; } public static int[][] bresenham(int x0, int y0, int x1, int y1) { int dx = Math.abs(x1 - x0); int dy = Math.abs(y1 - y0); - int sx; - if (x0 < x1) { - sx = 1; - } else { - sx = -1; - } - - int sy; - if (y0 < y1) { - sy = 1; - } else { - sy = -1; - } + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; int err = dx - dy; - int[][] pixelCoords = new int [dx + dy + 1][2]; - int index = 0; + List xpoints = new ArrayList<>(); + List ypoints = new ArrayList<>(); while (true) { - pixelCoords [index][0] = x0; - pixelCoords[index][1]= y0; - index++; + xpoints.add(x0); + ypoints.add(y0); if (x0 == x1 && y0 == y1) break; @@ -70,6 +74,6 @@ public static int[][] bresenham(int x0, int y0, int x1, int y1) { } } - return pixelCoords; + return new int[][] { xpoints.stream().mapToInt(i -> i).toArray(), ypoints.stream().mapToInt(i -> i).toArray() }; } } From 1515bed604066e081541f1fa5a9f668cf1dc71ee Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 5 Jul 2024 18:48:43 +0400 Subject: [PATCH 04/12] rewrite tests --- .../tests/algorithms/LineCalculatorTest.java | 309 ++++-------------- 1 file changed, 69 insertions(+), 240 deletions(-) diff --git a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java index 1ca2dbfa..908d6699 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java @@ -1,258 +1,87 @@ package com.github.creme332.tests.algorithms; -import org.junit.Test; - -import com.github.creme332.algorithms.LineCalculator; -import com.github.creme332.tests.utils.TestHelper; - -public class LineCalculatorTest { - - @Test - public void testDrawDDA() { - int x0 = 2, y0 = 3, x1 = 10, y1 = 8; - int[][] expected = { - {2, 3}, - {3, 3}, - {4, 4}, - {5, 4}, - {6, 5}, - {7, 5}, - {8, 6}, - {9, 7}, - {10, 8} - }; - - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - @Test - public void testDrawBresenham() { - int x0 = 2, y0 = 3, x1 = 10, y1 = 8; - int[][] expected = { - {2, 3}, - {3, 3}, - {4, 4}, - {5, 4}, - {6, 5}, - {7, 5}, - {8, 6}, - {9, 7}, - {10, 8} - }; - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } +import static org.junit.Assert.assertArrayEquals; - @Test - public void testDrawDDAHorizontal() { - int x0 = 1, y0 = 1, x1 = 5, y1 = 1; - int[][] expected = { - {1, 1}, - {2, 1}, - {3, 1}, - {4, 1}, - {5, 1} - }; +import java.util.Arrays; +import java.util.Collection; - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawBresenhamHorizontal() { - int x0 = 1, y0 = 1, x1 = 5, y1 = 1; - int[][] expected = { - {1, 1}, - {2, 1}, - {3, 1}, - {4, 1}, - {5, 1} - }; - - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawDDAVertical() { - int x0 = 1, y0 = 1, x1 = 1, y1 = 5; - int[][] expected = { - {1, 1}, - {1, 2}, - {1, 3}, - {1, 4}, - {1, 5} - }; - - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawBresenhamVertical() { - int x0 = 1, y0 = 1, x1 = 1, y1 = 5; - int[][] expected = { - {1, 1}, - {1, 2}, - {1, 3}, - {1, 4}, - {1, 5} - }; - - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawDDANegativeGradient() { - int x0 = 10, y0 = 10, x1 = 2, y1 = 3; - int[][] expected = { - {10, 10}, - {9, 9}, - {8, 8}, - {7, 7}, - {6, 6}, - {5, 5}, - {4, 4}, - {3, 3}, - {2, 3} - }; - - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawBresenhamNegativeGradient() { - int x0 = 10, y0 = 10, x1 = 2, y1 = 3; - int[][] expected = { - {10, 10}, - {9, 9}, - {8, 8}, - {7, 7}, - {6, 6}, - {5, 5}, - {4, 4}, - {3, 3}, - {2, 3} - }; - - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawDDASecondQuadrant() { - int x0 = -2, y0 = 3, x1 = -10, y1 = 8; - int[][] expected = { - {-2, 3}, - {-3, 3}, - {-4, 4}, - {-5, 4}, - {-6, 5}, - {-7, 5}, - {-8, 6}, - {-9, 7}, - {-10, 8} - }; - - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } - - @Test - public void testDrawBresenhamSecondQuadrant() { - int x0 = -2, y0 = 3, x1 = -10, y1 = 8; - int[][] expected = { - {-2, 3}, - {-3, 3}, - {-4, 4}, - {-5, 4}, - {-6, 5}, - {-7, 5}, - {-8, 6}, - {-9, 7}, - {-10, 8} - }; - - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); - } +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; - @Test - public void testDrawDDAThirdQuadrant() { - int x0 = -2, y0 = -3, x1 = -10, y1 = -8; - int[][] expected = { - {-2, -3}, - {-3, -3}, - {-4, -4}, - {-5, -4}, - {-6, -5}, - {-7, -5}, - {-8, -6}, - {-9, -7}, - {-10, -8} - }; +import com.github.creme332.algorithms.LineCalculator; - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); +@RunWith(Parameterized.class) +public class LineCalculatorTest { + private String description; + private int x0; + private int y0; + private int x1; + private int y1; + private int[][] expected; + + public LineCalculatorTest(String description, int x0, int y0, int x1, int y1, int[][] expected) { + this.description = description; + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + this.expected = expected; } - @Test - public void testDrawBresenhamThirdQuadrant() { - int x0 = -2, y0 = -3, x1 = -10, y1 = -8; - int[][] expected = { - {-2, -3}, - {-3, -3}, - {-4, -4}, - {-5, -4}, - {-6, -5}, - {-7, -5}, - {-8, -6}, - {-9, -7}, - {-10, -8} - }; - - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); + @Parameters(name = "{index}: {0}") + public static Collection dataBasedOnGradient() { + return Arrays.asList(new Object[][] { + // line with gradient 1 + { "0 < m < 1", 1, 1, 5, 5, new int[][] { + { 1, 2, 3, 4, 5 }, + { 1, 2, 3, 4, 5 }, + } }, + + // line with gradient between 0 and 1 + { "0 < m < 1", 2, 1, 8, 5, new int[][] { + { 2, 3, 4, 5, 6, 7, 8 }, + { 1, 2, 2, 3, 4, 4, 5 } + } }, + + // line with gradient > 1 + { "m > 1", 3, 2, 7, 8, new int[][] { + { 3, 4, 4, 5, 6, 6, 7 }, + { 2, 3, 4, 5, 6, 7, 8 } + } }, + + // line with gradient < -1 + { "m < -1", 2, 8, 5, 3, new int[][] { + { 2, 3, 3, 4, 4, 5 }, + { 8, 7, 6, 5, 4, 3 } + } }, + + // horizontal line + { "m = 0", 1, 1, 5, 1, new int[][] { + { 1, 2, 3, 4, 5 }, + { 1, 1, 1, 1, 1 } + } }, + + // vertical line starting at origin + { "m = INF", 0, 0, 0, 4, new int[][] { + { 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4 } + } }, + }); } @Test - public void testDrawDDAFourthQuadrant() { - int x0 = 2, y0 = -3, x1 = 10, y1 = -8; - int[][] expected = { - {2, -3}, - {3, -4}, - {4, -4}, - {5, -5}, - {6, -5}, - {7, -6}, - {8, -6}, - {9, -7}, - {10, -8} - }; - + public void testDDA() { + System.out.println("DDA: " + description); int[][] result = LineCalculator.dda(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); + assertArrayEquals(expected, result); } @Test - public void testDrawBresenhamFourthQuadrant() { - int x0 = 2, y0 = -3, x1 = 10, y1 = -8; - int[][] expected = { - {2, -3}, - {3, -4}, - {4, -4}, - {5, -5}, - {6, -5}, - {7, -6}, - {8, -6}, - {9, -7}, - {10, -8} - }; - + public void testBresenham() { + System.out.println("Bresenham: " + description); int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - TestHelper.assert2DArrayEquals(expected, result); + assertArrayEquals(expected, result); } } - From 5a2f7c736fd371ec94d8059ad75ea61416889254 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:08:52 +0400 Subject: [PATCH 05/12] use double instead of float --- .../github/creme332/algorithms/LineCalculator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index 2eb1a0e6..c60f8a7a 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -24,18 +24,18 @@ public static int[][] dda(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int steps = Math.max(Math.abs(dx), Math.abs(dy)); - float xInc = (float) dx / steps; - float yInc = (float) dy / steps; + double xInc = (double) dx / steps; + double yInc = (double) dy / steps; - float x = x0; - float y = y0; + double x = x0; + double y = y0; int[] xpoints = new int[steps + 1]; int[] ypoints = new int[steps + 1]; for (int i = 0; i <= steps; i++) { - xpoints[i] = Math.round(x); - ypoints[i] = Math.round(y); + xpoints[i] = (int) Math.round(x); + ypoints[i] = (int) Math.round(y); x += xInc; y += yInc; From 2e3436b84fdebf8954ccde654b5569ba7a76e781 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:31:11 +0400 Subject: [PATCH 06/12] fix rounding bug in dda --- .../creme332/algorithms/LineCalculator.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index c60f8a7a..1d38dc05 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -8,6 +8,35 @@ private LineCalculator() { } + /** + * Rounds the given value to the nearest integer, mimicking the behavior of the + * C round function. + * This method rounds ties away from zero unlike Math.round(). + * + *

+ * Examples: + * + *

+     * round(-5.5); // returns -6
+     * round(-2.5); // returns -3
+     * round(-1.4); // returns -1
+     * round(0.5); // returns 1
+     * round(1.5); // returns 2
+     * round(2.4); // returns 2
+     * round(3.5); // returns 4
+     * 
+ * + * @param value the value to be rounded + * @return the value rounded to the nearest integer + */ + public static int round(double value) { + if (value >= 0) { + return (int) Math.floor(value + 0.5); + } else { + return (int) Math.ceil(value - 0.5); + } + } + /** * Calculates pixels between any 2 points (x0, y0) and (x1, y1) using the DDA * line algorithm. @@ -34,8 +63,8 @@ public static int[][] dda(int x0, int y0, int x1, int y1) { int[] ypoints = new int[steps + 1]; for (int i = 0; i <= steps; i++) { - xpoints[i] = (int) Math.round(x); - ypoints[i] = (int) Math.round(y); + xpoints[i] = round(x); + ypoints[i] = round(y); x += xInc; y += yInc; From 33074091d9b41714c13e6b2a595d427672634a5f Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:32:22 +0400 Subject: [PATCH 07/12] add random tests --- .../tests/algorithms/LineCalculatorTest.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java index 908d6699..81f59ac4 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Random; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,11 +35,17 @@ public LineCalculatorTest(String description, int x0, int y0, int x1, int y1, in public static Collection dataBasedOnGradient() { return Arrays.asList(new Object[][] { // line with gradient 1 - { "0 < m < 1", 1, 1, 5, 5, new int[][] { + { "m = 1", 1, 1, 5, 5, new int[][] { { 1, 2, 3, 4, 5 }, { 1, 2, 3, 4, 5 }, } }, + // line with gradient -1 + { "m = -1", -5, 5, -1, 1, new int[][] { + { -5, -4, -3, -2, -1 }, + { 5, 4, 3, 2, 1 }, + } }, + // line with gradient between 0 and 1 { "0 < m < 1", 2, 1, 8, 5, new int[][] { { 2, 3, 4, 5, 6, 7, 8 }, @@ -63,7 +70,7 @@ public static Collection dataBasedOnGradient() { { 1, 1, 1, 1, 1 } } }, - // vertical line starting at origin + // vertical line { "m = INF", 0, 0, 0, 4, new int[][] { { 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4 } @@ -73,15 +80,46 @@ public static Collection dataBasedOnGradient() { @Test public void testDDA() { - System.out.println("DDA: " + description); int[][] result = LineCalculator.dda(x0, y0, x1, y1); assertArrayEquals(expected, result); } @Test public void testBresenham() { - System.out.println("Bresenham: " + description); int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); assertArrayEquals(expected, result); } + + public static int[] generateRandomCoordinate() { + final int BOUND = 10; + Random random = new Random(); + int x = random.nextInt(2 * BOUND + 1) - BOUND; // Random integer between -BOUND and +BOUND + int y = random.nextInt(2 * BOUND + 1) - BOUND; // Random integer between -BOUND and +BOUND + return new int[] { x, y }; + } + + @Test + public void testRandom() { + final int NUM_TESTS = 10; + + for (int i = 0; i < NUM_TESTS; i++) { + int[] start = generateRandomCoordinate(); + int[] end = generateRandomCoordinate(); + + int[][] bresenhamResult = LineCalculator.bresenham(start[0], start[1], end[0], end[1]); + int[][] ddaResult = LineCalculator.dda(start[0], start[1], end[0], end[1]); + + try { + assertArrayEquals(bresenhamResult, ddaResult); + } catch (AssertionError e) { + System.out.println( + "Random test failed for coordinates: " + Arrays.toString(start) + " " + Arrays.toString(end)); + System.out.println("DDA: " + Arrays.deepToString(ddaResult)); + System.out.println("Bresenham: " + Arrays.deepToString(bresenhamResult)); + System.out.println(); + + throw e; // Re-throw the assertion error to ensure the test fails + } + } + } } From 4c4ed053a0215ab9d3876c06fbd39d05289dc6ad Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Sat, 6 Jul 2024 08:00:42 +0400 Subject: [PATCH 08/12] do not use parametrized class to have more control on the number of random tests --- .../tests/algorithms/LineCalculatorTest.java | 161 ++++++++++-------- 1 file changed, 92 insertions(+), 69 deletions(-) diff --git a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java index 81f59ac4..743fa0a6 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java @@ -2,92 +2,114 @@ import static org.junit.Assert.assertArrayEquals; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Random; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; import com.github.creme332.algorithms.LineCalculator; -@RunWith(Parameterized.class) public class LineCalculatorTest { - private String description; - private int x0; - private int y0; - private int x1; - private int y1; - private int[][] expected; - - public LineCalculatorTest(String description, int x0, int y0, int x1, int y1, int[][] expected) { - this.description = description; - this.x0 = x0; - this.y0 = y0; - this.x1 = x1; - this.y1 = y1; - this.expected = expected; + // Helper class to hold test cases + private static class TestCase { + private String description; + private int x0; + private int y0; + private int x1; + private int y1; + private int[][] expected; + + TestCase(String description, int x0, int y0, int x1, int y1, int[][] expected) { + this.description = description; + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + this.expected = expected; + } } - @Parameters(name = "{index}: {0}") - public static Collection dataBasedOnGradient() { - return Arrays.asList(new Object[][] { - // line with gradient 1 - { "m = 1", 1, 1, 5, 5, new int[][] { - { 1, 2, 3, 4, 5 }, - { 1, 2, 3, 4, 5 }, - } }, - - // line with gradient -1 - { "m = -1", -5, 5, -1, 1, new int[][] { - { -5, -4, -3, -2, -1 }, - { 5, 4, 3, 2, 1 }, - } }, - - // line with gradient between 0 and 1 - { "0 < m < 1", 2, 1, 8, 5, new int[][] { - { 2, 3, 4, 5, 6, 7, 8 }, - { 1, 2, 2, 3, 4, 4, 5 } - } }, - - // line with gradient > 1 - { "m > 1", 3, 2, 7, 8, new int[][] { - { 3, 4, 4, 5, 6, 6, 7 }, - { 2, 3, 4, 5, 6, 7, 8 } - } }, - - // line with gradient < -1 - { "m < -1", 2, 8, 5, 3, new int[][] { - { 2, 3, 3, 4, 4, 5 }, - { 8, 7, 6, 5, 4, 3 } - } }, - - // horizontal line - { "m = 0", 1, 1, 5, 1, new int[][] { - { 1, 2, 3, 4, 5 }, - { 1, 1, 1, 1, 1 } - } }, - - // vertical line - { "m = INF", 0, 0, 0, 4, new int[][] { - { 0, 0, 0, 0, 0 }, - { 0, 1, 2, 3, 4 } - } }, - }); + public static Collection fixedTestCases() { + List testCases = new ArrayList<>(); + + testCases.add(new TestCase("m = 1", 1, 1, 5, 5, new int[][] { + { 1, 2, 3, 4, 5 }, + { 1, 2, 3, 4, 5 } + })); + + testCases.add(new TestCase("m = -1", -5, 5, -1, 1, new int[][] { + { -5, -4, -3, -2, -1 }, + { 5, 4, 3, 2, 1 } + })); + + testCases.add(new TestCase("0 < m < 1", 2, 1, 8, 5, new int[][] { + { 2, 3, 4, 5, 6, 7, 8 }, + { 1, 2, 2, 3, 4, 4, 5 } + })); + + testCases.add(new TestCase("-1 < m < 0", -6, 5, -1, 1, new int[][] { + { -6, -5, -4, -3, -2, -1 }, + { 5, 4, 3, 3, 2, 1 } + })); + + testCases.add(new TestCase("m > 1", 3, 2, 7, 8, new int[][] { + { 3, 4, 4, 5, 6, 6, 7 }, + { 2, 3, 4, 5, 6, 7, 8 } + })); + + testCases.add(new TestCase("m < -1", 2, 8, 5, 3, new int[][] { + { 2, 3, 3, 4, 4, 5 }, + { 8, 7, 6, 5, 4, 3 } + })); + + testCases.add(new TestCase("m < 0 in 1st quadrant", 8, 4, 6, 8, new int[][] { + { 8, 8, 7, 7, 6 }, + { 4, 5, 6, 7, 8 } + })); + + testCases.add(new TestCase("m = 0", 1, 1, 5, 1, new int[][] { + { 1, 2, 3, 4, 5 }, + { 1, 1, 1, 1, 1 } + })); + + testCases.add(new TestCase("m = INF", 0, 0, 0, 4, new int[][] { + { 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4 } + })); + + return testCases; } @Test public void testDDA() { - int[][] result = LineCalculator.dda(x0, y0, x1, y1); - assertArrayEquals(expected, result); + for (TestCase test : fixedTestCases()) { + int[][] result = LineCalculator.dda(test.x0, test.y0, test.x1, test.y1); + + try { + assertArrayEquals(test.expected, result); + } catch (AssertionError e) { + System.out.println(test.description); + throw e; + } + + } } @Test public void testBresenham() { - int[][] result = LineCalculator.bresenham(x0, y0, x1, y1); - assertArrayEquals(expected, result); + for (TestCase test : fixedTestCases()) { + int[][] result = LineCalculator.bresenham(test.x0, test.y0, test.x1, test.y1); + + try { + assertArrayEquals(test.expected, result); + } catch (AssertionError e) { + System.out.println(test.description); + throw e; + } + } } public static int[] generateRandomCoordinate() { @@ -100,7 +122,7 @@ public static int[] generateRandomCoordinate() { @Test public void testRandom() { - final int NUM_TESTS = 10; + final int NUM_TESTS = 100; for (int i = 0; i < NUM_TESTS; i++) { int[] start = generateRandomCoordinate(); @@ -113,7 +135,8 @@ public void testRandom() { assertArrayEquals(bresenhamResult, ddaResult); } catch (AssertionError e) { System.out.println( - "Random test failed for coordinates: " + Arrays.toString(start) + " " + Arrays.toString(end)); + "Random test failed for coordinates: " + Arrays.toString(start) + " " + + Arrays.toString(end)); System.out.println("DDA: " + Arrays.deepToString(ddaResult)); System.out.println("Bresenham: " + Arrays.deepToString(bresenhamResult)); System.out.println(); From 96286d1e3128bafe4f302f9b265285881740e537 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Sat, 6 Jul 2024 08:09:44 +0400 Subject: [PATCH 09/12] add javadoc to bresenham --- .../github/creme332/algorithms/LineCalculator.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index 1d38dc05..f1a47db8 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -5,7 +5,6 @@ public class LineCalculator { private LineCalculator() { - } /** @@ -73,6 +72,17 @@ public static int[][] dda(int x0, int y0, int x1, int y1) { return new int[][] { xpoints, ypoints }; } + /** + * Calculates pixels between any 2 points (x0, y0) and (x1, y1) using the + * Bresenham line algorithm. + * + * @param x0 x-coordinate of start point + * @param y0 y-coordinate of tart point + * @param x1 x-coordinate of end point + * @param y1 y-coordinate of end point + * @return A 2D array with 2 elements. The first element is the array of + * x-coordinates and the second element is the array of y-coordinates. + */ public static int[][] bresenham(int x0, int y0, int x1, int y1) { int dx = Math.abs(x1 - x0); int dy = Math.abs(y1 - y0); From 35f18d0cae491727f5bac8a34d4500d8042071a8 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Sun, 7 Jul 2024 13:33:30 +0400 Subject: [PATCH 10/12] rewrite dda using higher precision in calculations --- .../creme332/algorithms/LineCalculator.java | 59 ++++++------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index f1a47db8..146395f1 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -1,5 +1,8 @@ package com.github.creme332.algorithms; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -7,66 +10,38 @@ public class LineCalculator { private LineCalculator() { } - /** - * Rounds the given value to the nearest integer, mimicking the behavior of the - * C round function. - * This method rounds ties away from zero unlike Math.round(). - * - *

- * Examples: - * - *

-     * round(-5.5); // returns -6
-     * round(-2.5); // returns -3
-     * round(-1.4); // returns -1
-     * round(0.5); // returns 1
-     * round(1.5); // returns 2
-     * round(2.4); // returns 2
-     * round(3.5); // returns 4
-     * 
- * - * @param value the value to be rounded - * @return the value rounded to the nearest integer - */ - public static int round(double value) { - if (value >= 0) { - return (int) Math.floor(value + 0.5); - } else { - return (int) Math.ceil(value - 0.5); - } - } - /** * Calculates pixels between any 2 points (x0, y0) and (x1, y1) using the DDA - * line algorithm. + * line algorithm. High precision calculations are used to reduce floating point + * errors. * * @param x0 x-coordinate of start point - * @param y0 y-coordinate of tart point + * @param y0 y-coordinate of start point * @param x1 x-coordinate of end point * @param y1 y-coordinate of end point * @return A 2D array with 2 elements. The first element is the array of * x-coordinates and the second element is the array of y-coordinates. */ public static int[][] dda(int x0, int y0, int x1, int y1) { - int dx = x1 - x0; - int dy = y1 - y0; - int steps = Math.max(Math.abs(dx), Math.abs(dy)); + final int dx = x1 - x0; + final int dy = y1 - y0; + final int steps = Math.max(Math.abs(dx), Math.abs(dy)); - double xInc = (double) dx / steps; - double yInc = (double) dy / steps; + final BigDecimal xInc = BigDecimal.valueOf(dx).divide(BigDecimal.valueOf(steps), MathContext.DECIMAL128); + final BigDecimal yInc = BigDecimal.valueOf(dy).divide(BigDecimal.valueOf(steps), MathContext.DECIMAL128); - double x = x0; - double y = y0; + BigDecimal x = BigDecimal.valueOf(x0); + BigDecimal y = BigDecimal.valueOf(y0); int[] xpoints = new int[steps + 1]; int[] ypoints = new int[steps + 1]; for (int i = 0; i <= steps; i++) { - xpoints[i] = round(x); - ypoints[i] = round(y); + xpoints[i] = x.setScale(0, RoundingMode.HALF_UP).intValue(); + ypoints[i] = y.setScale(0, RoundingMode.HALF_UP).intValue(); - x += xInc; - y += yInc; + x = x.add(xInc); + y = y.add(yInc); } return new int[][] { xpoints, ypoints }; From 724a14c17575fd6f2313498158f4ecbdcc2bf78d Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Sun, 7 Jul 2024 17:36:12 +0400 Subject: [PATCH 11/12] add another version of bresenham --- .../creme332/algorithms/LineCalculator.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main/java/com/github/creme332/algorithms/LineCalculator.java b/src/main/java/com/github/creme332/algorithms/LineCalculator.java index 146395f1..51345cb9 100644 --- a/src/main/java/com/github/creme332/algorithms/LineCalculator.java +++ b/src/main/java/com/github/creme332/algorithms/LineCalculator.java @@ -90,4 +90,50 @@ public static int[][] bresenham(int x0, int y0, int x1, int y1) { return new int[][] { xpoints.stream().mapToInt(i -> i).toArray(), ypoints.stream().mapToInt(i -> i).toArray() }; } + + /** + * Another version of the bresenham line algorithm. + * Adapted from: https://github.com/madbence/node-bresenham + * + * @param x0 + * @param y0 + * @param x1 + * @param y1 + * @return + */ + public static int[][] bresenham2(int x0, int y0, int x1, int y1) { + List xpoints = new ArrayList<>(); + List ypoints = new ArrayList<>(); + + int dx = x1 - x0; + int dy = y1 - y0; + int adx = Math.abs(dx); + int ady = Math.abs(dy); + int sx = dx > 0 ? 1 : -1; + int sy = dy > 0 ? 1 : -1; + int eps = 0; + if (adx > ady) { + for (int x = x0, y = y0; sx < 0 ? x >= x1 : x <= x1; x += sx) { + xpoints.add(x); + ypoints.add(y); + + eps += ady; + if (eps << 1 >= adx) { + y += sy; + eps -= adx; + } + } + } else { + for (int x = x0, y = y0; sy < 0 ? y >= y1 : y <= y1; y += sy) { + xpoints.add(x); + ypoints.add(y); + eps += adx; + if (eps << 1 >= ady) { + x += sx; + eps -= ady; + } + } + } + return new int[][] { xpoints.stream().mapToInt(i -> i).toArray(), ypoints.stream().mapToInt(i -> i).toArray() }; + } } From 4d2e5c8ecebb749c2f36802b3b478e17603d81bf Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Sun, 7 Jul 2024 17:36:48 +0400 Subject: [PATCH 12/12] add new test case and disable random tests --- .../github/creme332/tests/algorithms/LineCalculatorTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java index 743fa0a6..c0a87c09 100644 --- a/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java +++ b/src/test/java/com/github/creme332/tests/algorithms/LineCalculatorTest.java @@ -80,6 +80,10 @@ public static Collection fixedTestCases() { { 0, 1, 2, 3, 4 } })); + testCases.add(new TestCase("m has recurring decimal places", -10, -9, 7, 9, new int[][] { + { -10, -9, -8, -7, -6, -5, -4, -3, -2, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7 }, + { -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + })); return testCases; } @@ -120,7 +124,6 @@ public static int[] generateRandomCoordinate() { return new int[] { x, y }; } - @Test public void testRandom() { final int NUM_TESTS = 100;