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