1 | package 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 | */
|
---|
9 | public 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 | }
|
---|