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