1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.geometry.euclidean.twod.hull;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.List;
29
30 import org.hipparchus.geometry.euclidean.twod.Line;
31 import org.hipparchus.geometry.euclidean.twod.Vector2D;
32 import org.hipparchus.util.FastMath;
33 import org.hipparchus.util.Precision;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class MonotoneChain extends AbstractConvexHullGenerator2D {
55
56
57
58
59 public MonotoneChain() {
60 this(false);
61 }
62
63
64
65
66
67 public MonotoneChain(final boolean includeCollinearPoints) {
68 super(includeCollinearPoints);
69 }
70
71
72
73
74
75
76 public MonotoneChain(final boolean includeCollinearPoints, final double tolerance) {
77 super(includeCollinearPoints, tolerance);
78 }
79
80
81 @Override
82 public Collection<Vector2D> findHullVertices(final Collection<Vector2D> points) {
83
84 final List<Vector2D> pointsSortedByXAxis = new ArrayList<>(points);
85
86
87 Collections.sort(pointsSortedByXAxis, new Comparator<Vector2D>() {
88
89 @Override
90 public int compare(final Vector2D o1, final Vector2D o2) {
91 final double tolerance = getTolerance();
92
93
94 final int diff = Precision.compareTo(o1.getX(), o2.getX(), tolerance);
95 if (diff == 0) {
96 return Precision.compareTo(o1.getY(), o2.getY(), tolerance);
97 } else {
98 return diff;
99 }
100 }
101 });
102
103
104 final List<Vector2D> lowerHull = new ArrayList<>();
105 for (Vector2D p : pointsSortedByXAxis) {
106 updateHull(p, lowerHull);
107 }
108
109
110 final List<Vector2D> upperHull = new ArrayList<>();
111 for (int idx = pointsSortedByXAxis.size() - 1; idx >= 0; idx--) {
112 final Vector2D p = pointsSortedByXAxis.get(idx);
113 updateHull(p, upperHull);
114 }
115
116
117
118 final List<Vector2D> hullVertices = new ArrayList<>(lowerHull.size() + upperHull.size() - 2);
119 for (int idx = 0; idx < lowerHull.size() - 1; idx++) {
120 hullVertices.add(lowerHull.get(idx));
121 }
122 for (int idx = 0; idx < upperHull.size() - 1; idx++) {
123 hullVertices.add(upperHull.get(idx));
124 }
125
126
127 if (hullVertices.isEmpty() && ! lowerHull.isEmpty()) {
128 hullVertices.add(lowerHull.get(0));
129 }
130
131 return hullVertices;
132 }
133
134
135
136
137
138
139
140 private void updateHull(final Vector2D point, final List<Vector2D> hull) {
141 final double tolerance = getTolerance();
142
143 if (hull.size() == 1) {
144
145 final Vector2D p1 = hull.get(0);
146 if (p1.distance(point) < tolerance) {
147 return;
148 }
149 }
150
151 while (hull.size() >= 2) {
152 final int size = hull.size();
153 final Vector2D p1 = hull.get(size - 2);
154 final Vector2D p2 = hull.get(size - 1);
155
156 final double offset = new Line(p1, p2, tolerance).getOffset(point);
157 if (FastMath.abs(offset) < tolerance) {
158
159
160 final double distanceToCurrent = p1.distance(point);
161 if (distanceToCurrent < tolerance || p2.distance(point) < tolerance) {
162
163 return;
164 }
165
166 final double distanceToLast = p1.distance(p2);
167 if (isIncludeCollinearPoints()) {
168 final int index = distanceToCurrent < distanceToLast ? size - 1 : size;
169 hull.add(index, point);
170 } else {
171 if (distanceToCurrent > distanceToLast) {
172 hull.remove(size - 1);
173 hull.add(point);
174 }
175 }
176 return;
177 } else if (offset > 0) {
178 hull.remove(size - 1);
179 } else {
180 break;
181 }
182 }
183 hull.add(point);
184 }
185
186 }