diff options
| author | Andrew <saintruler@gmail.com> | 2021-04-11 19:32:05 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-04-11 19:32:05 +0400 |
| commit | 42ceee1e4d99d41f3a4c52ff7a0cb5d95c6c3831 (patch) | |
| tree | 74b46ced8a0d08b4b3c94991a943d12e0b62fc2a | |
| parent | ffe4cbeb66522b83375cc92f3cd7cd254f88a5f4 (diff) | |
Added laser ellipse
| -rw-r--r-- | ellipse/main.py | 191 |
1 files changed, 155 insertions, 36 deletions
diff --git a/ellipse/main.py b/ellipse/main.py index 93af34e..758800f 100644 --- a/ellipse/main.py +++ b/ellipse/main.py @@ -3,17 +3,27 @@ import pygame from pygame.math import Vector2 import math -from math import sin, cos, pi, sqrt +from math import sin, cos, pi, sqrt, hypot import time -def screen2local(x, y): +def screen2local(x, y=None): + # Если аргументом передаётся Vector2 + if y is None: + y = x.y + x = x.x + nx = x - WIDTH / 2 ny = HEIGHT / 2 - y return (nx, ny) -def local2screen(x, y): +def local2screen(x, y=None): + # Если аргументом передаётся Vector2 + if y is None: + y = x.y + x = x.x + nx = x + WIDTH / 2 ny = HEIGHT / 2 - y return (nx, ny) @@ -152,24 +162,72 @@ class EllipseRay: def eps(self, x, y): a = self.a b = self.b - tmp = (x * x) / (a * a) + (y * y) / (b * b) + tmp = self.ellipse(x, y) e = 0.05 - return tmp < 1 + e and tmp > 1 - e + return tmp < e and tmp > e + + def hit(self): + eps = 0.001 + npos = self.pos + self.dir + tmp = self.ellipse(npos.x, npos.y) + + if tmp > eps: + return self.find_point(self.pos.x, self.pos.y, npos.x, npos.y) + else: + return None + + def ellipse(self, x, y): + return (x * x) / (self.a * self.a) + (y * y) / (self.b * self.b) - 1 + + def find_point(self, x0, y0, x1, y1, depth=0): + eps = 0.0001 + xc = (x0 + x1) / 2 + yc = (y0 + y1) / 2 + tmp = self.ellipse(xc, yc) + # if abs(tmp) < eps or hypot(x1 - x0, y1 - y0) < eps: + if abs(tmp) < eps: + return Vector2(xc, yc) + elif tmp >= eps: + # print(tmp, (x0, y0), (x1, y1), (xc, yc), (x1 - x0, y1 - y0)) + t1 = self.ellipse(xc, yc) + t2 = self.ellipse(x0, y0) + if t1 > 0 and t2 > 0: + print(">", t1, t2) + + if depth > 20: + print("> ret") + return Vector2(xc, yc) + return self.find_point(x0, y0, xc, yc, depth+1) + elif tmp <= -eps: + t1 = self.ellipse(xc, yc) + t2 = self.ellipse(x1, y1) + if t1 > 0 and t2 > 0: + print("<", t1, t2) + + if depth > 20: + print("< ret") + return Vector2(xc, yc) + return self.find_point(xc, yc, x1, y1, depth+1) def update(self, deltatime): npos = self.pos + self.dir * (deltatime * self.speed) - + + hit_result = self.hit() + if hit_result is None: + self.pos = npos + return + + npos = hit_result scr = Vector2(*local2screen(*tuple(self.pos))) scr_npos = Vector2(*local2screen(*tuple(npos))) - - p0 = self.pos + p0 = npos ######################## # Отражение от эллипса # ######################## - if p0.y == 0: - if p0.x < 0: + if npos.y == 0: + if npos.x < 0: self.dir.reflect_ip(Vector2(-1, 0)) else: self.dir.reflect_ip(Vector2(1, 0)) @@ -186,8 +244,7 @@ class EllipseRay: kas_p = Vector2(1, -kas.x / kas.y) kas_p.normalize_ip() - if self.eps(npos.x, npos.y): - self.dir.reflect_ip(kas_p) + self.dir.reflect_ip(kas_p) ######################## @@ -200,13 +257,73 @@ class EllipseRay: pygame.draw.circle(screen, RED, scr, 3) - -class Ray1Ellipse: - def __init__(self, a, b): +class LaserEllipse: + def __init__(self, a, b, ray_t=0, ray_end=None): self.a = a self.b = b + if ray_end is None: + ray_end = Vector2(0, 0) + + self.ray_end = ray_end + self.ray_t = ray_t + + self.c = sqrt(self.a * self.a - self.b * self.b) + self.f1 = Vector2(self.c, 0) + self.f2 = Vector2(-self.c, 0) + + def ellipse(self, x, y): + return (x * x) / (self.a * self.a) + (y * y) / (self.b * self.b) - 1 + + def get_normal(self, p0): + m = 1 + n = -(p0.x * self.b * self.b) / (p0.y * self.a * self.a) + p1 = Vector2(p0.x + m, p0.y + n) + + kas = Vector2(p1.x - p0.x, p1.y - p0.y) + kas.normalize_ip() + + normal = Vector2(1, -kas.x / kas.y) + return normal.normalize() + + def find_point(self, x0, y0, x1, y1, depth=0): + eps = 0.0001 + xc = (x0 + x1) / 2 + yc = (y0 + y1) / 2 + tmp = self.ellipse(xc, yc) + if abs(tmp) < eps or hypot(x1 - x0, y1 - y0) < eps: + return Vector2(xc, yc) + elif tmp >= eps: + return self.find_point(x0, y0, xc, yc, depth+1) + elif tmp <= -eps: + return self.find_point(xc, yc, x1, y1, depth+1) + + def parametric(self, t): + return Vector2(self.a * cos(t), self.b * sin(t)) + + def draw_lasers(self, screen): + start = self.parametric(self.ray_t) + d = (self.ray_end - start).normalize() + end = self.get_laser(start, d) + for i in range(50): + pygame.draw.line(screen, RED, local2screen(start), local2screen(end), 2) + + n = self.get_normal(end) + start = end + d.reflect_ip(n) + end = self.get_laser(start, d) + + def get_laser(self, start: Vector2, direction: Vector2) -> Vector2: + tmp = start + direction * ((self.a + self.b) ** 2) + end = self.find_point(start.x, start.y, tmp.x, tmp.y) + return end + + def update(self, deltatime): + self.ray_t += deltatime / 10 + def draw(self, deltatime, screen, cx, cy): + self.draw_lasers(screen) + cnt = 0 for i in range(0, int(2 * pi * 100) + 1): cnt += 1 @@ -214,23 +331,24 @@ class Ray1Ellipse: t1 = t + 1 / 100 # Координаты в локальном пространстве - x = self.a * cos(t) - y = self.b * sin(t) - x1 = self.a * cos(t1) - y1 = self.b * sin(t1) + p0 = self.parametric(t) + p1 = self.parametric(t1) - start = local2screen(x, y) - end = local2screen(x1, y1) + start = local2screen(p0) + end = local2screen(p1) pygame.draw.line(screen, WHITE, start, end, 2) - - c = sqrt(self.a * self.a - self.b * self.b) - f1 = local2screen(c, 0) - f2 = local2screen(-c, 0) - pygame.draw.circle(screen, WHITE, f1, 3) - pygame.draw.circle(screen, WHITE, f2, 3) - + pygame.draw.circle(screen, WHITE, local2screen(self.f1), 3) + pygame.draw.circle(screen, WHITE, local2screen(self.f2), 3) + + ray_start = self.parametric(self.ray_t) + start = local2screen(ray_start) + d = (self.ray_end - ray_start).normalize() * 30 + end = local2screen(ray_start + d) + + pygame.draw.line(screen, WHITE, start, end, 2) + pygame.init() @@ -246,16 +364,16 @@ WHITE = Color("white") GREEN = Color("green") RED = Color("red") -el = Ellipse(300, 200) +el = LaserEllipse(300, 200, ray_t=200, ray_end=Vector2(0, 100)) rays = [] -nn = 10 -for i in range(1, nn + 1): - x0, y0 = el.f2 - xr = cos(2 * pi * i / nn) + x0 - yr = sin(2 * pi * i / nn) + y0 - r = EllipseRay(Vector2(x0, y0), Vector2(xr, yr), 70, el) - rays.append(r) +# nn = 10 +# for i in range(1, nn + 1): + # x0, y0 = el.f2 + # xr = cos(2 * pi * i / nn) + x0 + # yr = sin(2 * pi * i / nn) + y0 + # r = EllipseRay(Vector2(x0, y0), Vector2(xr, yr), 70, el) + # rays.append(r) # deltatime = pygame.time.get_ticks() @@ -268,6 +386,7 @@ while running: r.update(deltatime) r.draw(screen) el.draw(deltatime, screen, 0, 0) + el.update(deltatime) # ... |