View Javadoc
1   package org.newdawn.slick.geom;
2   
3   import javax.annotation.Nonnull;
4   import javax.annotation.Nullable;
5   
6   /**
7    * Implemenation of a bunch of maths functions to do with lines. Note that lines
8    * can't be used as dynamic shapes right now - also collision with the end of a
9    * line is undefined.
10   * 
11   * @author Kevin Glass
12   */
13  public class Line extends Shape {
14      /**
15       * 
16       */
17      private static final long serialVersionUID = 1L;
18      /** The start point of the line */
19      private Vector2f start;
20      /** The end point of the line */
21      private Vector2f end;
22      /** The vector between the two points */
23      private Vector2f vec;
24      /** Temporary storage - declared globally to reduce GC */
25      @Nonnull
26      private final Vector2f loc = new Vector2f(0, 0);
27      /** Temporary storage - declared globally to reduce GC */
28      @Nonnull
29      private final Vector2f closest = new Vector2f(0, 0);
30      /**
31       * Create a new line based on the origin and a single point
32       *
33       * @param x
34       *            The end point of the line
35       * @param y
36       *            The end point of the line
37       * @param inner
38       *            True if this line blocks on it's inner edge
39       * @param outer
40       *            True if this line blocks on it's outer edge
41       */
42      private Line(float x, float y, boolean inner, boolean outer) {
43          this(0, 0, x, y);
44      }
45  
46      /**
47       * Create a new line based on the origin and a single point
48       *
49       * @param x
50       *            The end point of the line
51       * @param y
52       *            The end point of the line
53       */
54      public Line(float x, float y) {
55          this(x, y, true, true);
56      }
57  
58      /**
59       * Create a new line based on two points
60       *
61       * @param x1
62       *            The x coordinate of the start point
63       * @param y1
64       *            The y coordinate of the start point
65       * @param x2
66       *            The x coordinate of the end point
67       * @param y2
68       *            The y coordinate of the end point
69       */
70      public Line(float x1, float y1, float x2, float y2) {
71          this(new Vector2f(x1, y1), new Vector2f(x2, y2));
72      }
73  
74      /**
75       * Create a line with relative second point
76       *
77       * @param x1
78       *            The x coordinate of the start point
79       * @param y1
80       *            The y coordinate of the start point
81       * @param dx
82       *            The x change to get to the second point
83       * @param dy
84       *            The y change to get to the second point
85       * @param dummy
86       *            A dummy value
87       */
88      public Line(float x1, float y1, float dx, float dy, boolean dummy) {
89          this(new Vector2f(x1, y1), new Vector2f(x1 + dx, y1 + dy));
90      }
91  
92      /**
93       * Create a new line based on two points
94       *
95       * @param start
96       *            The start point
97       * @param end
98       *            The end point
99       */
100     public Line(float[] start, float[] end) {
101         super();
102 
103         set(start, end);
104     }
105 
106     /**
107      * Create a new line based on two points
108      *
109      * @param start
110      *            The start point
111      * @param end
112      *            The end point
113      */
114     private Line(Vector2f start, Vector2f end) {
115         super();
116 
117         set(start, end);
118     }
119 
120     /**
121      * Configure the line
122      *
123      * @param start
124      *            The start point of the line
125      * @param end
126      *            The end point of the line
127      */
128     void set(float[] start, float[] end) {
129         set(start[0], start[1], end[0], end[1]);
130     }
131 
132     /**
133      * Get the start point of the line
134      *
135      * @return The start point of the line
136      */
137     public Vector2f getStart() {
138         return start;
139     }
140 
141     /**
142      * Get the end point of the line
143      *
144      * @return The end point of the line
145      */
146     public Vector2f getEnd() {
147         return end;
148     }
149 
150     /**
151      * Find the length of the line
152      *
153      * @return The the length of the line
154      */
155     public float length() {
156         return vec.length();
157     }
158 
159     /**
160      * Find the length of the line squared (cheaper and good for comparisons)
161      *
162      * @return The length of the line squared
163      */
164     public float lengthSquared() {
165         return vec.lengthSquared();
166     }
167 
168     /**
169      * Configure the line
170      *
171      * @param start
172      *            The start point of the line
173      * @param end
174      *            The end point of the line
175      */
176     void set(Vector2f start, Vector2f end) {
177         super.pointsDirty = true;
178         if (this.start == null) {
179             this.start = new Vector2f();
180         }
181         this.start.set(start);
182 
183         if (this.end == null) {
184             this.end = new Vector2f();
185         }
186         this.end.set(end);
187 
188         vec = new Vector2f(end);
189         vec.sub(start);
190 
191         vec.lengthSquared();
192     }
193 
194     /**
195      * Configure the line without garbage
196      *
197      * @param sx
198      *            The x coordinate of the start
199      * @param sy
200      *            The y coordinate of the start
201      * @param ex
202      *            The x coordiante of the end
203      * @param ey
204      *            The y coordinate of the end
205      */
206     public void set(float sx, float sy, float ex, float ey) {
207         super.pointsDirty = true;
208         start.set(sx, sy);
209         end.set(ex, ey);
210         float dx = (ex - sx);
211         float dy = (ey - sy);
212         vec.set(dx,dy);
213     }
214 
215     /**
216      * Get the x direction of this line
217      *
218      * @return The x direction of this line
219      */
220     public float getDX() {
221         return end.getX() - start.getX();
222     }
223 
224     /**
225      * Get the y direction of this line
226      *
227      * @return The y direction of this line
228      */
229     public float getDY() {
230         return end.getY() - start.getY();
231     }
232 
233     /**
234      * @see org.newdawn.slick.geom.Shape#getX()
235      */
236     public float getX() {
237         return getX1();
238     }
239 
240     /**
241      * @see org.newdawn.slick.geom.Shape#getY()
242      */
243     public float getY() {
244         return getY1();
245     }
246 
247     /**
248      * Get the x coordinate of the start point
249      *
250      * @return The x coordinate of the start point
251      */
252     float getX1() {
253         return start.getX();
254     }
255 
256     /**
257      * Get the y coordinate of the start point
258      *
259      * @return The y coordinate of the start point
260      */
261     float getY1() {
262         return start.getY();
263     }
264 
265     /**
266      * Get the x coordinate of the end point
267      *
268      * @return The x coordinate of the end point
269      */
270     float getX2() {
271         return end.getX();
272     }
273 
274     /**
275      * Get the y coordinate of the end point
276      *
277      * @return The y coordinate of the end point
278      */
279     float getY2() {
280         return end.getY();
281     }
282 
283     /**
284      * Get the shortest distance from a point to this line
285      *
286      * @param point
287      *            The point from which we want the distance
288      * @return The distance from the line to the point
289      */
290     public float distance(Vector2f point) {
291         return (float) Math.sqrt(distanceSquared(point));
292     }
293 
294     /**
295      * Check if the given point is on the line
296      *
297      * @param point
298      *            The point to check
299      * @return True if the point is on this line
300      */
301     public boolean on(@Nonnull Vector2f point) {
302         getClosestPoint(point, closest);
303 
304         return point.equals(closest);
305     }
306 
307     /**
308      * Get the shortest distance squared from a point to this line
309      *
310      * @param point
311      *            The point from which we want the distance
312      * @return The distance squared from the line to the point
313      */
314     public float distanceSquared(Vector2f point) {
315         getClosestPoint(point, closest);
316         closest.sub(point);
317 
318         float result = closest.lengthSquared();
319 
320         return result;
321     }
322 
323     /**
324      * Get the closest point on the line to a given point
325      *
326      * @param point
327      *            The point which we want to project
328      * @param result
329      *            The point on the line closest to the given point
330      */
331     void getClosestPoint(Vector2f point, @Nonnull Vector2f result) {
332         loc.set(point);
333         loc.sub(start);
334 
335         float projDistance = vec.dot(loc);
336 
337         projDistance /= vec.lengthSquared();
338 
339         if (projDistance < 0) {
340             result.set(start);
341             return;
342         }
343         if (projDistance > 1) {
344             result.set(end);
345             return;
346         }
347 
348         result.x = start.getX() + projDistance * vec.getX();
349         result.y = start.getY() + projDistance * vec.getY();
350     }
351 
352     /**
353      * @see java.lang.Object#toString()
354      */
355     @Nonnull
356     public String toString() {
357         return "[Line " + start + "," + end + "]";
358     }
359 
360     /**
361      * Intersect this line with another
362      *
363      * @param other
364      *            The other line we should intersect with
365      * @return The intersection point or null if the lines are parallel
366      */
367     @Nullable
368     public Vector2f intersect(@Nonnull Line other) {
369         return intersect(other, false);
370     }
371 
372     /**
373      * Intersect this line with another
374      *
375      * @param other
376      *            The other line we should intersect with
377      * @param limit
378      *            True if the collision is limited to the extent of the lines
379      * @return The intersection point or null if the lines don't intersect
380      */
381     @Nullable
382     public Vector2f intersect(@Nonnull Line other, boolean limit) {
383         Vector2f temp = new Vector2f();
384 
385         if (!intersect(other, limit, temp)) {
386             return null;
387         }
388 
389         return temp;
390     }
391 
392     /**
393      * Intersect this line with another
394      *
395      * @param other
396      *            The other line we should intersect with
397      * @param limit
398      *            True if the collision is limited to the extent of the lines
399      * @param result
400      *            The resulting intersection point if any
401      * @return True if the lines intersect
402      */
403     boolean intersect(@Nonnull Line other, boolean limit, @Nonnull Vector2f result) {
404         float dx1 = end.getX() - start.getX();
405         float dx2 = other.end.getX() - other.start.getX();
406         float dy1 = end.getY() - start.getY();
407         float dy2 = other.end.getY() - other.start.getY();
408         float denom = (dy2 * dx1) - (dx2 * dy1);
409 
410         if (denom == 0) {
411             return false;
412         }
413 
414         float ua = (dx2 * (start.getY() - other.start.getY()))
415                 - (dy2 * (start.getX() - other.start.getX()));
416         ua /= denom;
417         float ub = (dx1 * (start.getY() - other.start.getY()))
418                 - (dy1 * (start.getX() - other.start.getX()));
419         ub /= denom;
420 
421         if ((limit) && ((ua < 0) || (ua > 1) || (ub < 0) || (ub > 1))) {
422             return false;
423         }
424 
425         float u = ua;
426 
427         float ix = start.getX() + (u * (end.getX() - start.getX()));
428         float iy = start.getY() + (u * (end.getY() - start.getY()));
429 
430         result.set(ix, iy);
431         return true;
432     }
433 
434     /**
435      * @see org.newdawn.slick.geom.Shape#createPoints()
436      */
437     protected void createPoints() {
438         points = new float[4];
439         points[0] = getX1();
440         points[1] = getY1();
441         points[2] = getX2();
442         points[3] = getY2();
443     }
444 
445     /**
446      * @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
447      */
448     @Nonnull
449     public Shape transform(@Nonnull Transform transform) {
450         float[] temp = new float[4];
451         createPoints();
452         transform.transform(points, 0, temp, 0, 2);
453 
454         return new Line(temp[0], temp[1], temp[2], temp[3]);
455     }
456 
457     /**
458      * @see org.newdawn.slick.geom.Shape#closed()
459      */
460     public boolean closed() {
461         return false;
462     }
463 
464     /**
465      * @see org.newdawn.slick.geom.Shape#intersects(org.newdawn.slick.geom.Shape)
466      */
467     public boolean intersects(@Nonnull Shape shape)
468     { 
469         if (shape instanceof Circle) 
470         { 
471             return shape.intersects(this); 
472         } 
473         return super.intersects(shape); 
474     }
475 }