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