source: trunk/src/org/expeditee/core/Line.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

File size: 4.6 KB
Line 
1package org.expeditee.core;
2
3/**
4 * The line class represents a line-segment in 2-dimensional space (i.e.
5 * the connection between 2 points).
6 *
7 * @author cts16
8 */
9public class Line {
10
11 /** The amount of error to use to ensure lines passing through pixels are hit.
12 Equal to 1 / sqrt(2). */
13 public static final double PIXEL_ERROR = 0.707106781;
14
15 /** The two end-points of the line segment. */
16 private Point _p1, _p2;
17
18 public Line(Point p1, Point p2)
19 {
20 _p1 = new Point(p1);
21 _p2 = new Point(p2);
22 }
23
24 public Line(int x1, int y1, int x2, int y2)
25 {
26 _p1 = new Point(x1, y1);
27 _p2 = new Point(x2, y2);
28 }
29
30 /** Gets the position of the first end of the line. */
31 public Point getFirstEnd()
32 {
33 return _p1;
34 }
35
36 /** Gets the position of the second end of the line. */
37 public Point getSecondEnd()
38 {
39 return _p2;
40 }
41
42 /** Sets the first end of the line to the given point. */
43 public void setFirstEnd(Point p)
44 {
45 if (p != null) _p1 = p;
46 }
47
48 /** Sets the second end of the line to the given point. */
49 public void setSecondEnd(Point p)
50 {
51 if (p != null) _p2 = p;
52 }
53
54 /** Gets the change in x-coordinate from one end to the other. */
55 public int deltaX()
56 {
57 return _p2.x - _p1.x;
58 }
59
60 /** Gets the change in y-coordinate from one end to the other. */
61 public int deltaY()
62 {
63 return _p2.y - _p1.y;
64 }
65
66 /** Gets the length of the line. */
67 public double length()
68 {
69 return Point.distanceBetween(_p1, _p2);
70 }
71
72 /**
73 * Checks if this line intersects the given line.
74 *
75 * https://stackoverflow.com/questions/563198/whats-the-most-efficent-way-to-calculate-where-two-line-segments-intersect
76 */
77 public boolean intersects(Line other, double error)
78 {
79 Point p = this._p1;
80 Point q = other._p1;
81 Point r = Point.difference(this._p2, p);
82 Point s = Point.difference(other._p2, q);
83
84 int numerator = crossLength(Point.difference(q, p), r);
85 int denominator = crossLength(r, s);
86
87 if (denominator == 0) {
88 return numerator == 0 && (contains(other._p1, error) || contains(other._p2, error));
89 } else {
90 double u = ((double) numerator) / denominator;
91 return u >= 0 && u <= 1 && this.contains(new Point((int) (q.x + u * s.x), (int) (q.y + u * s.y)), error);
92 }
93 }
94
95 /**
96 * Calculates the length of the cross-product between points a and b.
97 */
98 private int crossLength(Point a, Point b)
99 {
100 return a.x * b.y - a.y * b.x;
101 }
102
103 /**
104 * Checks if the given point falls on the line.
105 *
106 * @param p
107 * The point to check.
108 *
109 * @param error
110 * The maximum distance from the line that will be considered part of the line.
111 *
112 * @return
113 * True if the given point falls on the line (within <code>error</code>), false if not.
114 */
115 public boolean contains(Point p, double error)
116 {
117 // If the point is within error from either end,
118 // then we say it's on the line
119 if (Point.distanceBetween(p, _p1) <= error || Point.distanceBetween(p, _p2) <= error) return true;
120
121 // Point's projection onto the line must be between the two ends
122 double length = length();
123 double projection = Point.dotProduct(_p1, _p2, p) / length;
124 if (!(0 <= projection && projection <= length)) return false;
125
126 // Point must be within error distance from the line
127 double hypotenuse = Point.distanceBetween(p, _p1);
128 if ((hypotenuse * hypotenuse - projection * projection) <= (error * error)) return true;
129
130 // The point is too far from the line
131 return false;
132 }
133
134 /**
135 * Checks if there are any intersections between to sets of lines.
136 * Does not check for intersections within each set.
137 *
138 * @param lines1
139 * The first set of lines.
140 *
141 * @param lines2
142 * The second set of lines.
143 *
144 * @return
145 * True if a line from set 1 intersects a line from set 2,
146 * false if no intersections occur.
147 */
148 public static boolean anyIntersections(Line[] lines1, Line[] lines2, double error)
149 {
150 for (Line line1 : lines1) {
151 for (Line line2 : lines2) {
152 if (line1.intersects(line2, error)) return true;
153 }
154 }
155
156 return false;
157 }
158
159 /** Gets the point on the line nearest to the given point. */
160 public Point getPointNearestTo(Point p)
161 {
162 // If the line is degenerate, then that is the nearest point
163 if (_p1.equals(_p2)) return _p1.clone();
164
165 // Work out the distance from _p1 to the nearest point
166 double lineLength = length();
167 double projectionLength = Point.dotProduct(_p1, _p2, p) / lineLength;
168
169 // Calculate the position of the nearest point
170 if (projectionLength <= 0.0) {
171 return _p1.clone();
172 } else if (projectionLength >= lineLength) {
173 return _p2.clone();
174 } else {
175 double scaleFactor = projectionLength / lineLength;
176 return _p1.clone().add((_p2.x - _p1.x) * scaleFactor, (_p2.y - _p1.y) * scaleFactor);
177 }
178 }
179
180}
Note: See TracBrowser for help on using the repository browser.