View Javadoc

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