summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew <saintruler@gmail.com>2021-04-11 19:32:05 +0400
committerAndrew <saintruler@gmail.com>2021-04-11 19:32:05 +0400
commit42ceee1e4d99d41f3a4c52ff7a0cb5d95c6c3831 (patch)
tree74b46ced8a0d08b4b3c94991a943d12e0b62fc2a
parentffe4cbeb66522b83375cc92f3cd7cd254f88a5f4 (diff)
Added laser ellipse
-rw-r--r--ellipse/main.py191
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)
# ...