python - 从特定角度获取像素线
问题描述
我有一些问题。我正在使用pygame。我想获取以特定角度远离一个点的所有像素的线。
例如,我想获得以 18% 的角度远离点 (15,20) 的所有像素的线。
我知道有一些数学要做,但我不知道如何在 python 中做到这一点。你可以帮帮我吗?
解决方案
假设您希望从⟪some point⟫开始的直线中的像素以⟪some angle⟫。
首先让我们定义一些东西。
origin_point = ( 15, 20 ) # Co-ordinates of starting point
project_angle = math.radians( 18.0 ) # Angle of projection in degrees
我们需要将 Python 数学三角函数的角度从度数更改为弧度数。不仅 Python 使用弧度,大多数编程语言都使用弧度。
我们要定义从origin_point
方向 的线project_angle
。通过计算线的端点,它允许我们计算像素。那么这条线在哪里结束呢?无穷?可能屏幕的边缘会做。
我们需要计算出与屏幕边缘形成的线的交点,因此我们可以使用任何长度的线,只要理论上大于屏幕的最大可能线长(即对角线:diagonal_length = √(window_width² + window_length²)
)。所以任何“足够长”的长度都可以,所以我们会作弊并使用常数 10000。
MAX_LENGTH = 10000
现在对于那个坐标......使用角度的正弦和余弦,乘以长度来计算该点:
final_x = start_x + ( length * math.cos( angle_of_projection ) )
final_y = start_y + ( length * math.sin( angle_of_projection ) )
把它变成一个函数:
def getEndOfLine( start_point, angle_of_projection, length ):
""" Given a co-ordinate, projection angle, and line length, return
the co-ordinate of the end of line """
start_x, start_y = start_point
final_x = start_x + length * math.cos( angle_of_projection )
final_y = start_y + length * math.sin( angle_of_projection )
return ( final_x, final_y )
使用此函数,我们现在可以执行以下操作:
end_point = getEndOfLine( origin_point, project_angle, MAX_LENGTH )
给我们两点来定义这条线。所以,现在我们有了一条线,我们可以将它与屏幕边缘相交。因为我们的队伍很长,所以保证会在某个地方退出屏幕。
屏幕的边缘在哪里 - 这些很容易知道:
top_side = [ ( 0, 0 ), ( SCREEN_WIDTH-1, 0 ) ]
bottom_side = [ ( 0, SCREEN_HEIGHT-1 ), ( SCREEN_WIDTH-1, SCREEN_HEIGHT-1 ) ]
left_side = [ ( 0, 0 ), ( SCREEN_WIDTH-1, SCREEN_HEIGHT-1 ) ]
right_side = [ ( SCREEN_WIDTH-1, 0 ), ( SCREEN_WIDTH-1, SCREEN_HEIGHT-1 ) ]
all_sides = [ top_side, right_side, bottom_side, left_side ]
现在我们需要将斜线与屏幕边缘线相交。我不想在这里过多地讨论数学,但如果两条直线、无限长的线不平行,它们必须相交。所以首先确定你的线不平行,然后使用行列式方法计算交点。这些线可能在屏幕外相交,我们不关心这些点。
def intersectionPoint( x1,y1, x2,y2, x3,y3, x4,y4 ):
#Use determinant method, as per
#Ref: https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
Px = ((((x1*y2)-(y1*x2))*(x3 - x4)) - ((x1-x2)*((x3*y4)-(y3*x4)))) / (((x1-x2)*(y3-y4)) - ((y1-y2)*(x3-x4)))
Py = ((((x1*y2)-(y1*x2))*(y3 - y4)) - ((y1-y2)*((x3*y4)-(y3*x4)))) / (((x1-x2)*(y3-y4)) - ((y1-y2)*(x3-x4)))
return Px,Py
def getIntersectionPoint( line_start, line_end, screen_edges ):
x1, y1 = line_start
x2, y2 = line_end
for edge in screen_edges:
x3, y3 = edge[0] # line forming the screen edge
x4, y4 = edge[1]
is_parallel = (((x1-x2)*(y3-y4)) - ((y1-y2)*(x3-x4)) == 0)
if ( not is_parallel ):
int_x, int_y = intersectionPoint( x1,y1, x2,y2, x3,y3, x4,y4 )
# but is the intersection point on-screen (otherwise we don't care)
if ( int_x >= 0 and int_y >= 0 and int_x < SCREEN_WIDTH and int_y < SCREEN_HEIGHT ):
return ( int_x, int_y )
return None # should never happen
所以现在使用这个函数,我们可以确定屏幕边缘的点:
# Work out the long long line:
peliminary_point = getEndOfLine( origin_point, project_angle, MAX_LENGTH )
print( "Peliminary End Point from %3.1f° is (%3.1f,%3.1f)" % ( project_angle, peliminary_point[0], peliminary_point[1] ) )
# Work out where this line exits the screen/window:
end_point = getIntersectionPoint( origin_point, peliminary_point, all_sides )
print( "End Point is (%3.1f,%3.1f)" % ( end_point[0], end_point[1] ) )
对于 200x200 的窗口,MAX_LENGTH 为 10000,我得到:
从 0.31° 开始的初步终点是 (38052.3,12370.7)
终点是 (199.0,71.4)
现在我们有了线末端的坐标,很容易使用Bresenhnam 的中点线算法来计算像素。最好去阅读一下,我不打算在这里解释(甚至不确定我能不能)。
def _plotLineLow( x0,y0, x1,y1 ):
points = []
dx = x1 - x0
dy = y1 - y0
yi = 1
if dy < 0:
yi = -1
dy = -dy
D = 2*dy - dx
y = y0
for x in range( x0, x1 ):
points.append( (x,y) )
if D > 0:
y = y + yi
D = D - 2*dx
D = D + 2*dy
return points
def _plotLineHigh( x0,y0, x1,y1 ):
points = []
dx = x1 - x0
dy = y1 - y0
xi = 1
if dx < 0:
xi = -1
dx = -dx
D = 2*dx - dy
x = x0
for y in range( y0, y1 ):
points.append( (x,y) )
if D > 0:
x = x + xi
D = D - 2*dy
D = D + 2*dx
return points
def getMidLinePoints( pointA, pointB ):
x0, y0 = int( pointA[0] ), int( pointA[1] )
x1, y1 = int( pointB[0] ), int( pointB[1] )
points = []
if ( abs(y1 - y0) < abs(x1 - x0) ):
if ( x0 > x1 ):
points += _plotLineLow( x1, y1, x0, y0 )
else:
points += _plotLineLow( x0, y0, x1, y1 )
else:
if ( y0 > y1 ):
points += _plotLineHigh( x1, y1, x0, y0 )
else:
points += _plotLineHigh( x0, y0, x1, y1 )
return points
添加此代码,给出沿线从origin_point
atproject_angle
到屏幕边缘的像素坐标:
# Work out the long long line:
peliminary_point = getEndOfLine( origin_point, project_angle, MAX_LENGTH )
print( "Peliminary End Point from %3.1f° is (%3.1f,%3.1f)" % ( project_angle, peliminary_point[0], peliminary_point[1] ) )
# Work out where this line exits the screen/window:
end_point = getIntersectionPoint( origin_point, peliminary_point, all_sides )
print( "End Point is (%3.1f,%3.1f)" % ( end_point[0], end_point[1] ) )
line_points = getMidLinePoints( origin_point, end_point )
print( "Line Points are: " + str( line_points ) )
这给了我(这次是在 100x100 窗口上):
从 0.31° 开始的初步终点是 (9520.6,3100.2)
终点是 (99.0,38.9)
线点为:[(10, 10), (11, 10), (12, 11), (13, 11), (14, 11), (15, 12), (16, 12), (17, 12), (18, 13), (19, 13), (20, 13), (21, 13), (22, 14), (23, 14), (24, 14), (25, 15) , (26, 15), (27, 15), (28, 16), (29, 16), (30, 16), (31, 17), (32, 17), (33, 17), ( 34, 18), (35, 18), (36, 18), (37, 18), (38, 19), (39, 19), (40, 19), (41, 20), (42, 20), (43, 20), (44, 21), (45, 21), (46, 21), (47, 22), (48, 22), (49, 22), (50, 23) , (51, 23), (52, 23), (53, 24), (54, 24), (55, 24), (56, 24), (57, 25), (58, 25), ( 59, 25), (60, 26), (61, 26), (62, 26), (63, 27), (64, 27), (65, 27), (66, 28), (67, 28), (68, 28), (69, 29), (70, 29), (71, 29), (72, 30), (73, 30), (74, 30), (75, 30) , (76, 31), (77, 31), (78, 31), (79, 32), (80, 32), (81, 32), (82, 33), (83, 33), ( 84, 33), (85, 34), (86, 34), (87, 34), (88, 35), (89, 35), (90, 35), (91, 35), (92, 36), (93, 36),(94, 36), (95, 37), (96, 37), (97, 37), (98, 38)]
很简单嘿嘿! ;)
推荐阅读
- vba - 替换 Outlook 邮件 htmlbody 中的多个值
- python - 调整窗口时如何在 Kivy 中的图像之间设置固定宽度?
- javascript - 每当 DIV 出现在页面上时为其添加事件侦听器?
- kotlin - 我们如何在 Java 注释声明中引用 Kotlin 常量?
- java - 如何在每个活动 android 中添加抽屉?
- c - 在C中旋转矩阵
- javascript - JavaScript ES6 Promise 是否与 Promise/A+ 完全相同?
- c# - 执行 EF Core 迁移 (SmartEnum) 时“找不到适合实体类型的构造函数”
- php - 为 PHP 7.3 启用 ZeroMQ 扩展的具体步骤是什么?
- c# - 来自 Azure Active Directory 的 JWT 中的角色声明的 Azure HTTP 触发器函数授权