threequick 0.0.1__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {threequick-0.0.1/src/threequick.egg-info → threequick-0.0.2}/PKG-INFO +1 -1
- {threequick-0.0.1 → threequick-0.0.2}/scripts/example.py +4 -1
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick/__init__.py +1 -1
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick/threequick.py +103 -51
- {threequick-0.0.1 → threequick-0.0.2/src/threequick.egg-info}/PKG-INFO +1 -1
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick.egg-info/SOURCES.txt +2 -0
- threequick-0.0.2/src/threequick.egg-info/scm_file_list.json +12 -0
- threequick-0.0.2/src/threequick.egg-info/scm_version.json +8 -0
- {threequick-0.0.1 → threequick-0.0.2}/.gitignore +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/LICENSE +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/README.md +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/pyproject.toml +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/scripts/build-install-test.sh +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/setup.cfg +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick.egg-info/dependency_links.txt +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick.egg-info/requires.txt +0 -0
- {threequick-0.0.1 → threequick-0.0.2}/src/threequick.egg-info/top_level.txt +0 -0
|
@@ -13,6 +13,8 @@ renderable_objects = []
|
|
|
13
13
|
tc = Cube(0.2)
|
|
14
14
|
# pitch roll yaw
|
|
15
15
|
# x y z
|
|
16
|
+
#tc.set_rotation(0,0,0)
|
|
17
|
+
#tc.set_position(0,0,0)
|
|
16
18
|
tc.set_rotation(30,10,60)
|
|
17
19
|
tc.set_position(1,0,0)
|
|
18
20
|
renderable_objects.append(tc)
|
|
@@ -25,7 +27,7 @@ renderable_objects.append(sp)
|
|
|
25
27
|
|
|
26
28
|
#point_cloud.add_point([0,0.25,0.25], [0,255,0])
|
|
27
29
|
|
|
28
|
-
cam_dist =
|
|
30
|
+
cam_dist = 5
|
|
29
31
|
|
|
30
32
|
camera_context = CameraContext([0,-cam_dist,0], [70,0,30], 90, window)
|
|
31
33
|
camera_context.position[0] = (math.sin(math.radians(camera_context.rotation[2])) * -cam_dist) * math.sin(math.radians(camera_context.rotation[0]))
|
|
@@ -69,6 +71,7 @@ while not done:
|
|
|
69
71
|
|
|
70
72
|
tc.set_rotation(30,10,60 + cube_angle)
|
|
71
73
|
tc.set_position(math.sin(math.radians(cube_angle)),-math.cos(math.radians(cube_angle)),0)
|
|
74
|
+
#tc.set_position(0,0,0)
|
|
72
75
|
|
|
73
76
|
# Slow rotation of camera
|
|
74
77
|
#camera_context.rotation[2] += (last_ticks - current_ticks) / 80
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .threequick import CameraContext, Object3d, Cube, Sphere, PointCloud
|
|
1
|
+
from .threequick import CameraContext, Object3d, Cube, Sphere, PointCloud, Ellipsoid
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import pygame
|
|
2
|
+
import pygame.font
|
|
2
3
|
import numpy as np
|
|
3
4
|
import math
|
|
5
|
+
import random
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
pygame.font.init()
|
|
9
|
+
sans_font = pygame.font.Font(pygame.font.get_default_font(), 36)
|
|
4
10
|
|
|
5
11
|
def transform_3d_point_4x4_mat(matrix, point):
|
|
6
12
|
# Convert to homogeneous coordinates
|
|
@@ -56,7 +62,7 @@ class CameraContext:
|
|
|
56
62
|
self.screen_size = (self.surface.get_width(), self.surface.get_height())
|
|
57
63
|
|
|
58
64
|
self.min_render_distance = 0.1
|
|
59
|
-
self.frustum_width = self.min_render_distance
|
|
65
|
+
self.frustum_width = self.min_render_distance / (math.pi - math.radians(self.fov))
|
|
60
66
|
self.frustum_height = self.frustum_width * (self.screen_size[1] / self.screen_size[0])
|
|
61
67
|
#self.projection_matrix = [[nx/2, 0, 0, (nx-1)/2],
|
|
62
68
|
# [0, ny/2, 0, (ny-1)/2],
|
|
@@ -75,6 +81,8 @@ class CameraContext:
|
|
|
75
81
|
[0, 0, c, d],
|
|
76
82
|
[0, 0,-1, 0]]
|
|
77
83
|
|
|
84
|
+
# From https://songho.ca/opengl/gl_transform.html
|
|
85
|
+
|
|
78
86
|
self.translation_matrix = [[1, 0, 0, self.position[0]],
|
|
79
87
|
[0, 1, 0, self.position[1]],
|
|
80
88
|
[0, 0, 1, self.position[2]],
|
|
@@ -105,17 +113,33 @@ class Renderable():
|
|
|
105
113
|
class Object3d(Renderable):
|
|
106
114
|
def __init__(self,
|
|
107
115
|
vertices: list[list[float]],
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
faces: list[list[int]],
|
|
117
|
+
color: Optional[list[int]] = None,
|
|
118
|
+
face_color: list[int] = None) -> None:
|
|
110
119
|
self.__mod_vertices = [None] * len(vertices)
|
|
111
120
|
#self.__mod_edges = edges
|
|
112
121
|
#self.__mod_faces = faces
|
|
113
122
|
self.vertices = vertices
|
|
114
|
-
self.edges = edges
|
|
115
123
|
self.faces = faces
|
|
124
|
+
self.face_color = face_color
|
|
116
125
|
self.position = [0, 0, 0]
|
|
126
|
+
self.color = color
|
|
117
127
|
self.rotation_matrix = pry_rot_to_4x4(0, 0, 0)
|
|
118
128
|
self.update_vertex_cache()
|
|
129
|
+
edges = set()
|
|
130
|
+
for face in self.faces:
|
|
131
|
+
edges.add((face[0], face[1]))
|
|
132
|
+
edges.add((face[1], face[2]))
|
|
133
|
+
edges.add((face[2], face[0]))
|
|
134
|
+
self.edges = list(edges)
|
|
135
|
+
if self.color is None:
|
|
136
|
+
if self.face_color is None:
|
|
137
|
+
self.face_color = []
|
|
138
|
+
for face in self.faces:
|
|
139
|
+
self.face_color.append([random.randint(0,255),random.randint(0,255),random.randint(0,255)])
|
|
140
|
+
|
|
141
|
+
for face_idx in range(len(self.faces)):
|
|
142
|
+
print(str(self.faces[face_idx]) + " => " + str(self.face_color[face_idx]))
|
|
119
143
|
|
|
120
144
|
def set_rotation(self, pitch, roll, yaw):
|
|
121
145
|
self.rotation_matrix = pry_rot_to_4x4(pitch, roll, yaw)
|
|
@@ -130,45 +154,68 @@ class Object3d(Renderable):
|
|
|
130
154
|
vertex = transform_3d_point_4x4_mat(self.rotation_matrix, vertex)
|
|
131
155
|
self.__mod_vertices[vindex] = [vertex[0] + self.position[0], vertex[1] + self.position[1], vertex[2] + self.position[2]]
|
|
132
156
|
|
|
133
|
-
def
|
|
157
|
+
def draw_wireframe(self, camera_context: CameraContext) -> None:
|
|
134
158
|
scr_center = (camera_context.screen_size[0] / 2, camera_context.screen_size[1] / 2)
|
|
135
159
|
tpf = camera_context.transform_point
|
|
136
160
|
for edge in self.edges:
|
|
137
161
|
p1 = tpf(self.__mod_vertices[edge[0]])
|
|
138
162
|
p2 = tpf(self.__mod_vertices[edge[1]])
|
|
139
163
|
if p1 is not None and p2 is not None:
|
|
140
|
-
pygame.draw.line(camera_context.surface,
|
|
164
|
+
pygame.draw.line(camera_context.surface, self.color, p1, p2)
|
|
165
|
+
|
|
166
|
+
def draw(self, camera_context: CameraContext) -> None:
|
|
167
|
+
scr_center = (camera_context.screen_size[0] / 2, camera_context.screen_size[1] / 2)
|
|
168
|
+
tpf = camera_context.transform_point
|
|
169
|
+
for idx, face in enumerate(self.faces):
|
|
170
|
+
poly_points = [tpf(self.__mod_vertices[face[0]]),
|
|
171
|
+
tpf(self.__mod_vertices[face[1]]),
|
|
172
|
+
tpf(self.__mod_vertices[face[2]])]
|
|
173
|
+
|
|
174
|
+
dir_sum = 0
|
|
175
|
+
for edgeidx in range(3):
|
|
176
|
+
p1 = poly_points[edgeidx]
|
|
177
|
+
p2 = poly_points[(edgeidx + 1) % 3]
|
|
178
|
+
dir_sum += (p2[0] - p1[0]) * (p2[1] + p1[1])
|
|
179
|
+
backface_cull = dir_sum < 0
|
|
180
|
+
|
|
181
|
+
#if not backface_cull:
|
|
182
|
+
if self.color is None:
|
|
183
|
+
if backface_cull:
|
|
184
|
+
#pygame.draw.lines(camera_context.surface, self.face_color[idx], True, poly_points)
|
|
185
|
+
pass
|
|
186
|
+
else:
|
|
187
|
+
pygame.draw.polygon(camera_context.surface, self.face_color[idx], poly_points)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
#for ptidx in range(3):
|
|
191
|
+
# text_surface = sans_font.render(str(face[ptidx]), False, (255,255,255))
|
|
192
|
+
# camera_context.surface.blit(text_surface, poly_points[ptidx])
|
|
193
|
+
else:
|
|
194
|
+
if not backface_cull:
|
|
195
|
+
pygame.draw.polygon(camera_context.surface, self.color, poly_points)
|
|
196
|
+
|
|
141
197
|
|
|
142
198
|
class Cube(Object3d):
|
|
143
|
-
edges: list[list[int]] = [
|
|
144
|
-
[0, 1],
|
|
145
|
-
[1, 2],
|
|
146
|
-
[2, 3],
|
|
147
|
-
[3, 0],
|
|
148
|
-
[4, 5],
|
|
149
|
-
[5, 6],
|
|
150
|
-
[6, 7],
|
|
151
|
-
[7, 4],
|
|
152
|
-
[0, 4],
|
|
153
|
-
[1, 5],
|
|
154
|
-
[2, 6],
|
|
155
|
-
[3, 7]
|
|
156
|
-
]
|
|
157
199
|
faces: list[list[int]] = [
|
|
158
|
-
[0,
|
|
159
|
-
[
|
|
160
|
-
|
|
161
|
-
[
|
|
162
|
-
[
|
|
163
|
-
|
|
164
|
-
[
|
|
165
|
-
[2,
|
|
166
|
-
|
|
167
|
-
[
|
|
168
|
-
[
|
|
169
|
-
|
|
200
|
+
[0,1,2],
|
|
201
|
+
[0,2,3],
|
|
202
|
+
|
|
203
|
+
[4,7,6],
|
|
204
|
+
[6,5,4],
|
|
205
|
+
|
|
206
|
+
[5,2,1],
|
|
207
|
+
[6,2,5],
|
|
208
|
+
|
|
209
|
+
[5,1,4],
|
|
210
|
+
[4,1,0],
|
|
211
|
+
|
|
212
|
+
[7,0,3],
|
|
213
|
+
[4,0,7],
|
|
214
|
+
|
|
215
|
+
[7,3,6],
|
|
216
|
+
[6,3,2]
|
|
170
217
|
]
|
|
171
|
-
def __init__(self, size) -> None:
|
|
218
|
+
def __init__(self, size, color = None) -> None:
|
|
172
219
|
size = size/2
|
|
173
220
|
vertices: list[list[float]] = [
|
|
174
221
|
[-size, -size, -size],
|
|
@@ -180,17 +227,17 @@ class Cube(Object3d):
|
|
|
180
227
|
[size, size, size],
|
|
181
228
|
[-size, size, size]
|
|
182
229
|
]
|
|
183
|
-
super().__init__(vertices, Cube.
|
|
230
|
+
super().__init__(vertices, Cube.faces, color)
|
|
184
231
|
|
|
185
|
-
class
|
|
186
|
-
def __init__(self, n_meridians, n_parallels, diameter) -> None:
|
|
232
|
+
class Ellipsoid(Object3d):
|
|
233
|
+
def __init__(self, n_meridians, n_parallels, diameter, flattening, color = None) -> None:
|
|
234
|
+
compression_factor = 1 - flattening
|
|
187
235
|
radius = diameter/2
|
|
188
|
-
vertices = [[0,0,radius],[0,0,-radius]]
|
|
189
|
-
edges = []
|
|
236
|
+
vertices = [[0,0,radius * compression_factor],[0,0,-radius * compression_factor]]
|
|
190
237
|
faces = []
|
|
191
238
|
for ipa in range(n_parallels):
|
|
192
239
|
vang = ((ipa + 1) / (n_parallels + 1)) * math.pi
|
|
193
|
-
vpos = -math.cos(vang) * radius
|
|
240
|
+
vpos = -math.cos(vang) * radius * compression_factor
|
|
194
241
|
for ime in range(n_meridians):
|
|
195
242
|
ang = (ime * math.pi * 2) / n_meridians
|
|
196
243
|
sa = math.sin(ang)
|
|
@@ -200,24 +247,29 @@ class Sphere(Object3d):
|
|
|
200
247
|
|
|
201
248
|
# bottom cap
|
|
202
249
|
for ime in range(n_meridians):
|
|
203
|
-
|
|
250
|
+
faces.append([2+ime,1,2+((ime+1)%n_meridians)])
|
|
204
251
|
|
|
205
252
|
# top cap
|
|
206
253
|
for ime in range(n_meridians):
|
|
207
|
-
|
|
254
|
+
bc_offset = (n_meridians * (n_parallels - 1))
|
|
255
|
+
faces.append([0,2+ime+bc_offset,2+((ime+1)%n_meridians)+bc_offset])
|
|
208
256
|
|
|
209
|
-
#
|
|
257
|
+
# panels
|
|
210
258
|
for ipa in range(n_parallels - 1):
|
|
211
259
|
for ime in range(n_meridians):
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
260
|
+
pt1 = 2+((ime + 0)%n_meridians)+(n_meridians * ipa)
|
|
261
|
+
pt2 = 2+((ime + 0)%n_meridians)+(n_meridians * (ipa + 1))
|
|
262
|
+
pt3 = 2+((ime + 1)%n_meridians)+(n_meridians * ipa)
|
|
263
|
+
pt4 = 2+((ime + 1)%n_meridians)+(n_meridians * (ipa + 1))
|
|
264
|
+
faces.append([pt2, pt1, pt3])
|
|
265
|
+
faces.append([pt2, pt3, pt4])
|
|
266
|
+
#edges.append([2+ime+(n_meridians * (ipa + 1)),2+ime+(n_meridians * ipa)])
|
|
267
|
+
|
|
268
|
+
super().__init__(vertices, faces, color)
|
|
269
|
+
|
|
270
|
+
class Sphere(Ellipsoid):
|
|
271
|
+
def __init__(self, n_meridians, n_parallels, diameter, color = None) -> None:
|
|
272
|
+
super().__init__(n_meridians, n_parallels, diameter, 0, color)
|
|
221
273
|
|
|
222
274
|
class PointCloud(Renderable):
|
|
223
275
|
def __init__(self, diameter = 2) -> None:
|
|
@@ -10,4 +10,6 @@ src/threequick.egg-info/PKG-INFO
|
|
|
10
10
|
src/threequick.egg-info/SOURCES.txt
|
|
11
11
|
src/threequick.egg-info/dependency_links.txt
|
|
12
12
|
src/threequick.egg-info/requires.txt
|
|
13
|
+
src/threequick.egg-info/scm_file_list.json
|
|
14
|
+
src/threequick.egg-info/scm_version.json
|
|
13
15
|
src/threequick.egg-info/top_level.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|