66
77/**
88 * A class implementing the Rotating Calipers algorithm for geometric computations on convex polygons.
9- *
9+ *
1010 * The Rotating Calipers algorithm is an efficient technique for solving various geometric problems
1111 * on convex polygons, including:
1212 * - Computing the diameter (maximum distance between any two points)
1313 * - Computing the width (minimum distance between parallel supporting lines)
1414 * - Finding the minimum-area bounding rectangle
15- *
15+ *
1616 * Algorithm Description:
1717 * 1. Compute the convex hull of the given points
1818 * 2. Use rotating calipers (parallel lines) that rotate around the convex hull
1919 * 3. For each rotation, compute the desired geometric property
2020 * 4. Return the optimal result
21- *
21+ *
2222 * Time Complexity: O(n) where n is the number of points in the convex hull
2323 * Space Complexity: O(n) for storing the convex hull
24- *
24+ *
2525 * Reference:
2626 * Shamos, M. I. (1978). Computational Geometry.
27- *
27+ *
2828 * @author TheAlgorithms
2929 */
3030public final class RotatingCalipers {
31-
31+
3232 private RotatingCalipers () {
3333 }
34-
34+
3535 /**
3636 * Represents a pair of points with their distance.
3737 */
@@ -41,7 +41,7 @@ public String toString() {
4141 return String .format ("PointPair(%s, %s, distance=%.2f)" , p1 , p2 , distance );
4242 }
4343 }
44-
44+
4545 /**
4646 * Represents a rectangle with its area.
4747 */
@@ -51,11 +51,11 @@ public String toString() {
5151 return String .format ("Rectangle(%s, %s, area=%.2f)" , bottomLeft , topRight , area );
5252 }
5353 }
54-
54+
5555 /**
5656 * Computes the diameter of a convex polygon using rotating calipers.
5757 * The diameter is the maximum distance between any two points of the polygon.
58- *
58+ *
5959 * @param points List of points representing a convex polygon
6060 * @return PointPair containing the two points with maximum distance and the distance
6161 * @throws IllegalArgumentException if points is null or has less than 2 points
@@ -64,54 +64,54 @@ public static PointPair computeDiameter(List<Point> points) {
6464 if (points == null || points .size () < 2 ) {
6565 throw new IllegalArgumentException ("Points list must contain at least 2 points" );
6666 }
67-
67+
6868 List <Point > hull = ConvexHull .convexHullRecursive (new ArrayList <>(points ));
6969 if (hull .size () < 2 ) {
7070 throw new IllegalArgumentException ("Convex hull must contain at least 2 points" );
7171 }
72-
72+
7373 hull = ensureCounterClockwiseOrder (hull );
74-
74+
7575 if (hull .size () == 2 ) {
7676 Point p1 = hull .get (0 );
7777 Point p2 = hull .get (1 );
7878 return new PointPair (p1 , p2 , distance (p1 , p2 ));
7979 }
80-
80+
8181 int n = hull .size ();
8282 PointPair maxPair = null ;
8383 double maxDistance = 0.0 ;
84-
84+
8585 int j = 1 ;
8686 for (int i = 0 ; i < n ; i ++) {
8787 Point p1 = hull .get (i );
88-
88+
8989 while (true ) {
9090 Point next = hull .get ((j + 1 ) % n );
9191 double dist1 = distance (p1 , hull .get (j ));
9292 double dist2 = distance (p1 , next );
93-
93+
9494 if (dist2 > dist1 ) {
9595 j = (j + 1 ) % n ;
9696 } else {
9797 break ;
9898 }
9999 }
100-
100+
101101 double dist = distance (p1 , hull .get (j ));
102102 if (dist > maxDistance ) {
103103 maxDistance = dist ;
104104 maxPair = new PointPair (p1 , hull .get (j ), dist );
105105 }
106106 }
107-
107+
108108 return maxPair ;
109109 }
110-
110+
111111 /**
112112 * Computes the width of a convex polygon using rotating calipers.
113113 * The width is the minimum distance between two parallel supporting lines.
114- *
114+ *
115115 * @param points List of points representing a convex polygon
116116 * @return The minimum width of the polygon
117117 * @throws IllegalArgumentException if points is null or has less than 2 points
@@ -120,40 +120,40 @@ public static double computeWidth(List<Point> points) {
120120 if (points == null || points .size () < 2 ) {
121121 throw new IllegalArgumentException ("Points list must contain at least 2 points" );
122122 }
123-
123+
124124 List <Point > hull = ConvexHull .convexHullRecursive (new ArrayList <>(points ));
125125 if (hull .size () < 2 ) {
126126 throw new IllegalArgumentException ("Convex hull must contain at least 2 points" );
127127 }
128-
128+
129129 hull = ensureCounterClockwiseOrder (hull );
130-
130+
131131 if (hull .size () == 2 ) {
132132 return 0.0 ;
133133 }
134-
134+
135135 int n = hull .size ();
136136 double minWidth = Double .MAX_VALUE ;
137-
137+
138138 // Use rotating calipers to find minimum width
139139 for (int i = 0 ; i < n ; i ++) {
140140 Point p1 = hull .get (i );
141141 Point p2 = hull .get ((i + 1 ) % n );
142-
142+
143143 // Find the antipodal point for this edge
144144 int j = findAntipodalPoint (hull , i );
145-
145+
146146 // Compute width as distance between parallel lines
147147 double width = distanceToLine (p1 , p2 , hull .get (j ));
148148 minWidth = Math .min (minWidth , width );
149149 }
150-
150+
151151 return minWidth ;
152152 }
153-
153+
154154 /**
155155 * Computes the minimum-area bounding rectangle of a convex polygon using rotating calipers.
156- *
156+ *
157157 * @param points List of points representing a convex polygon
158158 * @return Rectangle containing the minimum-area bounding rectangle
159159 * @throws IllegalArgumentException if points is null or has less than 2 points
@@ -162,71 +162,71 @@ public static Rectangle computeMinimumAreaBoundingRectangle(List<Point> points)
162162 if (points == null || points .size () < 2 ) {
163163 throw new IllegalArgumentException ("Points list must contain at least 2 points" );
164164 }
165-
165+
166166 List <Point > hull = ConvexHull .convexHullRecursive (new ArrayList <>(points ));
167167 if (hull .size () < 2 ) {
168168 throw new IllegalArgumentException ("Convex hull must contain at least 2 points" );
169169 }
170-
170+
171171 hull = ensureCounterClockwiseOrder (hull );
172-
172+
173173 if (hull .size () == 2 ) {
174174 Point p1 = hull .get (0 );
175175 Point p2 = hull .get (1 );
176176 return new Rectangle (p1 , p2 , 0.0 );
177177 }
178-
178+
179179 int n = hull .size ();
180180 double minArea = Double .MAX_VALUE ;
181181 Rectangle bestRectangle = null ;
182-
182+
183183 for (int i = 0 ; i < n ; i ++) {
184184 Point p1 = hull .get (i );
185185 Point p2 = hull .get ((i + 1 ) % n );
186-
186+
187187 int j = findAntipodalPoint (hull , i );
188-
188+
189189 double edgeLength = distance (p1 , p2 );
190190 double height = distanceToLine (p1 , p2 , hull .get (j ));
191-
191+
192192 double area = edgeLength * height ;
193-
193+
194194 if (area < minArea ) {
195195 minArea = area ;
196196 Point bottomLeft = computeRectangleCorner (p1 , p2 , hull .get (j ), true );
197197 Point topRight = computeRectangleCorner (p1 , p2 , hull .get (j ), false );
198198 bestRectangle = new Rectangle (bottomLeft , topRight , area );
199199 }
200200 }
201-
201+
202202 return bestRectangle ;
203203 }
204-
204+
205205 /**
206206 * Finds the antipodal point for a given edge using rotating calipers.
207207 */
208208 private static int findAntipodalPoint (List <Point > hull , int edgeStart ) {
209209 int n = hull .size ();
210210 int j = (edgeStart + 1 ) % n ;
211-
211+
212212 Point p1 = hull .get (edgeStart );
213213 Point p2 = hull .get ((edgeStart + 1 ) % n );
214-
214+
215215 while (true ) {
216216 Point next = hull .get ((j + 1 ) % n );
217217 double dist1 = distanceToLine (p1 , p2 , hull .get (j ));
218218 double dist2 = distanceToLine (p1 , p2 , next );
219-
219+
220220 if (dist2 > dist1 ) {
221221 j = (j + 1 ) % n ;
222222 } else {
223223 break ;
224224 }
225225 }
226-
226+
227227 return j ;
228228 }
229-
229+
230230 /**
231231 * Computes a corner of the bounding rectangle.
232232 */
@@ -235,14 +235,14 @@ private static Point computeRectangleCorner(Point p1, Point p2, Point antipodal,
235235 int maxX = Math .max (Math .max (p1 .x (), p2 .x ()), antipodal .x ());
236236 int minY = Math .min (Math .min (p1 .y (), p2 .y ()), antipodal .y ());
237237 int maxY = Math .max (Math .max (p1 .y (), p2 .y ()), antipodal .y ());
238-
238+
239239 if (isBottomLeft ) {
240240 return new Point (minX , minY );
241241 } else {
242242 return new Point (maxX , maxY );
243243 }
244244 }
245-
245+
246246 /**
247247 * Computes the Euclidean distance between two points.
248248 */
@@ -251,27 +251,27 @@ private static double distance(Point p1, Point p2) {
251251 int dy = p2 .y () - p1 .y ();
252252 return Math .sqrt (dx * dx + dy * dy );
253253 }
254-
254+
255255 /**
256256 * Computes the perpendicular distance from a point to a line defined by two points.
257257 */
258258 private static double distanceToLine (Point lineStart , Point lineEnd , Point point ) {
259259 int dx = lineEnd .x () - lineStart .x ();
260260 int dy = lineEnd .y () - lineStart .y ();
261-
261+
262262 if (dx == 0 && dy == 0 ) {
263263 return distance (lineStart , point );
264264 }
265-
265+
266266 int px = point .x () - lineStart .x ();
267267 int py = point .y () - lineStart .y ();
268-
268+
269269 double crossProduct = Math .abs (px * dy - py * dx );
270270 double lineLength = Math .sqrt (dx * dx + dy * dy );
271-
271+
272272 return crossProduct / lineLength ;
273273 }
274-
274+
275275 /**
276276 * Ensures the hull points are in counter-clockwise order for rotating calipers.
277277 * The convex hull algorithm returns points sorted by natural order, but rotating calipers
@@ -281,7 +281,7 @@ private static List<Point> ensureCounterClockwiseOrder(List<Point> hull) {
281281 if (hull .size () <= 2 ) {
282282 return hull ;
283283 }
284-
284+
285285 Point bottomMost = hull .get (0 );
286286 int bottomIndex = 0 ;
287287 for (int i = 1 ; i < hull .size (); i ++) {
@@ -291,23 +291,23 @@ private static List<Point> ensureCounterClockwiseOrder(List<Point> hull) {
291291 bottomIndex = i ;
292292 }
293293 }
294-
294+
295295 List <Point > orderedHull = new ArrayList <>();
296296 for (int i = 0 ; i < hull .size (); i ++) {
297297 orderedHull .add (hull .get ((bottomIndex + i ) % hull .size ()));
298298 }
299-
299+
300300 if (orderedHull .size () >= 3 ) {
301301 Point p1 = orderedHull .get (0 );
302302 Point p2 = orderedHull .get (1 );
303303 Point p3 = orderedHull .get (2 );
304-
304+
305305 if (Point .orientation (p1 , p2 , p3 ) < 0 ) {
306306 Collections .reverse (orderedHull );
307307 Collections .rotate (orderedHull , 1 );
308308 }
309309 }
310-
310+
311311 return orderedHull ;
312312 }
313313}
0 commit comments