Skip to content

Commit c60c1cf

Browse files
author
Moksedur Rahman Sohan
committed
Implement Liang-Barsky Line Clipping Algorithm
1 parent 660814a commit c60c1cf

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.thealgorithms.lineclipping;
2+
3+
import com.thealgorithms.lineclipping.utils.Line;
4+
import com.thealgorithms.lineclipping.utils.Point;
5+
6+
/**
7+
* @author shikarisohan
8+
* @since 10/5/24
9+
*
10+
* * The Liang-Barsky line clipping algorithm is an efficient algorithm for
11+
* * line clipping against a rectangular window. It is based on the parametric
12+
* * equation of a line and checks the intersections of the line with the
13+
* * window boundaries. This algorithm calculates the intersection points,
14+
* * if any, and returns the clipped line that lies inside the window.
15+
* *
16+
* * Reference:
17+
* * https://en.wikipedia.org/wiki/Liang%E2%80%93Barsky_algorithm
18+
*
19+
* Clipping window boundaries are defined as (xMin, yMin) and (xMax, yMax).
20+
* The algorithm computes the clipped line segment if it's partially or
21+
* fully inside the clipping window.
22+
*/
23+
public class LiangBarsky {
24+
25+
// Define the clipping window
26+
private double xMin, xMax, yMin, yMax;
27+
28+
public LiangBarsky(double xMin, double yMin, double xMax, double yMax) {
29+
this.xMin = xMin;
30+
this.yMin = yMin;
31+
this.xMax = xMax;
32+
this.yMax = yMax;
33+
}
34+
35+
// Liang-Barsky algorithm to return the clipped line
36+
public Line liangBarskyClip(Line line) {
37+
double dx = line.end.x - line.start.x;
38+
double dy = line.end.y - line.start.y;
39+
40+
double[] p = {-dx, dx, -dy, dy};
41+
double[] q = {line.start.x - xMin, xMax - line.start.x, line.start.y - yMin, yMax - line.start.y};
42+
43+
double[] resultT = clipLine(p, q);
44+
45+
if (resultT == null) {
46+
return null; // Line is outside the clipping window
47+
}
48+
49+
return calculateClippedLine(line, resultT[0], resultT[1], dx, dy);
50+
}
51+
52+
// clip the line by adjusting t0 and t1 for each edge
53+
private double[] clipLine(double[] p, double[] q) {
54+
double t0 = 0.0;
55+
double t1 = 1.0;
56+
57+
for (int i = 0; i < 4; i++) {
58+
double t = q[i] / p[i];
59+
if (p[i] == 0 && q[i] < 0) {
60+
return null; // Line is outside the boundary
61+
} else if (p[i] < 0) {
62+
if (t > t1) return null; // Line is outside
63+
if (t > t0) t0 = t; // Update t0
64+
} else if (p[i] > 0) {
65+
if (t < t0) return null; // Line is outside
66+
if (t < t1) t1 = t; // Update t1
67+
}
68+
}
69+
return new double[]{t0, t1}; // Return valid t0 and t1
70+
}
71+
72+
// calculate the clipped line based on t0 and t1
73+
private Line calculateClippedLine(Line line, double t0, double t1, double dx, double dy) {
74+
double clippedX1 = line.start.x + t0 * dx;
75+
double clippedY1 = line.start.y + t0 * dy;
76+
double clippedX2 = line.start.x + t1 * dx;
77+
double clippedY2 = line.start.y + t1 * dy;
78+
79+
return new Line(new Point(clippedX1, clippedY1), new Point(clippedX2, clippedY2));
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.thealgorithms.lineclipping;
2+
3+
import com.thealgorithms.lineclipping.utils.Line;
4+
import com.thealgorithms.lineclipping.utils.Point;
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
/**
10+
* @author shikarisohan
11+
* @since 10/5/24
12+
*/
13+
class LiangBarskyTest {
14+
15+
LiangBarsky lb = new LiangBarsky(1.0, 1.0, 10.0, 10.0);
16+
17+
@Test
18+
void testLineCompletelyInside() {
19+
Line line = new Line(new Point(2.0, 2.0), new Point(8.0, 8.0));
20+
Line clippedLine = lb.liangBarskyClip(line);
21+
22+
assertNotNull(clippedLine, "Line should not be null.");
23+
assertEquals(line, clippedLine, "Line inside the window should remain unchanged.");
24+
}
25+
26+
@Test
27+
void testLineCompletelyOutside() {
28+
Line line = new Line(new Point(12.0, 12.0), new Point(15.0, 18.0));
29+
Line clippedLine = lb.liangBarskyClip(line);
30+
31+
assertNull(clippedLine, "Line should be null because it's completely outside.");
32+
}
33+
34+
@Test
35+
void testLinePartiallyInside() {
36+
Line line = new Line(new Point(5.0, 5.0), new Point(12.0, 12.0));
37+
Line expectedClippedLine = new Line(new Point(5.0, 5.0), new Point(10.0, 10.0)); // Clipped at (10, 10)
38+
Line clippedLine = lb.liangBarskyClip(line);
39+
40+
assertNotNull(clippedLine, "Line should not be null.");
41+
assertEquals(expectedClippedLine, clippedLine, "Line should be clipped correctly.");
42+
}
43+
44+
@Test
45+
void testDiagonalLineThroughClippingWindow() {
46+
Line line = new Line(new Point(0.0, 0.0), new Point(12.0, 12.0));
47+
Line expectedClippedLine = new Line(new Point(1.0, 1.0), new Point(10.0, 10.0)); // Clipped at both boundaries
48+
Line clippedLine = lb.liangBarskyClip(line);
49+
50+
assertNotNull(clippedLine, "Line should not be null.");
51+
assertEquals(expectedClippedLine, clippedLine, "Diagonal line should be clipped correctly.");
52+
}
53+
54+
@Test
55+
void testVerticalLineClipping() {
56+
Line line = new Line(new Point(5.0, 0.0), new Point(5.0, 12.0));
57+
Line expectedClippedLine = new Line(new Point(5.0, 1.0), new Point(5.0, 10.0)); // Clipped at yMin and yMax
58+
Line clippedLine = lb.liangBarskyClip(line);
59+
60+
assertNotNull(clippedLine, "Line should not be null.");
61+
assertEquals(expectedClippedLine, clippedLine, "Vertical line should be clipped correctly.");
62+
}
63+
64+
}

0 commit comments

Comments
 (0)