最近处理一些问题,涉及到对直线进行处理。对于十几年不接触数学的人而言,有些意思。
我想要实现的有以下:
- 已知两个点(x1, y1), (x2, y2),求直线方程;
- 已知四个点(x1, y1), (x2, y2), (p1, q1), (p2, q2),求(x1, y1), (x2, y2)组成的直线与(p1, q1), (p2, q2)组成直线,这两条直线的交点;
- 过该交点,平分交点的角,形成的直线,求该直线的方程;
- 最好能画出来上述的图。
需要准备的数学知识,可以参考维基或数学课本,也可以自己算出来。简单截图如下:
-
直线定义,两点式、斜截式和一般式
-
两个直线的交点
-
平面2点距离
-
角平分线定理
备注:实际上也可以求的交点的夹角后,通过一条直线旋转后生成方程。但是推导起来有点困难,所以没有采用。过后我如果解决了,会再写上。
步骤如下:
- 求交点;
- 在交点的左右取x值,计算出对应的y值,形成2个点后,直线的方程就有了。
下面是代码(简化处理,没有考虑平行的情况)
# 导入必要的包
import numpy as np
from matplotlib import pyplot as plt
import math
# 过直线A的两点
x1, y1 = 2, 3
x2, y2 = 7, 10
# 过直线B的两点
p1, q1 = 4, 8
p2, q2 = 6, 3
# 绘制直线A上的点
plt.plot(x1, y1, "ob")
plt.plot(x2, y2, "ob")
# 绘制直线A上的点
plt.plot(p1, q1, "or")
plt.plot(p2, q2, "or")
# 定义求斜率的方法
def get_k(x1,y1,x2,y2):
k = (y1-y2)/(x1-x2)
return k
# 定义求截距的方法
def get_b(x1,y1,x2,y2):
b = (x1*y2-x2*y1)/(x1-x2)
return b
# 绘制直线A
k1 = get_k(x1,y1,x2,y2)
b1 = get_b(x1,y1,x2,y2)
x_1 = np.arange(1, 10, 0.2)
y_1 = k1 *x_1 +b1
plt.plot(x_1 , y_1 , linewidth=0.5)
# 绘制直线B
k2 = get_k(p1,q1,p2,q2)
b2 = get_b(p1,q1,p2,q2)
x_2 = np.arange(1, 10, 0.2)
y_2 = k2 *x_2 +b2
plt.plot(x_2, y_2, linewidth=0.5)
# 定义矩阵运算,方便调用
def mat(a, b, c, d):
return a*d-b*c
# 定义获取一般式ABC的方法
def get_ABC(x1, y1, x2, y2):
A = y2 - y1
B = x1 - x2
C = x2*y1 - x1*y2
return A, B, C
# 分别求两条直线的ABC
A1, B1, C1 = get_ABC(x1, y1, x2, y2)
A2, B2, C2 = get_ABC(p1, q1, p2, q2)
# 求出交点并绘制
cross_x = - (mat(C1, B1, C2, B2))/(mat(A1, B1, A2, B2))
cross_y = - (mat(A1, C1, A2, C2))/(mat(A1, B1, A2, B2))
plt.plot(cross_x, cross_y, "og")
# 求三角形底边长度,随便定义个中文名函数。x是在x轴取的值,只需要在交点左右取两点即可
def get_底边长度(x, k1, b1, k2, b2):
y1 = k1*x+b1
y2 = k2*x+b2
return math.fabs(y1-y2)
# 定义求距离的方法
def get_distance(x0, y0, x1, y1):
dd = (x0-x1)**2 + (y0-y1)**2
d = math.sqrt(dd)
return d
# 在交点的左、右各取一个x轴位置,分别求出该x时,两条直线的y的值
x_left = cross_x - 3
y_left_1 = k1*x_left+b1
y_left_2 = k2*x_left+b2
x_right = cross_x + 3
y_right_1 = k1*x_right+b1
y_right_2 = k2*x_right+b2
# 于是一共有4个点:(x_left, y_left_1), (x_left, y_left_2), (x_right, y_right_1), (x_right, y_right_2)
# 利用角平分线定理求得在夹角平分线上的点的y值
上边长_left = get_distance(x_left, max(y_left_1, y_left_2), cross_x, cross_y)
下边长_left = get_distance(x_left, min(y_left_1, y_left_2), cross_x, cross_y)
上边长_right = get_distance(x_right, max(y_right_1, y_right_2), cross_x, cross_y)
下边长_right = get_distance(x_right, min(y_right_1, y_right_2), cross_x, cross_y)
底边长度_left = get_底边长度(x_left, k1, b1, k2, b2)
底边长度_right = get_底边长度(x_right, k1, b1, k2, b2)
上底边_left = (上边长_left * 底边长度_left)/(上边长_left+下边长_left)
y_left = max(y_left_1, y_left_2) - 上底边_left # 左边的点为(x_left, y_left)
上底边_right = (上边长_right * 底边长度_right)/(上边长_right+下边长_right)
y_right = max(y_right_1, y_right_2) - 上底边_right # 右边的点为(x_right, y_right)
# 现在夹角平分线上的2个点就有了。该直线很容易求得。顺便把这2个点也画出来
plt.plot(x_left, y_left, "og")
plt.plot(x_right, y_right, "og")
k = get_k(x_left, y_left, x_right, y_right)
b = get_b(x_left, y_left, x_right, y_right)
# 最后把直线画出来
x = np.arange(1, 10, 0.2)
y = k*x+b
plt.plot(x, y, linewidth=0.8)
结果如下图:
画出来的图看起来好像两个角不一样大。本着求真务实的精神,在用点到距离的公式验算一下。
正确的情况下,点到两条直线的距离应该相等。
# 定义计算点到直线的距离的方法
def get_点到直线距离(A, B, C, x, y):
up = math.fabs(A*x+B*y+C)
down = math.sqrt(A**2+B**2)
d = up/down
return d
d1 = line.get_点到直线距离(A1, B1, C1, x_left, y_left)
d2 = line.get_点到直线距离(A2, B2, C2, x_right, y_right)
# d1 = 2.6512291367965264
# d2 = 2.6512291367965277
d1与d2几乎相等了,看起来角不一样大,可能是由于运算的误差造成的。
以上,如果有错误,还请留言批评指正。