View Javadoc

1   /*
2    * @(#) DirectionalLine.java 23-jul-2004
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
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 		// dot product between vector to the point and the normal
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 		// The intersection point I, of two vectors, A1->A2 and
275 		// B1->B2, is:
276 		// I = A1 + Ua * (A2 - A1)
277 		// I = B1 + Ub * (B2 - B1)
278 		//
279 		// Solving for Ua gives us the following formula.
280 		// Ua is returned.
281 		double denominator = (line.y2 - line.y1) * (this.x2 - this.x1)
282 				- (line.x2 - line.x1) * (this.y2 - this.y1);
283 
284 		// check if the two lines are parallel
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 }