Skip to content

Commit 660814a

Browse files
author
Moksedur Rahman Sohan
committed
Implement Cohen-Sutherland Line Clipping Algorithm
1 parent 87871ef commit 660814a

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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/4/24
9+
* Cohen-Sutherland Line Clipping Algorithm
10+
*
11+
* This algorithm is used to clip a line segment to a rectangular window.
12+
* It assigns a region code to each endpoint of the line segment, and
13+
* then efficiently determines whether the line segment is fully inside,
14+
* fully outside, or partially inside the window.
15+
*
16+
* Reference:
17+
* https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_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 CohenSutherland {
24+
25+
// Region codes for the 9 regions
26+
final int INSIDE = 0; // 0000
27+
final int LEFT = 1; // 0001
28+
final int RIGHT = 2; // 0010
29+
final int BOTTOM = 4; // 0100
30+
final int TOP = 8; // 1000
31+
32+
// Define the clipping window
33+
double xMin, yMin, xMax, yMax;
34+
35+
public CohenSutherland(double xMin, double yMin, double xMax, double yMax) {
36+
this.xMin = xMin;
37+
this.yMin = yMin;
38+
this.xMax = xMax;
39+
this.yMax = yMax;
40+
}
41+
42+
// Compute the region code for a point (x, y)
43+
private int computeCode(double x, double y) {
44+
int code = INSIDE;
45+
46+
if (x < xMin) // to the left of rectangle
47+
code |= LEFT;
48+
else if (x > xMax) // to the right of rectangle
49+
code |= RIGHT;
50+
if (y < yMin) // below the rectangle
51+
code |= BOTTOM;
52+
else if (y > yMax) // above the rectangle
53+
code |= TOP;
54+
55+
return code;
56+
}
57+
58+
// Cohen-Sutherland algorithm to return the clipped line
59+
public Line cohenSutherlandClip(Line line) {
60+
double x1 = line.start.x, y1 = line.start.y;
61+
double x2 = line.end.x, y2 = line.end.y;
62+
63+
int code1 = computeCode(x1, y1);
64+
int code2 = computeCode(x2, y2);
65+
boolean accept = false;
66+
67+
while (true) {
68+
if ((code1 == 0) && (code2 == 0)) {
69+
// Both points are inside the rectangle
70+
accept = true;
71+
break;
72+
} else if ((code1 & code2) != 0) {
73+
// Both points are outside the rectangle in the same region
74+
break;
75+
} else {
76+
// Some segment of the line is inside the rectangle
77+
double x = 0, y = 0;
78+
79+
// Pick an endpoint that is outside the rectangle
80+
int codeOut = (code1 != 0) ? code1 : code2;
81+
82+
// Find the intersection point using the line equation
83+
if ((codeOut & TOP) != 0) {
84+
// Point is above the rectangle
85+
x = x1 + (x2 - x1) * (yMax - y1) / (y2 - y1);
86+
y = yMax;
87+
} else if ((codeOut & BOTTOM) != 0) {
88+
// Point is below the rectangle
89+
x = x1 + (x2 - x1) * (yMin - y1) / (y2 - y1);
90+
y = yMin;
91+
} else if ((codeOut & RIGHT) != 0) {
92+
// Point is to the right of the rectangle
93+
y = y1 + (y2 - y1) * (xMax - x1) / (x2 - x1);
94+
x = xMax;
95+
} else if ((codeOut & LEFT) != 0) {
96+
// Point is to the left of the rectangle
97+
y = y1 + (y2 - y1) * (xMin - x1) / (x2 - x1);
98+
x = xMin;
99+
}
100+
101+
// Replace the point outside the rectangle with the intersection point
102+
if (codeOut == code1) {
103+
x1 = x;
104+
y1 = y;
105+
code1 = computeCode(x1, y1);
106+
} else {
107+
x2 = x;
108+
y2 = y;
109+
code2 = computeCode(x2, y2);
110+
}
111+
}
112+
}
113+
114+
if (accept) {
115+
return new Line(new Point(x1, y1), new Point(x2, y2));
116+
} else {
117+
118+
return null; // The line is fully rejected
119+
}
120+
}
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.thealgorithms.lineclipping.utils;
2+
3+
import java.util.Objects;
4+
5+
/**
6+
* @author moksedursohan
7+
* @since 10/4/24
8+
*/
9+
public class Line {
10+
11+
public Point start, end;
12+
13+
public Line() {
14+
}
15+
16+
public Line(Point start, Point end) {
17+
this.start = start;
18+
this.end = end;
19+
}
20+
21+
@Override
22+
public boolean equals(Object o) {
23+
if (this == o) return true;
24+
if (!(o instanceof Line line)) return false;
25+
return Objects.equals(start, line.start) && Objects.equals(end, line.end);
26+
}
27+
28+
@Override
29+
public int hashCode() {
30+
return Objects.hash(start, end);
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return "Line from " + start + " to " + end;
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.thealgorithms.lineclipping.utils;
2+
3+
import java.util.Objects;
4+
5+
/**
6+
* @author moksedursohan
7+
* @since 10/4/24
8+
*/
9+
public class Point {
10+
11+
public double x, y;
12+
13+
public Point() {
14+
}
15+
16+
public Point(double x, double y) {
17+
this.x = x;
18+
this.y = y;
19+
}
20+
21+
@Override
22+
public boolean equals(Object o) {
23+
if (this == o) return true;
24+
if (!(o instanceof Point point)) return false;
25+
return Double.compare(x, point.x) == 0 && Double.compare(y, point.y) == 0;
26+
}
27+
28+
@Override
29+
public int hashCode() {
30+
return Objects.hash(x, y);
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return "(" + x + ", " + y + ")";
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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/4/24
12+
*/
13+
class CohenSutherlandTest {
14+
15+
// Define the clipping window (1.0, 1.0) to (10.0, 10.0)
16+
CohenSutherland cs = new CohenSutherland(1.0, 1.0, 10.0, 10.0);
17+
18+
@Test
19+
void testLineCompletelyInside() {
20+
// Line fully inside the clipping window
21+
Line line = new Line(new Point(2.0, 2.0), new Point(8.0, 8.0));
22+
Line clippedLine = cs.cohenSutherlandClip(line);
23+
24+
assertNotNull(clippedLine, "Line should not be null.");
25+
assertEquals(line, clippedLine, "Line inside the window should remain unchanged.");
26+
}
27+
28+
@Test
29+
void testLineCompletelyOutside() {
30+
// Line completely outside and above the clipping window
31+
Line line = new Line(new Point(11.0, 12.0), new Point(15.0, 18.0));
32+
Line clippedLine = cs.cohenSutherlandClip(line);
33+
34+
assertNull(clippedLine, "Line should be null because it's completely outside.");
35+
}
36+
37+
@Test
38+
void testLinePartiallyInside() {
39+
// Line partially inside the clipping window
40+
Line line = new Line(new Point(5.0, 5.0), new Point(12.0, 12.0));
41+
Line expectedClippedLine = new Line(new Point(5.0, 5.0), new Point(10.0, 10.0)); // Clipped at (10, 10)
42+
Line clippedLine = cs.cohenSutherlandClip(line);
43+
44+
assertNotNull(clippedLine, "Line should not be null.");
45+
assertEquals(expectedClippedLine, clippedLine, "Line should be clipped correctly.");
46+
}
47+
48+
@Test
49+
void testLineOnBoundary() {
50+
// Line exactly on the boundary of the clipping window
51+
Line line = new Line(new Point(1.0, 5.0), new Point(10.0, 5.0));
52+
Line clippedLine = cs.cohenSutherlandClip(line);
53+
54+
assertNotNull(clippedLine, "Line should not be null.");
55+
assertEquals(line, clippedLine, "Line on the boundary should remain unchanged.");
56+
}
57+
58+
@Test
59+
void testDiagonalLineThroughClippingWindow() {
60+
// Diagonal line crossing from outside to outside through the window
61+
Line line = new Line(new Point(0.0, 0.0), new Point(12.0, 12.0));
62+
Line expectedClippedLine = new Line(new Point(1.0, 1.0), new Point(10.0, 10.0)); // Clipped at both boundaries
63+
Line clippedLine = cs.cohenSutherlandClip(line);
64+
65+
assertNotNull(clippedLine, "Line should not be null.");
66+
assertEquals(expectedClippedLine, clippedLine, "Diagonal line should be clipped correctly.");
67+
}
68+
69+
@Test
70+
void testVerticalLineClipping() {
71+
// Vertical line crossing the top and bottom of the clipping window
72+
Line line = new Line(new Point(5.0, 0.0), new Point(5.0, 12.0));
73+
Line expectedClippedLine = new Line(new Point(5.0, 1.0), new Point(5.0, 10.0)); // Clipped at yMin and yMax
74+
Line clippedLine = cs.cohenSutherlandClip(line);
75+
76+
assertNotNull(clippedLine, "Line should not be null.");
77+
assertEquals(expectedClippedLine, clippedLine, "Vertical line should be clipped correctly.");
78+
}
79+
}

0 commit comments

Comments
 (0)