1
2
3
4
5
6 package nl.tudelft.simulation.language.d2;
7
8 import java.awt.geom.Line2D;
9 import java.awt.geom.Point2D;
10
11 /***
12 * A directional line with normal vector. Based on the BSPLine-example from the book Developing games in Java
13 * from David Brackeen. DirectionalLine.java
14 * <p>
15 * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft University of Technology </a>, the
16 * Netherlands.
17 * <p>
18 * See for project information <a
19 * href="http://www.simulation.tudelft.nl/dsol/language">www.simulation.tudelft.nl/language </a> <br>
20 * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser General Public License (LGPL)
21 * </a>, no warranty
22 *
23 * @version $Revision: 1.8 $ $Date: 2005/08/04 12:08:55 $
24 * @author <a href="mailto:royc@tbm.tudelft.nl">Roy Chin </a>
25 */
26 public final class DirectionalLine extends Line2D.Double
27 {
28 /*** point at the back of the line. */
29 public static final int BACKSIDE = -1;
30
31 /*** point collinear with the line. */
32 public static final int COLLINEAR = 0;
33
34 /*** point in front of the line. */
35 public static final int FRONTSIDE = 1;
36
37 /*** other line is spanning this line. */
38 public static final int SPANNING = 2;
39
40 /*** the thickness of the line. */
41 private double lineThickness = 1;
42
43 /***
44 * x coordinate of the line normal.
45 */
46 private double normalX;
47
48 /***
49 * y coordinate of the line normal.
50 */
51 private double normalY;
52
53 /***
54 * Creates a new DirectionalLine based on the specified coordinates.
55 *
56 * @param x1
57 * Coordinate x1
58 * @param y1
59 * Coordinate y1
60 * @param x2
61 * Coordinate x2
62 * @param y2
63 * Coordinate y2
64 */
65 public DirectionalLine(final double x1, final double y1, final double x2, final double y2)
66 {
67 this.setLine(x1, y1, x2, y2);
68 }
69
70 /***
71 * Calculates the normal to this line.
72 */
73 public void calcNormal()
74 {
75 this.normalX = this.y2 - this.y1;
76 this.normalY = this.x1 - this.x2;
77 }
78
79 /***
80 * Normalizes the normal of this line (make the normal's length 1).
81 */
82 public void normalize()
83 {
84 double length = Math.sqrt(this.normalX * this.normalX + this.normalY * this.normalY);
85 this.normalX /= length;
86 this.normalY /= length;
87 }
88
89 /***
90 * Set the line using floats.
91 *
92 * @param x1
93 * x1 coordinate
94 * @param y1
95 * y1 coodinate
96 * @param x2
97 * x2 coordinate
98 * @param y2
99 * y2 coordinate
100 */
101 public void setLine(final float x1, final float y1, final float x2, final float y2)
102 {
103 super.setLine(x1, y1, x2, y2);
104 this.calcNormal();
105 }
106
107 /***
108 * @see java.awt.geom.Line2D#setLine(double, double, double, double)
109 */
110 @Override
111 public void setLine(final double x1, final double y1, final double x2, final double y2)
112 {
113 super.setLine(x1, y1, x2, y2);
114 this.calcNormal();
115 }
116
117 /***
118 * Flips this line so that the end points are reversed (in other words, (x1,y1) becomes (x2,y2) and vice
119 * versa) and the normal is changed to point the opposite direction.
120 */
121 public void flip()
122 {
123 double tx = this.x1;
124 double ty = this.y1;
125 this.x1 = this.x2;
126 this.y1 = this.y2;
127 this.x2 = tx;
128 this.y2 = ty;
129 this.normalX = -this.normalX;
130 this.normalY = -this.normalY;
131 }
132
133 /***
134 * Returns true if the endpoints of this line match the endpoints of the specified line. Ignores normal
135 * and height values.
136 *
137 * @param line
138 * another line
139 * @return true if this line's coordinates are equal to the other line's coordinates
140 */
141 public boolean equalsCoordinates(final DirectionalLine line)
142 {
143 return (this.x1 == line.x1 && this.x2 == line.x2 && this.y1 == line.y1 && this.y2 == line.y2);
144 }
145
146 /***
147 * Returns true if the endpoints of this line match the endpoints of the specified line, ignoring endpoint
148 * order (if the first point of this line is equal to the second point of the specified line, and vice
149 * versa, returns true). Ignores normal and height values.
150 *
151 * @param line
152 * another line
153 * @return true if coordinates match independent of the order
154 */
155 public boolean equalsCoordinatesIgnoreOrder(final DirectionalLine line)
156 {
157 return equalsCoordinates(line)
158 || ((this.x1 == line.x2 && this.x2 == line.x1 && this.y1 == line.y2 && this.y2 == line.y1));
159 }
160
161 /***
162 * @see java.lang.Object#toString()
163 */
164 @Override
165 public String toString()
166 {
167 return "(" + this.x1 + ", " + this.y1 + ")->" + "(" + this.x2 + "," + this.y2 + ")";
168 }
169
170 /***
171 * Gets the side of this line the specified point is on. This method treats the line as 1-unit thick, so
172 * points within this 1-unit border are considered collinear. For this to work correctly, the normal of
173 * this line must be normalized, either by setting this line to a polygon or by calling normalize().
174 * Returns either FRONTSIDE, BACKSIDE, or COLLINEAR.
175 *
176 * @param x
177 * coordinate x
178 * @param y
179 * coordinate y
180 * @return the side
181 */
182 public int getSideThick(final double x, final double y)
183 {
184 double normalX = this.normalX * this.lineThickness;
185 double normalY = this.normalY * this.lineThickness;
186
187 int frontSide = getSideThin(x - normalX / 2, y - normalY / 2);
188 if (frontSide == DirectionalLine.FRONTSIDE)
189 {
190 return FRONTSIDE;
191 }
192 else if (frontSide == DirectionalLine.BACKSIDE)
193 {
194 int backSide = getSideThin(x + normalX / 2, y + normalY / 2);
195 if (backSide == DirectionalLine.BACKSIDE)
196 {
197 return DirectionalLine.BACKSIDE;
198 }
199 }
200 return DirectionalLine.COLLINEAR;
201 }
202
203 /***
204 * Gets the side of this line the specified point is on. Because of doubleing point inaccuracy, a
205 * collinear line will be rare. For this to work correctly, the normal of this line must be normalized,
206 * either by setting this line to a polygon or by calling normalize(). Returns either FRONTSIDE, BACKSIDE,
207 * or COLLINEAR.
208 *
209 * @param x
210 * coordinate x
211 * @param y
212 * coordinate y
213 * @return the side
214 */
215 public int getSideThin(final double x, final double y)
216 {
217
218 double side = (x - this.x1) * this.normalX + (y - this.y1) * this.normalY;
219 if (side < 0)
220 {
221 return DirectionalLine.BACKSIDE;
222 }
223 else if (side > 0)
224 {
225 return DirectionalLine.FRONTSIDE;
226 }
227 else
228 {
229 return DirectionalLine.COLLINEAR;
230 }
231 }
232
233 /***
234 * Gets the side of this line that the specified line segment is on. Returns either FRONT, BACK, COLINEAR,
235 * or SPANNING.
236 *
237 * @param line
238 * line segment
239 * @return the side
240 */
241 public int getSide(final Line2D.Double line)
242 {
243 if (this.x1 == line.x1 && this.x2 == line.x2 && this.y1 == line.y1 && this.y2 == line.y2)
244 {
245 return DirectionalLine.COLLINEAR;
246 }
247 int p1Side = getSideThick(line.x1, line.y1);
248 int p2Side = getSideThick(line.x2, line.y2);
249 if (p1Side == p2Side)
250 {
251 return p1Side;
252 }
253 else if (p1Side == DirectionalLine.COLLINEAR)
254 {
255 return p2Side;
256 }
257 else if (p2Side == DirectionalLine.COLLINEAR)
258 {
259 return p1Side;
260 }
261 else
262 {
263 return DirectionalLine.SPANNING;
264 }
265 }
266
267 /***
268 * Returns the fraction of intersection along this line. Returns a value from 0 to 1 if the segments
269 * intersect. For example, a return value of 0 means the intersection occurs at point (x1, y1), 1 means
270 * the intersection occurs at point (x2, y2), and .5 mean the intersection occurs halfway between the two
271 * endpoints of this line. Returns -1 if the lines are parallel.
272 *
273 * @param line
274 * a line
275 * @return the intersection
276 */
277 public double getIntersection(final Line2D.Double line)
278 {
279
280
281
282
283
284
285
286 double denominator = (line.y2 - line.y1) * (this.x2 - this.x1) - (line.x2 - line.x1)
287 * (this.y2 - this.y1);
288
289
290 if (denominator == 0)
291 {
292 return -1;
293 }
294
295 double numerator = (line.x2 - line.x1) * (this.y1 - line.y1) - (line.y2 - line.y1)
296 * (this.x1 - line.x1);
297
298 return numerator / denominator;
299 }
300
301 /***
302 * Returns the interection point of this line with the specified line.
303 *
304 * @param line
305 * a line
306 * @return intersection point
307 */
308 public Point2D.Double getIntersectionPoint(final Line2D.Double line)
309 {
310 double fraction = getIntersection(line);
311 Point2D.Double intersection = new Point2D.Double();
312 intersection.setLocation(this.x1 + fraction * (this.x2 - this.x1), this.y1 + fraction
313 * (this.y2 - this.y1));
314 return intersection;
315 }
316
317 /***
318 * Gets the thickness of the line.
319 *
320 * @return returns the lineThickness
321 */
322 public double getLineThickness()
323 {
324 return this.lineThickness;
325 }
326
327 /***
328 * Sets the tickness of the line.
329 *
330 * @param lineThickness
331 * the lineThickness to set
332 */
333 public void setLineThickness(final double lineThickness)
334 {
335 this.lineThickness = lineThickness;
336 }
337
338 /***
339 * @return returns the normalX
340 */
341 public double getNormalx()
342 {
343 return this.normalX;
344 }
345
346 /***
347 * @return returns the normalY
348 */
349 public double getNormaly()
350 {
351 return this.normalY;
352 }
353 }