-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobject.py
More file actions
172 lines (146 loc) · 6.82 KB
/
object.py
File metadata and controls
172 lines (146 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from __future__ import annotations
import numpy as np
from numpy.typing import ArrayLike
from configurations import Configuration
from transformation_matrix import *
from camera import *
from projection import *
from numba import njit
from rapidmodule import collision
@njit(fastmath=True)
def any_(arr: ArrayLike, a: float, b: float) -> bool:
return np.any((arr == a) | (arr == b))
class Object2D():
def __init__(self,
points: ArrayLike,
config: (np.array, float) = (np.array((0., 0.)), 0.),
):
self.config : Configuration = Configuration(config=(np.array(config[0]), config[1]),
parent=None)
self.points: np.array = points
self.set_points(config)
def set_points(self, config: (np.array, float) = (np.array((0., 0.)), 0.)) -> np.array:
self.points -= self.points[0]
return self.move((np.array(config[0]), config[1]))
def move(self, act: (np.array, float) = (np.zeros(2), 0.)) -> np.array:
self.points += act[0]
rot = np.array([[np.cos(act[1]), -np.sin(act[1])],
[np.sin(act[1]), np.cos(act[1])]])
ref_point = np.array(self.points[0])
self.points -= ref_point
self.points = np.dot(self.points, rot.T) + ref_point
return self.points
@staticmethod
def scale(points: ArrayLike, zoom: float = 0.) -> np.array:
pass
class Object3D():
def __init__(self,
render,
points: ArrayLike = np.array([[0, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1], [1, 0, 0, 1],
[0, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1], [1, 0, 1, 1]]),
faces: ArrayLike = np.array([(0, 1, 2, 3), (4, 5, 6, 7), (0, 4, 5, 1),
(2, 3, 7, 6), (1, 2, 6, 5), (0, 3, 7, 4)]),
path: str | None = None,
scale: float | None = None,
p_color: tuple | pg.Color = pg.Color('red'),
l_color: tuple | pg.Color = pg.Color('orange'),
):
self.config = None
self.render = render
if path is None:
self.points = points
self.faces = faces
else:
self.points, self.faces = self.get_from_obj_file(path, scale)
self.color_faces = [(l_color, face) for face in self.faces]
self.p_color = p_color
self.draw_points = False
def get_from_obj_file(self, path: str, scale: float | None) -> tuple:
points, faces = [], []
with open(path) as obj:
for line in obj:
if line.startswith('v '):
points.append([float(i) for i in line.split()[1:4]] + [1])
elif line.startswith('f'):
faces_ = line.split()[1:]
faces.append([int(face_.split('/')[0]) - 1 for face_ in faces_])
points = np.array(points)
if scale:
points = points @ scale_f(scale)
x = (max(points, key = lambda x: x[0])[0] + min(points, key = lambda x: x[0])[0])/2
y = (max(points, key = lambda x: x[1])[1] + min(points, key = lambda x: x[1])[1])/2
z = min(points, key = lambda x: x[2])[2]
ref_point = self.get_ref_point(points) -np.array([0, 0, 0, 1])
points = points - ref_point
self.config = Configuration(np.zeros(3), np.zeros(3))
return ([np.array(p) for p in points],
[np.array(f) for f in faces])
def draw(self):
self.screen_projection()
def translate(self, trans: ArrayLike) -> None:
self.config.trans += trans
self.points = self.points @ translate(trans)
def in_collision(self, other: Object3D) -> bool:
return collision(self.rapid_mod(), other.rapid_mod())
def screen_projection(self):
points = self.points @ self.render.camera.camera_matrix()
points = points @ self.render.projection.projection_matrix
points /= points[:, -1].reshape(-1, 1)
points[(points > 2) | (points < -2)] = 0
points = points @ self.render.projection.to_screen_matrix
points = points[:, :2]
if self.draw_points:
for index, point in enumerate(points):
if not any_(point, self.render.w_width, self.render.w_height):
pg.draw.circle(
self.render.screen, self.p_color,
point, 10 * np.exp(-0.005*np.sqrt(sum((self.points[index] - self.render.camera.position) ** 2)))
)
else:
for index, color_face in enumerate(self.color_faces):
color, face = color_face
polygon = points[face]
if not any_(polygon, self.render.w_width, self.render.w_height):
pg.draw.polygon(self.render.screen, color, polygon, 1)
def set_to(self, configuration: Configuration):
trans, rot = self.config.get_act(configuration)
self.translate(trans)
self.rotation(rot)
self.config.trans = np.array(configuration.trans)
self.config.rot = np.array(configuration.rot)
def act(self, trans: np.array, rot: np.array) -> None:
self.translate(trans)
self.rotation(rot)
def rotation(self, angles: ArrayLike) -> None:
assert len(angles) == 3, 'rotation in 3D space must receive 3 angles (len(angles) == 3)'
pos = np.array(self.config.trans)
self.translate(-pos)
self.points = self.points @ rotate_x(angles[0])
self.points = self.points @ rotate_y(angles[1])
self.points = self.points @ rotate_z(angles[2])
self.config.rot += angles
self.translate(pos)
def rapid_mod(self) -> list:
return [self.points[face].tolist() for face in self.faces]
def dist_to(self, other: Object3D) -> float:
return np.sqrt(sum((self.config.trans - other.config.trans)**2))
def copy_o(self) -> Object3D:
ret = Object3D(None,points=np.copy(self.points),
faces=np.copy(self.faces))
ret.config = self.config.copy()
return ret
def get_ref_point(self, points: ArrayLike) -> np.array:
points_ = np.array(points)
avg_p = sum(points_)/len(points_)
return min(points_, key=lambda x: sum((x-avg_p)**2))
class Axes(Object3D):
def __init__(self, render):
super().__init__(render)
self.points = np.array([[0, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 0, 1],
[0, 0, 1, 1]])
self.faces = np.array([[0, 1], [0, 2], [0, 3]])
self.colors = [pg.Color(col) for col in ['red', 'green', 'blue']]
self.color_faces = [(color, face) for color, face in zip(self.colors, self.faces)]
self.config = Configuration(trans=np.zeros(3), rot=np.zeros(3))