1 |
|
---|
2 |
|
---|
3 | /*
|
---|
4 | * The JTS Topology Suite is a collection of Java classes that
|
---|
5 | * implement the fundamental operations required to validate a given
|
---|
6 | * geo-spatial data set to a known topological specification.
|
---|
7 | *
|
---|
8 | * Copyright (C) 2001 Vivid Solutions
|
---|
9 | *
|
---|
10 | * This library is free software; you can redistribute it and/or
|
---|
11 | * modify it under the terms of the GNU Lesser General Public
|
---|
12 | * License as published by the Free Software Foundation; either
|
---|
13 | * version 2.1 of the License, or (at your option) any later version.
|
---|
14 | *
|
---|
15 | * This library is distributed in the hope that it will be useful,
|
---|
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
18 | * Lesser General Public License for more details.
|
---|
19 | *
|
---|
20 | * You should have received a copy of the GNU Lesser General Public
|
---|
21 | * License along with this library; if not, write to the Free Software
|
---|
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
23 | *
|
---|
24 | * For more information, contact:
|
---|
25 | *
|
---|
26 | * Vivid Solutions
|
---|
27 | * Suite #1A
|
---|
28 | * 2328 Government Street
|
---|
29 | * Victoria BC V8T 5G5
|
---|
30 | * Canada
|
---|
31 | *
|
---|
32 | * (250)385-6040
|
---|
33 | * www.vividsolutions.com
|
---|
34 | */
|
---|
35 | package com.vividsolutions.jts.algorithm;
|
---|
36 | import java.util.ArrayList;
|
---|
37 | import java.util.Arrays;
|
---|
38 | import java.util.Comparator;
|
---|
39 | import java.util.Stack;
|
---|
40 | import java.util.TreeSet;
|
---|
41 |
|
---|
42 | import com.vividsolutions.jts.geom.Coordinate;
|
---|
43 | import com.vividsolutions.jts.geom.CoordinateArrays;
|
---|
44 | import com.vividsolutions.jts.geom.CoordinateList;
|
---|
45 | import com.vividsolutions.jts.geom.Geometry;
|
---|
46 | import com.vividsolutions.jts.geom.GeometryCollection;
|
---|
47 | import com.vividsolutions.jts.geom.GeometryFactory;
|
---|
48 | import com.vividsolutions.jts.geom.LineString;
|
---|
49 | import com.vividsolutions.jts.geom.LinearRing;
|
---|
50 | import com.vividsolutions.jts.geom.Point;
|
---|
51 | import com.vividsolutions.jts.geom.Polygon;
|
---|
52 | import com.vividsolutions.jts.util.Assert;
|
---|
53 | import com.vividsolutions.jts.util.UniqueCoordinateArrayFilter;
|
---|
54 |
|
---|
55 | /**
|
---|
56 | * Computes the convex hull of a {@link Geometry}.
|
---|
57 | * The convex hull is the smallest convex Geometry that contains all the
|
---|
58 | * points in the input Geometry.
|
---|
59 | * <p>
|
---|
60 | * Uses the Graham Scan algorithm.
|
---|
61 | *
|
---|
62 | *@version 1.7
|
---|
63 | */
|
---|
64 | public class ConvexHull
|
---|
65 | {
|
---|
66 | private GeometryFactory geomFactory;
|
---|
67 | private Coordinate[] inputPts;
|
---|
68 |
|
---|
69 | /**
|
---|
70 | * Create a new convex hull construction for the input {@link Geometry}.
|
---|
71 | */
|
---|
72 | public ConvexHull(Geometry geometry)
|
---|
73 | {
|
---|
74 | this(extractCoordinates(geometry), geometry.getFactory());
|
---|
75 | }
|
---|
76 | /**
|
---|
77 | * Create a new convex hull construction for the input {@link Coordinate} array.
|
---|
78 | */
|
---|
79 | public ConvexHull(Coordinate[] pts, GeometryFactory geomFactory)
|
---|
80 | {
|
---|
81 | inputPts = pts;
|
---|
82 | this.geomFactory = geomFactory;
|
---|
83 | }
|
---|
84 |
|
---|
85 | private static Coordinate[] extractCoordinates(Geometry geom)
|
---|
86 | {
|
---|
87 | UniqueCoordinateArrayFilter filter = new UniqueCoordinateArrayFilter();
|
---|
88 | geom.apply(filter);
|
---|
89 | return filter.getCoordinates();
|
---|
90 | }
|
---|
91 |
|
---|
92 | /**
|
---|
93 | * Returns a {@link Geometry} that represents the convex hull of the input
|
---|
94 | * geometry.
|
---|
95 | * The returned geometry contains the minimal number of points needed to
|
---|
96 | * represent the convex hull. In particular, no more than two consecutive
|
---|
97 | * points will be collinear.
|
---|
98 | *
|
---|
99 | * @return if the convex hull contains 3 or more points, a {@link Polygon};
|
---|
100 | * 2 points, a {@link LineString};
|
---|
101 | * 1 point, a {@link Point};
|
---|
102 | * 0 points, an empty {@link GeometryCollection}.
|
---|
103 | */
|
---|
104 | public Geometry getConvexHull() {
|
---|
105 |
|
---|
106 | if (inputPts.length == 0) {
|
---|
107 | return geomFactory.createGeometryCollection(null);
|
---|
108 | }
|
---|
109 | if (inputPts.length == 1) {
|
---|
110 | return geomFactory.createPoint(inputPts[0]);
|
---|
111 | }
|
---|
112 | if (inputPts.length == 2) {
|
---|
113 | return geomFactory.createLineString(inputPts);
|
---|
114 | }
|
---|
115 |
|
---|
116 | Coordinate[] reducedPts = inputPts;
|
---|
117 | // use heuristic to reduce points, if large
|
---|
118 | if (inputPts.length > 50) {
|
---|
119 | reducedPts = reduce(inputPts);
|
---|
120 | }
|
---|
121 | // sort points for Graham scan.
|
---|
122 | Coordinate[] sortedPts = preSort(reducedPts);
|
---|
123 |
|
---|
124 | // Use Graham scan to find convex hull.
|
---|
125 | Stack cHS = grahamScan(sortedPts);
|
---|
126 |
|
---|
127 | // Convert stack to an array.
|
---|
128 | Coordinate[] cH = toCoordinateArray(cHS);
|
---|
129 |
|
---|
130 | // Convert array to appropriate output geometry.
|
---|
131 | return lineOrPolygon(cH);
|
---|
132 | }
|
---|
133 |
|
---|
134 | /**
|
---|
135 | * An alternative to Stack.toArray, which is not present in earlier versions
|
---|
136 | * of Java.
|
---|
137 | */
|
---|
138 | protected Coordinate[] toCoordinateArray(Stack stack) {
|
---|
139 | Coordinate[] coordinates = new Coordinate[stack.size()];
|
---|
140 | for (int i = 0; i < stack.size(); i++) {
|
---|
141 | Coordinate coordinate = (Coordinate) stack.get(i);
|
---|
142 | coordinates[i] = coordinate;
|
---|
143 | }
|
---|
144 | return coordinates;
|
---|
145 | }
|
---|
146 |
|
---|
147 | /**
|
---|
148 | * Uses a heuristic to reduce the number of points scanned
|
---|
149 | * to compute the hull.
|
---|
150 | * The heuristic is to find a polygon guaranteed to
|
---|
151 | * be in (or on) the hull, and eliminate all points inside it.
|
---|
152 | * A quadrilateral defined by the extremal points
|
---|
153 | * in the four orthogonal directions
|
---|
154 | * can be used, but even more inclusive is
|
---|
155 | * to use an octilateral defined by the points in the 8 cardinal directions.
|
---|
156 | * <p>
|
---|
157 | * Note that even if the method used to determine the polygon vertices
|
---|
158 | * is not 100% robust, this does not affect the robustness of the convex hull.
|
---|
159 | * <p>
|
---|
160 | * To satisfy the requirements of the Graham Scan algorithm,
|
---|
161 | * the returned array has at least 3 entries.
|
---|
162 | *
|
---|
163 | * @param pts the points to reduce
|
---|
164 | * @return the reduced list of points (at least 3)
|
---|
165 | */
|
---|
166 | private Coordinate[] reduce(Coordinate[] inputPts)
|
---|
167 | {
|
---|
168 | //Coordinate[] polyPts = computeQuad(inputPts);
|
---|
169 | Coordinate[] polyPts = computeOctRing(inputPts);
|
---|
170 | //Coordinate[] polyPts = null;
|
---|
171 |
|
---|
172 | // unable to compute interior polygon for some reason
|
---|
173 | if (polyPts == null)
|
---|
174 | return inputPts;
|
---|
175 |
|
---|
176 | // LinearRing ring = geomFactory.createLinearRing(polyPts);
|
---|
177 | // System.out.println(ring);
|
---|
178 |
|
---|
179 | // add points defining polygon
|
---|
180 | TreeSet reducedSet = new TreeSet();
|
---|
181 | for (int i = 0; i < polyPts.length; i++) {
|
---|
182 | reducedSet.add(polyPts[i]);
|
---|
183 | }
|
---|
184 | /**
|
---|
185 | * Add all unique points not in the interior poly.
|
---|
186 | * CGAlgorithms.isPointInRing is not defined for points actually on the ring,
|
---|
187 | * but this doesn't matter since the points of the interior polygon
|
---|
188 | * are forced to be in the reduced set.
|
---|
189 | */
|
---|
190 | for (int i = 0; i < inputPts.length; i++) {
|
---|
191 | if (! CGAlgorithms.isPointInRing(inputPts[i], polyPts)) {
|
---|
192 | reducedSet.add(inputPts[i]);
|
---|
193 | }
|
---|
194 | }
|
---|
195 | Coordinate[] reducedPts = CoordinateArrays.toCoordinateArray(reducedSet);
|
---|
196 |
|
---|
197 | // ensure that computed array has at least 3 points (not necessarily unique)
|
---|
198 | if (reducedPts.length < 3)
|
---|
199 | return padArray3(reducedPts);
|
---|
200 | return reducedPts;
|
---|
201 | }
|
---|
202 |
|
---|
203 | private Coordinate[] padArray3(Coordinate[] pts)
|
---|
204 | {
|
---|
205 | Coordinate[] pad = new Coordinate[3];
|
---|
206 | for (int i = 0; i < pad.length; i++) {
|
---|
207 | if (i < pts.length) {
|
---|
208 | pad[i] = pts[i];
|
---|
209 | }
|
---|
210 | else
|
---|
211 | pad[i] = pts[0];
|
---|
212 | }
|
---|
213 | return pad;
|
---|
214 | }
|
---|
215 |
|
---|
216 | private Coordinate[] preSort(Coordinate[] pts) {
|
---|
217 | Coordinate t;
|
---|
218 |
|
---|
219 | // find the lowest point in the set. If two or more points have
|
---|
220 | // the same minimum y coordinate choose the one with the minimu x.
|
---|
221 | // This focal point is put in array location pts[0].
|
---|
222 | for (int i = 1; i < pts.length; i++) {
|
---|
223 | if ((pts[i].y < pts[0].y) || ((pts[i].y == pts[0].y) && (pts[i].x < pts[0].x))) {
|
---|
224 | t = pts[0];
|
---|
225 | pts[0] = pts[i];
|
---|
226 | pts[i] = t;
|
---|
227 | }
|
---|
228 | }
|
---|
229 |
|
---|
230 | // sort the points radially around the focal point.
|
---|
231 | Arrays.sort(pts, 1, pts.length, new RadialComparator(pts[0]));
|
---|
232 |
|
---|
233 | //radialSort(pts);
|
---|
234 | return pts;
|
---|
235 | }
|
---|
236 |
|
---|
237 | /**
|
---|
238 | * Uses the Graham Scan algorithm to compute the convex hull vertices.
|
---|
239 | *
|
---|
240 | * @param c a list of points, with at least 3 entries
|
---|
241 | * @return a Stack containing the ordered points of the convex hull ring
|
---|
242 | */
|
---|
243 | private Stack grahamScan(Coordinate[] c) {
|
---|
244 | Coordinate p;
|
---|
245 | Stack ps = new Stack();
|
---|
246 | p = (Coordinate) ps.push(c[0]);
|
---|
247 | p = (Coordinate) ps.push(c[1]);
|
---|
248 | p = (Coordinate) ps.push(c[2]);
|
---|
249 | for (int i = 3; i < c.length; i++) {
|
---|
250 | p = (Coordinate) ps.pop();
|
---|
251 | while (CGAlgorithms.computeOrientation((Coordinate) ps.peek(), p, c[i]) > 0) {
|
---|
252 | p = (Coordinate) ps.pop();
|
---|
253 | }
|
---|
254 | p = (Coordinate) ps.push(p);
|
---|
255 | p = (Coordinate) ps.push(c[i]);
|
---|
256 | }
|
---|
257 | p = (Coordinate) ps.push(c[0]);
|
---|
258 | return ps;
|
---|
259 | }
|
---|
260 |
|
---|
261 | /**
|
---|
262 | *@return whether the three coordinates are collinear and c2 lies between
|
---|
263 | * c1 and c3 inclusive
|
---|
264 | */
|
---|
265 | private boolean isBetween(Coordinate c1, Coordinate c2, Coordinate c3) {
|
---|
266 | if (CGAlgorithms.computeOrientation(c1, c2, c3) != 0) {
|
---|
267 | return false;
|
---|
268 | }
|
---|
269 | if (c1.x != c3.x) {
|
---|
270 | if (c1.x <= c2.x && c2.x <= c3.x) {
|
---|
271 | return true;
|
---|
272 | }
|
---|
273 | if (c3.x <= c2.x && c2.x <= c1.x) {
|
---|
274 | return true;
|
---|
275 | }
|
---|
276 | }
|
---|
277 | if (c1.y != c3.y) {
|
---|
278 | if (c1.y <= c2.y && c2.y <= c3.y) {
|
---|
279 | return true;
|
---|
280 | }
|
---|
281 | if (c3.y <= c2.y && c2.y <= c1.y) {
|
---|
282 | return true;
|
---|
283 | }
|
---|
284 | }
|
---|
285 | return false;
|
---|
286 | }
|
---|
287 |
|
---|
288 | private Coordinate[] computeOctRing(Coordinate[] inputPts) {
|
---|
289 | Coordinate[] octPts = computeOctPts(inputPts);
|
---|
290 | CoordinateList coordList = new CoordinateList();
|
---|
291 | coordList.add(octPts, false);
|
---|
292 |
|
---|
293 | // points must all lie in a line
|
---|
294 | if (coordList.size() < 3) {
|
---|
295 | return null;
|
---|
296 | }
|
---|
297 | coordList.closeRing();
|
---|
298 | return coordList.toCoordinateArray();
|
---|
299 | }
|
---|
300 |
|
---|
301 | private Coordinate[] computeOctPts(Coordinate[] inputPts)
|
---|
302 | {
|
---|
303 | Coordinate[] pts = new Coordinate[8];
|
---|
304 | for (int j = 0; j < pts.length; j++) {
|
---|
305 | pts[j] = inputPts[0];
|
---|
306 | }
|
---|
307 | for (int i = 1; i < inputPts.length; i++) {
|
---|
308 | if (inputPts[i].x < pts[0].x) {
|
---|
309 | pts[0] = inputPts[i];
|
---|
310 | }
|
---|
311 | if (inputPts[i].x - inputPts[i].y < pts[1].x - pts[1].y) {
|
---|
312 | pts[1] = inputPts[i];
|
---|
313 | }
|
---|
314 | if (inputPts[i].y > pts[2].y) {
|
---|
315 | pts[2] = inputPts[i];
|
---|
316 | }
|
---|
317 | if (inputPts[i].x + inputPts[i].y > pts[3].x + pts[3].y) {
|
---|
318 | pts[3] = inputPts[i];
|
---|
319 | }
|
---|
320 | if (inputPts[i].x > pts[4].x) {
|
---|
321 | pts[4] = inputPts[i];
|
---|
322 | }
|
---|
323 | if (inputPts[i].x - inputPts[i].y > pts[5].x - pts[5].y) {
|
---|
324 | pts[5] = inputPts[i];
|
---|
325 | }
|
---|
326 | if (inputPts[i].y < pts[6].y) {
|
---|
327 | pts[6] = inputPts[i];
|
---|
328 | }
|
---|
329 | if (inputPts[i].x + inputPts[i].y < pts[7].x + pts[7].y) {
|
---|
330 | pts[7] = inputPts[i];
|
---|
331 | }
|
---|
332 | }
|
---|
333 | return pts;
|
---|
334 |
|
---|
335 | }
|
---|
336 |
|
---|
337 | /*
|
---|
338 | // MD - no longer used, but keep for reference purposes
|
---|
339 | private Coordinate[] computeQuad(Coordinate[] inputPts) {
|
---|
340 | BigQuad bigQuad = bigQuad(inputPts);
|
---|
341 |
|
---|
342 | // Build a linear ring defining a big poly.
|
---|
343 | ArrayList bigPoly = new ArrayList();
|
---|
344 | bigPoly.add(bigQuad.westmost);
|
---|
345 | if (! bigPoly.contains(bigQuad.northmost)) {
|
---|
346 | bigPoly.add(bigQuad.northmost);
|
---|
347 | }
|
---|
348 | if (! bigPoly.contains(bigQuad.eastmost)) {
|
---|
349 | bigPoly.add(bigQuad.eastmost);
|
---|
350 | }
|
---|
351 | if (! bigPoly.contains(bigQuad.southmost)) {
|
---|
352 | bigPoly.add(bigQuad.southmost);
|
---|
353 | }
|
---|
354 | // points must all lie in a line
|
---|
355 | if (bigPoly.size() < 3) {
|
---|
356 | return null;
|
---|
357 | }
|
---|
358 | // closing point
|
---|
359 | bigPoly.add(bigQuad.westmost);
|
---|
360 |
|
---|
361 | Coordinate[] bigPolyArray = CoordinateArrays.toCoordinateArray(bigPoly);
|
---|
362 |
|
---|
363 | return bigPolyArray;
|
---|
364 | }
|
---|
365 |
|
---|
366 | private BigQuad bigQuad(Coordinate[] pts) {
|
---|
367 | BigQuad bigQuad = new BigQuad();
|
---|
368 | bigQuad.northmost = pts[0];
|
---|
369 | bigQuad.southmost = pts[0];
|
---|
370 | bigQuad.westmost = pts[0];
|
---|
371 | bigQuad.eastmost = pts[0];
|
---|
372 | for (int i = 1; i < pts.length; i++) {
|
---|
373 | if (pts[i].x < bigQuad.westmost.x) {
|
---|
374 | bigQuad.westmost = pts[i];
|
---|
375 | }
|
---|
376 | if (pts[i].x > bigQuad.eastmost.x) {
|
---|
377 | bigQuad.eastmost = pts[i];
|
---|
378 | }
|
---|
379 | if (pts[i].y < bigQuad.southmost.y) {
|
---|
380 | bigQuad.southmost = pts[i];
|
---|
381 | }
|
---|
382 | if (pts[i].y > bigQuad.northmost.y) {
|
---|
383 | bigQuad.northmost = pts[i];
|
---|
384 | }
|
---|
385 | }
|
---|
386 | return bigQuad;
|
---|
387 | }
|
---|
388 |
|
---|
389 | private static class BigQuad {
|
---|
390 | public Coordinate northmost;
|
---|
391 | public Coordinate southmost;
|
---|
392 | public Coordinate westmost;
|
---|
393 | public Coordinate eastmost;
|
---|
394 | }
|
---|
395 | */
|
---|
396 |
|
---|
397 | /**
|
---|
398 | *@param vertices the vertices of a linear ring, which may or may not be
|
---|
399 | * flattened (i.e. vertices collinear)
|
---|
400 | *@return a 2-vertex <code>LineString</code> if the vertices are
|
---|
401 | * collinear; otherwise, a <code>Polygon</code> with unnecessary
|
---|
402 | * (collinear) vertices removed
|
---|
403 | */
|
---|
404 | private Geometry lineOrPolygon(Coordinate[] coordinates) {
|
---|
405 |
|
---|
406 | coordinates = cleanRing(coordinates);
|
---|
407 | if (coordinates.length == 3) {
|
---|
408 | return geomFactory.createLineString(new Coordinate[]{coordinates[0], coordinates[1]});
|
---|
409 | // return new LineString(new Coordinate[]{coordinates[0], coordinates[1]},
|
---|
410 | // geometry.getPrecisionModel(), geometry.getSRID());
|
---|
411 | }
|
---|
412 | LinearRing linearRing = geomFactory.createLinearRing(coordinates);
|
---|
413 | return geomFactory.createPolygon(linearRing, null);
|
---|
414 | }
|
---|
415 |
|
---|
416 | /**
|
---|
417 | *@param vertices the vertices of a linear ring, which may or may not be
|
---|
418 | * flattened (i.e. vertices collinear)
|
---|
419 | *@return the coordinates with unnecessary (collinear) vertices
|
---|
420 | * removed
|
---|
421 | */
|
---|
422 | private Coordinate[] cleanRing(Coordinate[] original) {
|
---|
423 | Assert.equals(original[0], original[original.length - 1]);
|
---|
424 | ArrayList cleanedRing = new ArrayList();
|
---|
425 | Coordinate previousDistinctCoordinate = null;
|
---|
426 | for (int i = 0; i <= original.length - 2; i++) {
|
---|
427 | Coordinate currentCoordinate = original[i];
|
---|
428 | Coordinate nextCoordinate = original[i+1];
|
---|
429 | if (currentCoordinate.equals(nextCoordinate)) {
|
---|
430 | continue;
|
---|
431 | }
|
---|
432 | if (previousDistinctCoordinate != null
|
---|
433 | && isBetween(previousDistinctCoordinate, currentCoordinate, nextCoordinate)) {
|
---|
434 | continue;
|
---|
435 | }
|
---|
436 | cleanedRing.add(currentCoordinate);
|
---|
437 | previousDistinctCoordinate = currentCoordinate;
|
---|
438 | }
|
---|
439 | cleanedRing.add(original[original.length - 1]);
|
---|
440 | Coordinate[] cleanedRingCoordinates = new Coordinate[cleanedRing.size()];
|
---|
441 | return (Coordinate[]) cleanedRing.toArray(cleanedRingCoordinates);
|
---|
442 | }
|
---|
443 |
|
---|
444 |
|
---|
445 | /**
|
---|
446 | * Compares {@link Coordinate}s for their angle and distance
|
---|
447 | * relative to an origin.
|
---|
448 | *
|
---|
449 | * @author Martin Davis
|
---|
450 | * @version 1.7
|
---|
451 | */
|
---|
452 | private static class RadialComparator
|
---|
453 | implements Comparator
|
---|
454 | {
|
---|
455 | private Coordinate origin;
|
---|
456 |
|
---|
457 | public RadialComparator(Coordinate origin)
|
---|
458 | {
|
---|
459 | this.origin = origin;
|
---|
460 | }
|
---|
461 | public int compare(Object o1, Object o2)
|
---|
462 | {
|
---|
463 | Coordinate p1 = (Coordinate) o1;
|
---|
464 | Coordinate p2 = (Coordinate) o2;
|
---|
465 | return polarCompare(origin, p1, p2);
|
---|
466 | }
|
---|
467 |
|
---|
468 | /**
|
---|
469 | * Given two points p and q compare them with respect to their radial
|
---|
470 | * ordering about point o. First checks radial ordering.
|
---|
471 | * If points are collinear, the comparison is based
|
---|
472 | * on their distance to the origin.
|
---|
473 | * <p>
|
---|
474 | * p < q iff
|
---|
475 | * <ul>
|
---|
476 | * <li>ang(o-p) < ang(o-q) (e.g. o-p-q is CCW)
|
---|
477 | * <li>or ang(o-p) == ang(o-q) && dist(o,p) < dist(o,q)
|
---|
478 | * </ul>
|
---|
479 | *
|
---|
480 | * @param o the origin
|
---|
481 | * @param p a point
|
---|
482 | * @param q another point
|
---|
483 | * @return -1, 0 or 1 depending on whether p is less than,
|
---|
484 | * equal to or greater than q
|
---|
485 | */
|
---|
486 | private static int polarCompare(Coordinate o, Coordinate p, Coordinate q)
|
---|
487 | {
|
---|
488 | double dxp = p.x - o.x;
|
---|
489 | double dyp = p.y - o.y;
|
---|
490 | double dxq = q.x - o.x;
|
---|
491 | double dyq = q.y - o.y;
|
---|
492 |
|
---|
493 | /*
|
---|
494 | // MD - non-robust
|
---|
495 | int result = 0;
|
---|
496 | double alph = Math.atan2(dxp, dyp);
|
---|
497 | double beta = Math.atan2(dxq, dyq);
|
---|
498 | if (alph < beta) {
|
---|
499 | result = -1;
|
---|
500 | }
|
---|
501 | if (alph > beta) {
|
---|
502 | result = 1;
|
---|
503 | }
|
---|
504 | if (result != 0) return result;
|
---|
505 | //*/
|
---|
506 |
|
---|
507 | int orient = CGAlgorithms.computeOrientation(o, p, q);
|
---|
508 |
|
---|
509 | if (orient == CGAlgorithms.COUNTERCLOCKWISE) return 1;
|
---|
510 | if (orient == CGAlgorithms.CLOCKWISE) return -1;
|
---|
511 |
|
---|
512 | // points are collinear - check distance
|
---|
513 | double op = dxp * dxp + dyp * dyp;
|
---|
514 | double oq = dxq * dxq + dyq * dyq;
|
---|
515 | if (op < oq) {
|
---|
516 | return -1;
|
---|
517 | }
|
---|
518 | if (op > oq) {
|
---|
519 | return 1;
|
---|
520 | }
|
---|
521 | return 0;
|
---|
522 | }
|
---|
523 |
|
---|
524 | }
|
---|
525 | }
|
---|