|
3 | 3 | import java.util.ArrayList; |
4 | 4 | import java.util.Collection; |
5 | 5 | import java.util.Collections; |
| 6 | +import java.util.HashSet; |
6 | 7 | import java.util.List; |
| 8 | +import java.util.Set; |
7 | 9 |
|
8 | 10 | /** |
9 | 11 | * A class implementing the Rotating Calipers algorithm for geometric computations on convex polygons. |
@@ -138,43 +140,61 @@ public static double computeWidth(Collection<Point> points) { |
138 | 140 | int n = hull.size(); |
139 | 141 | double minWidth = Double.MAX_VALUE; |
140 | 142 |
|
141 | | - // Check all orientations defined by pairs of hull points |
142 | | - // This ensures we find the true minimum width |
| 143 | + // Generate all critical orientations for rotating calipers |
| 144 | + Set<Double> angles = new HashSet<>(); |
| 145 | + |
| 146 | + // Add orientations perpendicular to each edge |
| 147 | + for (int i = 0; i < n; i++) { |
| 148 | + Point p1 = hull.get(i); |
| 149 | + Point p2 = hull.get((i + 1) % n); |
| 150 | + |
| 151 | + double dx = p2.x() - p1.x(); |
| 152 | + double dy = p2.y() - p1.y(); |
| 153 | + |
| 154 | + if (dx != 0 || dy != 0) { |
| 155 | + // Angle of the perpendicular to this edge |
| 156 | + double angle = Math.atan2(-dx, dy); // perpendicular to (dx,dy) is (-dy,dx) |
| 157 | + angles.add(angle); |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + // Add orientations defined by vertex pairs (for antipodal cases) |
143 | 162 | for (int i = 0; i < n; i++) { |
144 | 163 | for (int j = i + 1; j < n; j++) { |
145 | 164 | Point p1 = hull.get(i); |
146 | 165 | Point p2 = hull.get(j); |
147 | | - |
148 | | - // Calculate direction vector |
| 166 | + |
149 | 167 | double dx = p2.x() - p1.x(); |
150 | 168 | double dy = p2.y() - p1.y(); |
151 | | - double len = Math.sqrt(dx * dx + dy * dy); |
152 | | - |
153 | | - if (len == 0) continue; |
154 | | - |
155 | | - // Unit direction vector |
156 | | - double unitX = dx / len; |
157 | | - double unitY = dy / len; |
158 | | - |
159 | | - // Perpendicular unit vector |
160 | | - double perpX = -unitY; |
161 | | - double perpY = unitX; |
162 | | - |
163 | | - // Project all points onto the perpendicular direction |
164 | | - double minProj = Double.MAX_VALUE; |
165 | | - double maxProj = Double.MIN_VALUE; |
166 | | - |
167 | | - for (Point p : hull) { |
168 | | - double proj = p.x() * perpX + p.y() * perpY; |
169 | | - minProj = Math.min(minProj, proj); |
170 | | - maxProj = Math.max(maxProj, proj); |
| 169 | + |
| 170 | + if (dx != 0 || dy != 0) { |
| 171 | + // Angle perpendicular to the line from p1 to p2 |
| 172 | + double angle = Math.atan2(-dx, dy); |
| 173 | + angles.add(angle); |
171 | 174 | } |
172 | | - |
173 | | - double width = maxProj - minProj; |
174 | | - minWidth = Math.min(minWidth, width); |
175 | 175 | } |
176 | 176 | } |
177 | 177 |
|
| 178 | + // Test each critical orientation |
| 179 | + for (double angle : angles) { |
| 180 | + // Unit vector in the measurement direction |
| 181 | + double dirX = Math.sin(angle); |
| 182 | + double dirY = Math.cos(angle); |
| 183 | + |
| 184 | + // Project all points onto this direction |
| 185 | + double minProj = Double.MAX_VALUE; |
| 186 | + double maxProj = Double.MIN_VALUE; |
| 187 | + |
| 188 | + for (Point p : hull) { |
| 189 | + double proj = p.x() * dirX + p.y() * dirY; |
| 190 | + minProj = Math.min(minProj, proj); |
| 191 | + maxProj = Math.max(maxProj, proj); |
| 192 | + } |
| 193 | + |
| 194 | + double width = maxProj - minProj; |
| 195 | + minWidth = Math.min(minWidth, width); |
| 196 | + } |
| 197 | + |
178 | 198 | return minWidth; |
179 | 199 | } |
180 | 200 |
|
|
0 commit comments