1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.hipparchus.geometry.euclidean.twod.hull;
18
19 import java.io.Serializable;
20
21 import org.hipparchus.exception.LocalizedCoreFormats;
22 import org.hipparchus.exception.MathIllegalArgumentException;
23 import org.hipparchus.geometry.LocalizedGeometryFormats;
24 import org.hipparchus.geometry.euclidean.twod.Euclidean2D;
25 import org.hipparchus.geometry.euclidean.twod.Line;
26 import org.hipparchus.geometry.euclidean.twod.Segment;
27 import org.hipparchus.geometry.euclidean.twod.SubLine;
28 import org.hipparchus.geometry.euclidean.twod.Vector2D;
29 import org.hipparchus.geometry.hull.ConvexHull;
30 import org.hipparchus.geometry.partitioning.Region;
31 import org.hipparchus.geometry.partitioning.RegionFactory;
32 import org.hipparchus.util.MathArrays;
33 import org.hipparchus.util.Precision;
34
35
36
37
38
39 public class ConvexHull2D implements ConvexHull<Euclidean2D, Vector2D, Line, SubLine>, Serializable {
40
41
42 private static final long serialVersionUID = 20140129L;
43
44
45 private final Vector2D[] vertices;
46
47
48 private final double tolerance;
49
50
51
52
53
54 private transient Segment[] lineSegments;
55
56
57
58
59
60
61
62 public ConvexHull2D(final Vector2D[] vertices, final double tolerance)
63 throws MathIllegalArgumentException {
64
65
66 this.tolerance = tolerance;
67
68 if (!isConvex(vertices)) {
69 throw new MathIllegalArgumentException(LocalizedGeometryFormats.NOT_CONVEX);
70 }
71
72 this.vertices = vertices.clone();
73 }
74
75
76
77
78
79
80 private boolean isConvex(final Vector2D[] hullVertices) {
81 if (hullVertices.length < 3) {
82 return true;
83 }
84
85 int sign = 0;
86 for (int i = 0; i < hullVertices.length; i++) {
87 final Vector2D p1 = hullVertices[i == 0 ? hullVertices.length - 1 : i - 1];
88 final Vector2D p2 = hullVertices[i];
89 final Vector2D p3 = hullVertices[i == hullVertices.length - 1 ? 0 : i + 1];
90
91 final Vector2D d1 = p2.subtract(p1);
92 final Vector2D d2 = p3.subtract(p2);
93
94 final double crossProduct = MathArrays.linearCombination(d1.getX(), d2.getY(), -d1.getY(), d2.getX());
95 final int cmp = Precision.compareTo(crossProduct, 0.0, tolerance);
96
97 if (cmp != 0.0) {
98 if (sign != 0.0 && cmp != sign) {
99 return false;
100 }
101 sign = cmp;
102 }
103 }
104
105 return true;
106 }
107
108
109 @Override
110 public Vector2D[] getVertices() {
111 return vertices.clone();
112 }
113
114
115
116
117
118 public Segment[] getLineSegments() {
119 return retrieveLineSegments().clone();
120 }
121
122
123
124
125
126
127 private Segment[] retrieveLineSegments() {
128 if (lineSegments == null) {
129
130 final int size = vertices.length;
131 if (size <= 1) {
132 this.lineSegments = new Segment[0];
133 } else if (size == 2) {
134 this.lineSegments = new Segment[1];
135 final Vector2D p1 = vertices[0];
136 final Vector2D p2 = vertices[1];
137 this.lineSegments[0] = new Segment(p1, p2, new Line(p1, p2, tolerance));
138 } else {
139 this.lineSegments = new Segment[size];
140 Vector2D firstPoint = null;
141 Vector2D lastPoint = null;
142 int index = 0;
143 for (Vector2D point : vertices) {
144 if (lastPoint == null) {
145 firstPoint = point;
146 lastPoint = point;
147 } else {
148 this.lineSegments[index++] =
149 new Segment(lastPoint, point, new Line(lastPoint, point, tolerance));
150 lastPoint = point;
151 }
152 }
153 this.lineSegments[index] =
154 new Segment(lastPoint, firstPoint, new Line(lastPoint, firstPoint, tolerance));
155 }
156 }
157 return lineSegments;
158 }
159
160
161 @Override
162 public Region<Euclidean2D, Vector2D, Line, SubLine> createRegion() throws MathIllegalArgumentException {
163 if (vertices.length < 3) {
164 throw new MathIllegalArgumentException(LocalizedCoreFormats.INSUFFICIENT_DATA);
165 }
166 final RegionFactory<Euclidean2D, Vector2D, Line, SubLine> factory = new RegionFactory<>();
167 final Segment[] segments = retrieveLineSegments();
168 final Line[] lineArray = new Line[segments.length];
169 for (int i = 0; i < segments.length; i++) {
170 lineArray[i] = segments[i].getLine();
171 }
172 return factory.buildConvex(lineArray);
173 }
174 }