|
3 | 3 | import java.util.ArrayList; |
4 | 4 | import java.util.Collection; |
5 | 5 | import java.util.Collections; |
6 | | -import java.util.HashSet; |
7 | 6 | import java.util.List; |
8 | | -import java.util.Set; |
9 | 7 |
|
10 | 8 | /** |
11 | 9 | * A class implementing the Rotating Calipers algorithm for geometric computations on convex polygons. |
@@ -137,62 +135,63 @@ public static double computeWidth(Collection<Point> points) { |
137 | 135 | return 0.0; |
138 | 136 | } |
139 | 137 |
|
140 | | - int n = hull.size(); |
141 | 138 | double minWidth = Double.MAX_VALUE; |
| 139 | + int n = hull.size(); |
142 | 140 |
|
143 | | - // Generate all critical orientations for rotating calipers |
144 | | - Set<Double> angles = new HashSet<>(); |
145 | | - |
146 | | - // Add orientations perpendicular to each edge |
| 141 | + // Method 1: Check orientations perpendicular to edges |
147 | 142 | for (int i = 0; i < n; i++) { |
148 | 143 | Point p1 = hull.get(i); |
149 | 144 | Point p2 = hull.get((i + 1) % n); |
150 | 145 |
|
151 | 146 | double dx = p2.x() - p1.x(); |
152 | 147 | double dy = p2.y() - p1.y(); |
| 148 | + double edgeLen = Math.sqrt(dx * dx + dy * dy); |
| 149 | + |
| 150 | + if (edgeLen == 0) continue; |
| 151 | + |
| 152 | + // Perpendicular to edge direction (measurement direction) |
| 153 | + double perpX = -dy / edgeLen; |
| 154 | + double perpY = dx / edgeLen; |
| 155 | + |
| 156 | + double minProj = Double.MAX_VALUE; |
| 157 | + double maxProj = Double.MIN_VALUE; |
153 | 158 |
|
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); |
| 159 | + for (Point p : hull) { |
| 160 | + double proj = p.x() * perpX + p.y() * perpY; |
| 161 | + minProj = Math.min(minProj, proj); |
| 162 | + maxProj = Math.max(maxProj, proj); |
158 | 163 | } |
| 164 | + |
| 165 | + minWidth = Math.min(minWidth, maxProj - minProj); |
159 | 166 | } |
160 | 167 |
|
161 | | - // Add orientations defined by vertex pairs (for antipodal cases) |
| 168 | + // Method 2: Check orientations defined by vertex pairs |
162 | 169 | for (int i = 0; i < n; i++) { |
163 | 170 | for (int j = i + 1; j < n; j++) { |
164 | 171 | Point p1 = hull.get(i); |
165 | 172 | Point p2 = hull.get(j); |
166 | 173 |
|
167 | 174 | double dx = p2.x() - p1.x(); |
168 | 175 | double dy = p2.y() - p1.y(); |
| 176 | + double len = Math.sqrt(dx * dx + dy * dy); |
169 | 177 |
|
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); |
174 | | - } |
175 | | - } |
176 | | - } |
| 178 | + if (len == 0) continue; |
177 | 179 |
|
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); |
| 180 | + // Direction perpendicular to the line p1-p2 |
| 181 | + double perpX = -dy / len; |
| 182 | + double perpY = dx / len; |
183 | 183 |
|
184 | | - // Project all points onto this direction |
185 | | - double minProj = Double.MAX_VALUE; |
186 | | - double maxProj = Double.MIN_VALUE; |
| 184 | + double minProj = Double.MAX_VALUE; |
| 185 | + double maxProj = Double.MIN_VALUE; |
187 | 186 |
|
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 | | - } |
| 187 | + for (Point p : hull) { |
| 188 | + double proj = p.x() * perpX + p.y() * perpY; |
| 189 | + minProj = Math.min(minProj, proj); |
| 190 | + maxProj = Math.max(maxProj, proj); |
| 191 | + } |
193 | 192 |
|
194 | | - double width = maxProj - minProj; |
195 | | - minWidth = Math.min(minWidth, width); |
| 193 | + minWidth = Math.min(minWidth, maxProj - minProj); |
| 194 | + } |
196 | 195 | } |
197 | 196 |
|
198 | 197 | return minWidth; |
|
0 commit comments