1 package org.djutils.draw.line;
2
3 import java.util.Arrays;
4 import java.util.Iterator;
5 import java.util.Locale;
6
7 import org.djutils.draw.Drawable3d;
8 import org.djutils.draw.InvalidProjectionException;
9 import org.djutils.draw.bounds.Bounds3d;
10 import org.djutils.draw.point.DirectedPoint3d;
11 import org.djutils.draw.point.Point3d;
12 import org.djutils.exceptions.Throw;
13
14
15
16
17
18
19
20
21
22
23
24
25 public class LineSegment3d implements Drawable3d, LineSegment<Point3d, DirectedPoint3d>
26 {
27
28 @SuppressWarnings("checkstyle:visibilitymodifier")
29 public final double startX;
30
31
32 @SuppressWarnings("checkstyle:visibilitymodifier")
33 public final double startY;
34
35
36 @SuppressWarnings("checkstyle:visibilitymodifier")
37 public final double startZ;
38
39
40 @SuppressWarnings("checkstyle:visibilitymodifier")
41 public final double endX;
42
43
44 @SuppressWarnings("checkstyle:visibilitymodifier")
45 public final double endY;
46
47
48 @SuppressWarnings("checkstyle:visibilitymodifier")
49 public final double endZ;
50
51
52
53
54
55
56
57
58
59
60
61 public LineSegment3d(final double startX, final double startY, final double startZ, final double endX, final double endY,
62 final double endZ)
63 {
64 Throw.when(startX == endX && startY == endY && startZ == endZ, IllegalArgumentException.class,
65 "Start and end may not be equal");
66 this.startX = startX;
67 this.startY = startY;
68 this.startZ = startZ;
69 this.endX = endX;
70 this.endY = endY;
71 this.endZ = endZ;
72 }
73
74
75
76
77
78
79
80
81
82
83 public LineSegment3d(final Point3d start, final double endX, final double endY, final double endZ)
84 {
85 this(Throw.whenNull(start, "start").x, start.y, start.z, endX, endY, endZ);
86 }
87
88
89
90
91
92
93
94
95
96
97 public LineSegment3d(final double startX, final double startY, final double startZ, final Point3d end)
98 {
99 this(startX, startY, startZ, Throw.whenNull(end, "end").x, end.y, end.z);
100 }
101
102
103
104
105
106
107
108
109 public LineSegment3d(final Point3d start, final Point3d end)
110 {
111 this(Throw.whenNull(start, "start point may not be null").x, start.y, start.z,
112 Throw.whenNull(end, "end point may not be null").x, end.y, end.z);
113 }
114
115 @Override
116 public Point3d getStartPoint()
117 {
118 return new Point3d(this.startX, this.startY, this.startZ);
119 }
120
121 @Override
122 public Point3d getEndPoint()
123 {
124 return new Point3d(this.endX, this.endY, this.endZ);
125 }
126
127 @Override
128 public double getLength()
129 {
130
131 double dX = this.endX - this.startX;
132 double dY = this.endY - this.startY;
133 double dZ = this.endZ - this.startZ;
134 return Math.sqrt(dX * dX + dY * dY + dZ * dZ);
135 }
136
137 @Override
138 public Iterator<Point3d> iterator()
139 {
140 return Arrays.stream(new Point3d[] {getStartPoint(), getEndPoint()}).iterator();
141 }
142
143 @Override
144 public int size()
145 {
146 return 2;
147 }
148
149 @Override
150 public Bounds3d getAbsoluteBounds()
151 {
152 return new Bounds3d(Math.min(this.startX, this.endX), Math.max(this.startX, this.endX),
153 Math.min(this.startY, this.endY), Math.max(this.startY, this.endY), Math.min(this.startZ, this.endZ),
154 Math.max(this.startZ, this.endZ));
155 }
156
157 @Override
158 public LineSegment2d project() throws InvalidProjectionException
159 {
160 return new LineSegment2d(this.startX, this.startY, this.endX, this.endY);
161 }
162
163 @Override
164 public DirectedPoint3d getLocationExtended(final double position) throws IllegalArgumentException
165 {
166 Throw.whenNaN(position, "position");
167 Throw.when(Double.isInfinite(position), IllegalArgumentException.class, "position must be finite");
168 double dX = this.endX - this.startX;
169 double dY = this.endY - this.startY;
170 double dZ = this.endZ - this.startZ;
171 double length = Math.sqrt(dX * dX + dY * dY + dZ * dZ);
172 return new DirectedPoint3d(this.startX + position * dX / length, this.startY + position * dY / length,
173 this.startZ + position * dZ / length, Math.atan2(dZ, Math.hypot(dX, dY)), Math.atan2(dY, dX));
174 }
175
176 @Override
177 public Point3d closestPointOnSegment(final Point3d point)
178 {
179 Throw.whenNull(point, "point");
180 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, true, true);
181 }
182
183 @Override
184 public LineSegment3d reverse()
185 {
186 return new LineSegment3d(this.endX, this.endY, this.endZ, this.startX, this.startY, this.startZ);
187 }
188
189 @Override
190 public Point3d projectOrthogonal(final Point3d point)
191 {
192 Throw.whenNull(point, "point");
193 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null, null);
194 }
195
196 @Override
197 public Point3d projectOrthogonalExtended(final Point3d point)
198 {
199 Throw.whenNull(point, "point");
200 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
201 }
202
203 @Override
204 public double projectOrthogonalFractional(final Point3d point)
205 {
206 Throw.whenNull(point, "point");
207 return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null,
208 null);
209 }
210
211 @Override
212 public double projectOrthogonalFractionalExtended(final Point3d point)
213 {
214 Throw.whenNull(point, "point");
215 return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, false,
216 false);
217 }
218
219 @Override
220 public String toString()
221 {
222 return toString("%f", false);
223 }
224
225 @Override
226 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
227 {
228 String format = String.format("%1$s[startX=%2$s, startY=%2$s, startZ=%2$s - endX=%2%s, endY=%2$s, endZ=%2$s]",
229 doNotIncludeClassName ? "" : "LineSegment3d ", doubleFormat);
230 return String.format(Locale.US, format, this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
231 }
232
233 @Override
234 public int hashCode()
235 {
236 final int prime = 31;
237 int result = 1;
238 long temp;
239 temp = Double.doubleToLongBits(this.endX);
240 result = prime * result + (int) (temp ^ (temp >>> 32));
241 temp = Double.doubleToLongBits(this.endY);
242 result = prime * result + (int) (temp ^ (temp >>> 32));
243 temp = Double.doubleToLongBits(this.endZ);
244 result = prime * result + (int) (temp ^ (temp >>> 32));
245 temp = Double.doubleToLongBits(this.startX);
246 result = prime * result + (int) (temp ^ (temp >>> 32));
247 temp = Double.doubleToLongBits(this.startY);
248 result = prime * result + (int) (temp ^ (temp >>> 32));
249 temp = Double.doubleToLongBits(this.startZ);
250 result = prime * result + (int) (temp ^ (temp >>> 32));
251 return result;
252 }
253
254 @Override
255 @SuppressWarnings("checkstyle:needbraces")
256 public boolean equals(final Object obj)
257 {
258 if (this == obj)
259 return true;
260 if (obj == null)
261 return false;
262 if (getClass() != obj.getClass())
263 return false;
264 LineSegment3d other = (LineSegment3d) obj;
265 if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
266 return false;
267 if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
268 return false;
269 if (Double.doubleToLongBits(this.endZ) != Double.doubleToLongBits(other.endZ))
270 return false;
271 if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
272 return false;
273 if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
274 return false;
275 if (Double.doubleToLongBits(this.startZ) != Double.doubleToLongBits(other.startZ))
276 return false;
277 return true;
278 }
279
280 }