寻找最近的交叉路口的问题
Problem with finding the closest intersection
光线一直投射在错误的 "wall" 上,但前提是 lamp 更多地位于右下角。如果 lamp 在左上角,一切正常。
我试了很多东西,但上次我写的有问题,我检查了很多次公式,最后是公式有问题,所以我什至没有去尝试
(https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection)
寻找最近墙的函数:
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
if 0 < t < 1 and u > 0:
px = round(x1 + t * (x2 - x1))
py = round(y1 + t * (y2 - y1))
dist = px**2+py**2
if dist < bestdist:
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
完整代码:
import pygame
import sys
import math
import random as rd
import numpy as np
class Obs(object):
def __init__(self, startp, endp):
self.startp = startp
self.endp = endp
def drawww(self):
pygame.draw.line(run.screen, pygame.Color('red'), (self.startp), (self.endp))
class rays(object):
def __init__(self, maxendpoint):
self.maxendpoint = maxendpoint
self.endpoint = []
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
if 0 < t < 1 and u > 0:
px = round(x1 + t * (x2 - x1))
py = round(y1 + t * (y2 - y1))
dist = px**2+py**2
if dist < bestdist:
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
class Control(object):
def __init__(self):
self.winw = 800
self.winh = 800
self.screen = pygame.display.set_mode((self.winw, self.winh))
self.fps = 60
self.clock = pygame.time.Clock()
self.lamp = [round(self.winw/2), round(self.winh/2)]
self.lampr = 13
self.lines = []
self.r = 5
self.Obs = []
self.angel = 0
self.fov = 360
self.scene = np.ones(self.fov)
self.done = False
self.makeobs()
def event_loop(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_F5:
self.__init__()
elif event.key == pygame.K_LEFT:
if self.angel <= 0:
self.angel = 360
else:
self.angel -= 5
elif event.key == pygame.K_RIGHT:
if self.angel >= 360:
self.angel = 0
else:
self.angel += 5
elif event.key == pygame.K_w:
self.lamp[1] -= 10
elif event.key == pygame.K_a:
self.lamp[0] -= 10
elif event.key == pygame.K_s:
self.lamp[1] += 10
elif event.key == pygame.K_d:
self.lamp[0] += 10
elif event.key == pygame.K_y:
pass
if pygame.mouse.get_pressed() == (1, 0, 0):
if pygame.mouse.get_pos()[0] > 800:
self.lamp = [799, pygame.mouse.get_pos()[1]]
else:
self.lamp[0] = pygame.mouse.get_pos()[0]
self.lamp[1] = pygame.mouse.get_pos()[1]
def draw(self):
self.screen.fill((pygame.Color('black')))
pygame.draw.circle(self.screen, pygame.Color('white'), self.lamp, self.lampr)
for obs in self.Obs:
obs.drawww()
for line in self.lines:
line.draw()
def makeobs(self):
for i in range(2):
self.Obs.append(Obs((rd.randint(0, self.winw), rd.randint(0, self.winh)),
(rd.randint(0, self.winw), rd.randint(0, self.winh))))
# self.Obs.append(Obs((0, 0), (self.winw, 0)))
# self.Obs.append(Obs((0, 0), (0, self.winh)))
# self.Obs.append(Obs((self.winw, 0), (self.winw, self.winh)))
# self.Obs.append(Obs((0, self.winh), (self.winw, self.winh)))
def createlines(self):
self.lines.clear()
for angle in range(self.angel, self.angel+self.fov):
angle = math.radians(angle)
self.lines.append(rays([self.lamp[0] + math.cos(angle), self.lamp[1] + math.sin(angle)]))
def main_loop(self):
while not self.done:
self.event_loop()
self.createlines()
self.draw()
pygame.display.update()
self.clock.tick(self.fps)
pygame.display.set_caption(f"Draw FPS: {self.clock.get_fps()}")
if __name__ == '__main__':
run = Control()
run.main_loop()
pygame.quit()
sys.exit()
无论 lamp 在哪里,都希望它能正常工作。
代码
px = round(x1 + t * (x2 - x1))
py = round(y1 + t * (y2 - y1))
dist = px**2+py**2
没有任何意义,因为(py, py)
是一个点,所以dist = px**2+py**2
是原点(0, 0)
到[=14=的平方Euclidean distance ].
您需要计算从 (x3, x4)
到交点的距离:
vx = u * (x4 - x3)
vy = u * (y4 - y3)
dist = vx**2+vy**2
此外在计算u
时还有一个问题:
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / d
方法classrays
:
class rays(object):
def __init__(self, maxendpoint):
self.maxendpoint = maxendpoint
self.endpoint = []
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / d
if 0 < t < 1 and u > 0:
vx = u * (x4 - x3)
vy = u * (y4 - y3)
dist = vx**2+vy**2
if dist < bestdist:
px = round(x3 + u * (x4 - x3))
py = round(y3 + u * (y4 - y3))
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
最小示例: repl.it/@Rabbid76/PyGame-IntersectAndCutLines
import pygame
import math
import random
def intersect(obstacles, P0, P1):
bestdist = 1000000000000000000
endpoint = P1
for Q0, Q1 in obstacles:
d = (P1[0]-P0[0]) * (Q1[1]-Q0[1]) + (P1[1]-P0[1]) * (Q0[0]-Q1[0])
if d != 0:
t = ((Q0[0]-P0[0]) * (Q1[1]-Q0[1]) + (Q0[1]-P0[1]) * (Q0[0]-Q1[0])) / d
u = ((Q0[0]-P0[0]) * (P1[1]-P0[1]) + (Q0[1]-P0[1]) * (P0[0]-P1[0])) / d
if 0 <= t <= 1 and 0 <= u <= 1:
vx, vy = (P1[0]-P0[0]) * t, (P1[1]-P0[1]) * t
dist = vx*vx + vy*vy
if dist < bestdist:
px, py = round(Q1[0] * u + Q0[0] * (1-u)), round(Q1[1] * u + Q0[1] * (1-u))
bestdist = dist
endpoint = (px, py)
return endpoint
def createRays(center):
return [(center[0] + 1200 * math.cos(angle), center[1] + 1200 * math.sin(angle)) for angle in range(0, 360, 10)]
def createObstacles(surface):
w, h = surface.get_size()
return [((random.randrange(w), random.randrange(h)), (random.randrange(w), random.randrange(h))) for _ in range(5)]
window = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()
origin = window.get_rect().center
rays = createRays(origin)
obstacles = createObstacles(window)
move_center = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
obstacles = createObstacles(window)
if event.type == pygame.KEYDOWN:
move_center = not move_center
if move_center:
origin = pygame.mouse.get_pos()
rays = createRays(origin)
window.fill(0)
for endpoint in rays:
endpoint = intersect(obstacles, origin, endpoint)
pygame.draw.line(window, (128, 128, 128), origin, endpoint)
pygame.draw.circle(window, (255, 255, 255), origin, 10)
for start, end in obstacles:
pygame.draw.line(window, (255, 0, 0), start, end)
pygame.display.flip()
pygame.quit()
exit()
光线一直投射在错误的 "wall" 上,但前提是 lamp 更多地位于右下角。如果 lamp 在左上角,一切正常。
我试了很多东西,但上次我写的有问题,我检查了很多次公式,最后是公式有问题,所以我什至没有去尝试
(https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection)
寻找最近墙的函数:
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
if 0 < t < 1 and u > 0:
px = round(x1 + t * (x2 - x1))
py = round(y1 + t * (y2 - y1))
dist = px**2+py**2
if dist < bestdist:
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
完整代码:
import pygame
import sys
import math
import random as rd
import numpy as np
class Obs(object):
def __init__(self, startp, endp):
self.startp = startp
self.endp = endp
def drawww(self):
pygame.draw.line(run.screen, pygame.Color('red'), (self.startp), (self.endp))
class rays(object):
def __init__(self, maxendpoint):
self.maxendpoint = maxendpoint
self.endpoint = []
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
if 0 < t < 1 and u > 0:
px = round(x1 + t * (x2 - x1))
py = round(y1 + t * (y2 - y1))
dist = px**2+py**2
if dist < bestdist:
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
class Control(object):
def __init__(self):
self.winw = 800
self.winh = 800
self.screen = pygame.display.set_mode((self.winw, self.winh))
self.fps = 60
self.clock = pygame.time.Clock()
self.lamp = [round(self.winw/2), round(self.winh/2)]
self.lampr = 13
self.lines = []
self.r = 5
self.Obs = []
self.angel = 0
self.fov = 360
self.scene = np.ones(self.fov)
self.done = False
self.makeobs()
def event_loop(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_F5:
self.__init__()
elif event.key == pygame.K_LEFT:
if self.angel <= 0:
self.angel = 360
else:
self.angel -= 5
elif event.key == pygame.K_RIGHT:
if self.angel >= 360:
self.angel = 0
else:
self.angel += 5
elif event.key == pygame.K_w:
self.lamp[1] -= 10
elif event.key == pygame.K_a:
self.lamp[0] -= 10
elif event.key == pygame.K_s:
self.lamp[1] += 10
elif event.key == pygame.K_d:
self.lamp[0] += 10
elif event.key == pygame.K_y:
pass
if pygame.mouse.get_pressed() == (1, 0, 0):
if pygame.mouse.get_pos()[0] > 800:
self.lamp = [799, pygame.mouse.get_pos()[1]]
else:
self.lamp[0] = pygame.mouse.get_pos()[0]
self.lamp[1] = pygame.mouse.get_pos()[1]
def draw(self):
self.screen.fill((pygame.Color('black')))
pygame.draw.circle(self.screen, pygame.Color('white'), self.lamp, self.lampr)
for obs in self.Obs:
obs.drawww()
for line in self.lines:
line.draw()
def makeobs(self):
for i in range(2):
self.Obs.append(Obs((rd.randint(0, self.winw), rd.randint(0, self.winh)),
(rd.randint(0, self.winw), rd.randint(0, self.winh))))
# self.Obs.append(Obs((0, 0), (self.winw, 0)))
# self.Obs.append(Obs((0, 0), (0, self.winh)))
# self.Obs.append(Obs((self.winw, 0), (self.winw, self.winh)))
# self.Obs.append(Obs((0, self.winh), (self.winw, self.winh)))
def createlines(self):
self.lines.clear()
for angle in range(self.angel, self.angel+self.fov):
angle = math.radians(angle)
self.lines.append(rays([self.lamp[0] + math.cos(angle), self.lamp[1] + math.sin(angle)]))
def main_loop(self):
while not self.done:
self.event_loop()
self.createlines()
self.draw()
pygame.display.update()
self.clock.tick(self.fps)
pygame.display.set_caption(f"Draw FPS: {self.clock.get_fps()}")
if __name__ == '__main__':
run = Control()
run.main_loop()
pygame.quit()
sys.exit()
无论 lamp 在哪里,都希望它能正常工作。
代码
px = round(x1 + t * (x2 - x1)) py = round(y1 + t * (y2 - y1)) dist = px**2+py**2
没有任何意义,因为(py, py)
是一个点,所以dist = px**2+py**2
是原点(0, 0)
到[=14=的平方Euclidean distance ].
您需要计算从 (x3, x4)
到交点的距离:
vx = u * (x4 - x3)
vy = u * (y4 - y3)
dist = vx**2+vy**2
此外在计算u
时还有一个问题:
u = ((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d
u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / d
方法classrays
:
class rays(object):
def __init__(self, maxendpoint):
self.maxendpoint = maxendpoint
self.endpoint = []
def draw(self):
bestdist = 1000000000000000000
for obs in run.Obs:
x1, y1 = obs.startp
x2, y2 = obs.endp
x3, y3 = run.lamp
x4, y4 = self.maxendpoint
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if d != 0:
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d
u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / d
if 0 < t < 1 and u > 0:
vx = u * (x4 - x3)
vy = u * (y4 - y3)
dist = vx**2+vy**2
if dist < bestdist:
px = round(x3 + u * (x4 - x3))
py = round(y3 + u * (y4 - y3))
bestdist = dist
self.endpoint= [px, py]
# pygame.draw.circle(run.screen, pygame.Color('green'), (px, py), 3)
if len(self.endpoint) == 2:
pygame.draw.line(run.screen, pygame.Color('white'), run.lamp, self.endpoint)
最小示例:
import pygame
import math
import random
def intersect(obstacles, P0, P1):
bestdist = 1000000000000000000
endpoint = P1
for Q0, Q1 in obstacles:
d = (P1[0]-P0[0]) * (Q1[1]-Q0[1]) + (P1[1]-P0[1]) * (Q0[0]-Q1[0])
if d != 0:
t = ((Q0[0]-P0[0]) * (Q1[1]-Q0[1]) + (Q0[1]-P0[1]) * (Q0[0]-Q1[0])) / d
u = ((Q0[0]-P0[0]) * (P1[1]-P0[1]) + (Q0[1]-P0[1]) * (P0[0]-P1[0])) / d
if 0 <= t <= 1 and 0 <= u <= 1:
vx, vy = (P1[0]-P0[0]) * t, (P1[1]-P0[1]) * t
dist = vx*vx + vy*vy
if dist < bestdist:
px, py = round(Q1[0] * u + Q0[0] * (1-u)), round(Q1[1] * u + Q0[1] * (1-u))
bestdist = dist
endpoint = (px, py)
return endpoint
def createRays(center):
return [(center[0] + 1200 * math.cos(angle), center[1] + 1200 * math.sin(angle)) for angle in range(0, 360, 10)]
def createObstacles(surface):
w, h = surface.get_size()
return [((random.randrange(w), random.randrange(h)), (random.randrange(w), random.randrange(h))) for _ in range(5)]
window = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()
origin = window.get_rect().center
rays = createRays(origin)
obstacles = createObstacles(window)
move_center = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
obstacles = createObstacles(window)
if event.type == pygame.KEYDOWN:
move_center = not move_center
if move_center:
origin = pygame.mouse.get_pos()
rays = createRays(origin)
window.fill(0)
for endpoint in rays:
endpoint = intersect(obstacles, origin, endpoint)
pygame.draw.line(window, (128, 128, 128), origin, endpoint)
pygame.draw.circle(window, (255, 255, 255), origin, 10)
for start, end in obstacles:
pygame.draw.line(window, (255, 0, 0), start, end)
pygame.display.flip()
pygame.quit()
exit()