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.base.AngleUtil;
8 import org.djutils.draw.DrawRuntimeException;
9 import org.djutils.draw.Drawable2d;
10 import org.djutils.draw.Space2d;
11 import org.djutils.draw.bounds.Bounds2d;
12 import org.djutils.draw.point.Point2d;
13 import org.djutils.exceptions.Throw;
14
15
16
17
18
19
20
21
22
23
24 public class Ray2d extends Point2d implements Drawable2d, Ray<Ray2d, Point2d, Space2d>
25 {
26
27 private static final long serialVersionUID = 20210119L;
28
29
30 @SuppressWarnings("checkstyle:visibilitymodifier")
31 public final double phi;
32
33
34
35
36
37
38
39
40 public Ray2d(final double x, final double y, final double phi) throws DrawRuntimeException
41 {
42 super(x, y);
43 Throw.when(Double.isNaN(phi), DrawRuntimeException.class, "phi may not be NaN");
44 this.phi = phi;
45 }
46
47
48
49
50
51
52
53
54 public Ray2d(final Point2d point, final double phi) throws NullPointerException, DrawRuntimeException
55 {
56 this(Throw.whenNull(point, "point may not be null").x, point.y, phi);
57 }
58
59
60
61
62
63
64
65
66
67 public Ray2d(final double x, final double y, final double throughX, final double throughY) throws DrawRuntimeException
68 {
69 super(x, y);
70 Throw.when(throughX == x && throughY == y, DrawRuntimeException.class,
71 "the coordinates of the through point must differ from (x, y)");
72 this.phi = Math.atan2(throughY - y, throughX - x);
73 }
74
75
76
77
78
79
80
81
82
83 public Ray2d(final Point2d point, final double throughX, final double throughY)
84 throws NullPointerException, DrawRuntimeException
85 {
86 this(Throw.whenNull(point, "point may not be null").x, point.y, throughX, throughY);
87 }
88
89
90
91
92
93
94
95
96
97 public Ray2d(final double x, final double y, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
98 {
99 this(x, y, Throw.whenNull(throughPoint, "througPoint may not be null").x, throughPoint.y);
100 }
101
102
103
104
105
106
107
108
109 public Ray2d(final Point2dd.html#Point2d">Point2d point, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
110 {
111 this(Throw.whenNull(point, "point may not be null").x, point.y,
112 Throw.whenNull(throughPoint, "throughPoint may not be null").x, throughPoint.y);
113 }
114
115
116 @Override
117 public final double getPhi()
118 {
119 return this.phi;
120 }
121
122
123 @Override
124 public Point2d getEndPoint()
125 {
126 return new Point2d(this.x, this.y);
127 }
128
129
130 @Override
131 public int size()
132 {
133 return 2;
134 }
135
136
137 @Override
138 public Iterator<Point2d> getPoints()
139 {
140 double cosPhi = Math.cos(this.phi);
141 double sinPhi = Math.sin(this.phi);
142 Point2doint2d.html#Point2d">Point2d.html#Point2d">Point2d[] array = new Point2doint2d.html#Point2d">Point2d[] { new Point2d(this.x, this.y),
143 new Point2d(cosPhi == 0 ? this.x : cosPhi * Double.POSITIVE_INFINITY,
144 sinPhi == 0 ? this.y : sinPhi * Double.POSITIVE_INFINITY) };
145 return Arrays.stream(array).iterator();
146 }
147
148
149 @Override
150 public Bounds2d getBounds()
151 {
152 double cosPhi = Math.cos(this.phi);
153 double sinPhi = Math.sin(this.phi);
154 return new Bounds2d(cosPhi >= 0 ? this.x : Double.NEGATIVE_INFINITY, cosPhi <= 0 ? this.x : Double.POSITIVE_INFINITY,
155 sinPhi >= 0 ? this.y : Double.NEGATIVE_INFINITY, sinPhi <= 0 ? this.y : Double.POSITIVE_INFINITY);
156 }
157
158
159 @Override
160 public Ray2d neg()
161 {
162 return new Ray2d(-this.x, -this.y, this.phi + Math.PI);
163 }
164
165
166 @Override
167 public Ray2d flip()
168 {
169 return new Ray2d(this.x, this.y, this.phi + Math.PI);
170 }
171
172
173 @Override
174 public Ray2d getLocationExtended(final double position) throws DrawRuntimeException
175 {
176 Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
177 "position must be finite");
178 return new Ray2d(this.x + Math.cos(this.phi) * position, this.y + Math.sin(this.phi) * position, this.phi);
179 }
180
181
182 @Override
183 public Point2dt2d">Point2d closestPointOnRay(final Point2d point) throws NullPointerException
184 {
185 Throw.whenNull(point, "point may not be null");
186 double dX = Math.cos(this.phi);
187 double dY = Math.sin(this.phi);
188 return point.closestPointOnLine(this.x, this.y, this.x + dX, this.y + dY, true, false);
189 }
190
191
192 @Override
193 public Point2dt2d">Point2d projectOrthogonal(final Point2d point) throws NullPointerException
194 {
195 Throw.whenNull(point, "point may not be null");
196 return point.closestPointOnLine(this.x, this.y, this.x + Math.cos(this.phi), this.y + Math.sin(this.phi), null, false);
197 }
198
199
200 @Override
201 public Point2dnt2d projectOrthogonalExtended(final Point2d point)
202 {
203 Throw.whenNull(point, "point may not be null");
204 return point.closestPointOnLine(getX(), getY(), getX() + Math.cos(this.phi), getY() + Math.sin(this.phi), false, false);
205 }
206
207
208 @Override
209 public double projectOrthogonalFractional(final Point2d point) throws NullPointerException
210 {
211 Throw.whenNull(point, "point may not be null");
212 return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.phi), this.y + Math.sin(this.phi), null,
213 false);
214 }
215
216
217 @Override
218 public double projectOrthogonalFractionalExtended(final Point2d point) throws NullPointerException
219 {
220 Throw.whenNull(point, "point may not be null");
221 return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.phi), this.y + Math.sin(this.phi), false,
222 false);
223 }
224
225
226 @Override
227 public boolean epsilonEquals(final Ray2d other, final double epsilonCoordinate, final double epsilonDirection)
228 throws NullPointerException, IllegalArgumentException
229 {
230 Throw.whenNull(other, "other point may not be null");
231 Throw.when(
232 Double.isNaN(epsilonCoordinate) || epsilonCoordinate < 0 || Double.isNaN(epsilonDirection)
233 || epsilonDirection < 0,
234 IllegalArgumentException.class, "epsilon values may not be negative and may not be NaN");
235 if (Math.abs(this.x - other.x) > epsilonCoordinate)
236 {
237 return false;
238 }
239 if (Math.abs(this.y - other.y) > epsilonCoordinate)
240 {
241 return false;
242 }
243 if (Math.abs(AngleUtil.normalizeAroundZero(this.phi - other.phi)) > epsilonDirection)
244 {
245 return false;
246 }
247 return true;
248 }
249
250
251 @Override
252 public String toString()
253 {
254 return toString("%f", false);
255 }
256
257
258 @Override
259 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
260 {
261 String format = String.format("%1$s[x=%2$s, y=%2$s, phi=%2%s]", doNotIncludeClassName ? "" : "Ray2d ", doubleFormat);
262 return String.format(Locale.US, format, this.x, this.y, this.phi);
263 }
264
265
266 @Override
267 public int hashCode()
268 {
269 final int prime = 31;
270 int result = super.hashCode();
271 long temp;
272 temp = Double.doubleToLongBits(this.phi);
273 result = prime * result + (int) (temp ^ (temp >>> 32));
274 return result;
275 }
276
277
278 @Override
279 @SuppressWarnings("checkstyle:needbraces")
280 public boolean equals(final Object obj)
281 {
282 if (this == obj)
283 return true;
284 if (!super.equals(obj))
285 return false;
286 if (getClass() != obj.getClass())
287 return false;
288 Ray2d="../../../../org/djutils/draw/line/Ray2d.html#Ray2d">Ray2d other = (Ray2d) obj;
289 if (Double.doubleToLongBits(this.phi) != Double.doubleToLongBits(other.phi))
290 return false;
291 return true;
292 }
293
294 }