Проблема с поиском ближайшего пересечения - PullRequest
1 голос
/ 26 мая 2019

Лучи продолжают излучать не ту «стену», но только если лампа находится справа внизу. Если лампа находится в левом верхнем углу, все работает нормально.

Я много чего перепробовал, но в прошлый раз у меня была проблема, которую я написал, я много раз проверял формуляры, и в конце концов это была проблема с формулой, так что я даже не собираюсь пробовать

(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()

Ожидается, что он будет работать независимо от того, где находится лампа.

1 Ответ

1 голос
/ 26 мая 2019

Код

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) до (py, py).

Вы должны рассчитать расстояние от (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

Класс метода rays:

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)

...