1 package org.djutils.draw.line;
2
3 import org.djutils.base.AngleUtil;
4 import org.djutils.draw.DrawRuntimeException;
5 import org.djutils.draw.Drawable2d;
6 import org.djutils.draw.Space2d;
7 import org.djutils.draw.bounds.Bounds2d;
8 import org.djutils.draw.point.Point2d;
9 import org.djutils.exceptions.Throw;
10
11
12
13
14
15
16
17
18
19
20 public class Ray2d extends Point2d implements Drawable2d, Ray<Ray2d, Point2d, Space2d>
21 {
22
23 private static final long serialVersionUID = 20210119L;
24
25
26 @SuppressWarnings("checkstyle:visibilitymodifier")
27 public final double phi;
28
29
30
31
32
33
34
35
36 public Ray2d(final double x, final double y, final double phi) throws DrawRuntimeException
37 {
38 super(x, y);
39 Throw.when(Double.isNaN(phi), DrawRuntimeException.class, "phi may not be NaN");
40 this.phi = phi;
41 }
42
43
44
45
46
47
48
49
50 public Ray2d(final Point2d point, final double phi) throws NullPointerException, DrawRuntimeException
51 {
52 this(Throw.whenNull(point, "point may not be null").x, point.y, phi);
53 }
54
55
56
57
58
59
60
61
62
63 public Ray2d(final double x, final double y, final double throughX, final double throughY) throws DrawRuntimeException
64 {
65 super(x, y);
66 Throw.when(throughX == x && throughY == y, DrawRuntimeException.class,
67 "the coordinates of the through points must differ from (x, y)");
68 this.phi = Math.atan2(throughY - y, throughX - x);
69 }
70
71
72
73
74
75
76
77
78
79 public Ray2d(final Point2d point, final double throughX, final double throughY)
80 throws NullPointerException, DrawRuntimeException
81 {
82 this(Throw.whenNull(point, "point may not be null").x, point.y, throughX, throughY);
83 }
84
85
86
87
88
89
90
91
92
93 public Ray2d(final double x, final double y, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
94 {
95 this(x, y, Throw.whenNull(throughPoint, "througPoint may not be null").x, throughPoint.y);
96 }
97
98
99
100
101
102
103
104
105 public Ray2d(final Point2dd.html#Point2d">Point2d point, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
106 {
107 this(Throw.whenNull(point, "point may not be null").x, point.y,
108 Throw.whenNull(throughPoint, "throughPoint may not be null").x, throughPoint.y);
109 }
110
111
112 @Override
113 public final double getPhi()
114 {
115 return this.phi;
116 }
117
118
119 @Override
120 public Point2d getEndPoint()
121 {
122 return new Point2d(this.x, this.y);
123 }
124
125
126 @Override
127 public Bounds2d getBounds()
128 {
129 double normalizedPhi = AngleUtil.normalizeAroundZero(this.phi);
130 boolean toPositiveX = Math.abs(normalizedPhi) <= Math.PI / 2;
131 return new Bounds2d(toPositiveX ? this.x : Double.NEGATIVE_INFINITY, toPositiveX ? Double.POSITIVE_INFINITY : this.x,
132 normalizedPhi >= 0 ? this.y : Double.NEGATIVE_INFINITY, normalizedPhi <= 0 ? this.y : Double.POSITIVE_INFINITY);
133 }
134
135
136 @Override
137 public Ray2d neg()
138 {
139 return new Ray2d(-this.x, -this.y, this.phi + Math.PI);
140 }
141
142
143 @Override
144 public Ray2d getLocationExtended(final double position) throws DrawRuntimeException
145 {
146 Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
147 "position must be finite");
148 return new Ray2d(this.x + Math.cos(this.phi) * position, this.y + Math.sin(this.phi) * position, this.phi);
149 }
150
151
152 @Override
153 public Point2dt2d">Point2d closestPointOnRay(final Point2d point) throws NullPointerException
154 {
155 Throw.whenNull(point, "point may not be null");
156 double dX = Math.cos(this.phi);
157 double dY = Math.sin(this.phi);
158 final double u = (point.x - this.x) * dX + (point.y - this.y) * dY;
159 if (u <= 0)
160 {
161 return getEndPoint();
162 }
163 return new Point2d(this.x + u * dX, this.y + u * dY);
164 }
165
166
167 @Override
168 public boolean epsilonEquals(final Ray2d other, final double epsilonCoordinate, final double epsilonDirection)
169 throws NullPointerException, IllegalArgumentException
170 {
171 Throw.whenNull(other, "other point may not be null");
172 Throw.when(
173 Double.isNaN(epsilonCoordinate) || epsilonCoordinate < 0 || Double.isNaN(epsilonDirection)
174 || epsilonDirection < 0,
175 IllegalArgumentException.class, "epsilon values may not be negative and may not be NaN");
176 if (Math.abs(this.x - other.x) > epsilonCoordinate)
177 {
178 return false;
179 }
180 if (Math.abs(this.y - other.y) > epsilonCoordinate)
181 {
182 return false;
183 }
184 if (Math.abs(AngleUtil.normalizeAroundZero(this.phi - other.phi)) > epsilonDirection)
185 {
186 return false;
187 }
188 return true;
189 }
190
191
192 @Override
193 public int hashCode()
194 {
195 final int prime = 31;
196 int result = super.hashCode();
197 long temp;
198 temp = Double.doubleToLongBits(this.phi);
199 result = prime * result + (int) (temp ^ (temp >>> 32));
200 return result;
201 }
202
203
204 @Override
205 @SuppressWarnings("checkstyle:needbraces")
206 public boolean equals(final Object obj)
207 {
208 if (this == obj)
209 return true;
210 if (!super.equals(obj))
211 return false;
212 if (getClass() != obj.getClass())
213 return false;
214 Ray2d="../../../../org/djutils/draw/line/Ray2d.html#Ray2d">Ray2d other = (Ray2d) obj;
215 if (Double.doubleToLongBits(this.phi) != Double.doubleToLongBits(other.phi))
216 return false;
217 return true;
218 }
219
220
221 @Override
222 public String toString()
223 {
224 return "Ray2d [x=" + this.x + " y=" + this.y + " phi=" + this.phi + "]";
225 }
226
227 }