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