3 # line_equation - some experiments about representing a line
5 # Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Some experiments about representing a line passing for two points"""
23 import matplotlib.pyplot as plt
29 # pylint: disable=invalid-name
33 """Return the distance between the two points p1 and p2"""
37 return math.sqrt(dx * dx + dy * dy)
40 def line_eq_slope_intercept(p1, p2):
41 """Return the coefficients of the line equation in the slope/intercept
49 # For vertical lines the slope would be undefined in theory.
50 # For practical purposes a large value can work.
52 slope = INFINITY * math.copysign(1, dy)
56 intercept = -slope * p1[0] + p1[1]
58 return slope, intercept
61 def line_angle(p1, p2):
62 """Return the angle between the line passing for p1 and p2, and the positive
65 The return value is in radians.
70 angle = math.atan2(dy, dx)
77 def slope_intercept_to_a_b_c(slope, intercept):
78 """Convert the coefficients from the slope/intercept form:
82 to the coefficients in the linear equation form:
93 def line_eq_two_points(p1, p2):
94 """Return the coefficients for the line equation, in the linear equation
100 b = - (p2[0] - p1[0])
101 c = - (p1[0] * p2[1] - p2[0] * p1[1])
106 def line_calc_normal(p1, p2):
107 """Return the unit normal to the line passing for two points."""
111 n_length = math.sqrt(dx*dx + dy*dy)
112 if n_length < EPSILON:
122 def list_rotate(l, n):
134 # The points are assumed to be in clockwise order
135 for p1, p2 in zip(points, list_rotate(points, 1)):
139 line_x_extremes = [p1[0], p2[0]]
140 line_y_extremes = [p1[1], p2[1]]
142 plt.plot(line_x_extremes, line_y_extremes, 'k')
144 x_samples = np.linspace(min(line_x_extremes), max(line_x_extremes), 15)
146 # slope/intercept form
147 m, q = line_eq_slope_intercept(p1, p2)
149 if abs(m) == INFINITY:
150 print("m is undefined: vertical line!")
152 np.linspace(min(line_y_extremes), max(line_y_extremes), 15)
154 y_samples = x_samples * m + q
156 plt.plot(x_samples, y_samples, 'wo', markersize=15)
158 # linear equation form
159 a, b, c = line_eq_two_points(p1, p2)
162 print("b is 0: vertical line!")
164 np.linspace(min(line_y_extremes), max(line_y_extremes), 15)
166 y_samples = (-x_samples * a - c) / b
168 plt.plot(x_samples, y_samples, 'ws', markersize=10)
171 t = np.linspace(0, 1, len(x_samples))
173 # point and direction form
177 x_samples = p1[0] + dx * t
178 y_samples = p1[1] + dy * t
179 plt.plot(x_samples, y_samples, 'm+', markersize=10)
181 # or using a point and the normal, but in this case the normal has to
182 # be rotated by 90 degrees counterclockwise
183 nx, ny = line_calc_normal(p1, p2)
187 length = distance(p1, p2)
188 x_samples = p1[0] + rnx * t * length
189 y_samples = p1[1] + rny * t * length
190 plt.plot(x_samples, y_samples, 'bx', markersize=10)
193 mid_x = (p1[0] + p2[0]) / 2
194 mid_y = (p1[1] + p2[1]) / 2
196 plt.arrow(mid_x, mid_y, nx * n_length, ny * n_length,
197 head_width=10, head_length=5, fc='k', ec='k')
199 # check the other functions
200 print("angle: %g" % math.degrees(line_angle(p1, p2)))
201 a1, b1, c1 = slope_intercept_to_a_b_c(m, q)
204 print(a / b, b / b, c / b)
209 # draw the vertices on top of everything else
210 xvalues = [p[0] for p in points]
211 yvalues = [p[1] for p in points]
212 plt.plot(xvalues, yvalues, 'ro')
213 plt.title('Line passing for two points')
217 plt.plot([], 'k', label="line")
218 plt.plot([], 'wo', label="y = mx + q")
219 plt.plot([], 'ws', label="ax + by + c = 0")
220 plt.plot([], 'm+', label="P = P1 + D * t")
221 plt.plot([], 'bx', label="P = P1 + N * t * length")
222 plt.legend(loc='lower left', numpoints=1, fancybox=True, prop={'size': 10})
226 if __name__ == "__main__":