basilisk-engine 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
Potentially problematic release.
This version of basilisk-engine might be problematic. Click here for more details.
- basilisk/collisions/broad/broad_aabb.py +8 -1
- basilisk/collisions/broad/broad_bvh.py +8 -1
- basilisk/collisions/collider.py +15 -6
- basilisk/collisions/collider_handler.py +70 -68
- basilisk/collisions/narrow/contact_manifold.py +9 -10
- basilisk/collisions/narrow/dataclasses.py +27 -0
- basilisk/collisions/narrow/deprecated.py +47 -0
- basilisk/collisions/narrow/epa.py +11 -10
- basilisk/collisions/narrow/gjk.py +15 -14
- basilisk/collisions/narrow/helper.py +8 -7
- basilisk/collisions/narrow/sutherland_hodgman.py +52 -0
- basilisk/draw/draw_handler.py +5 -3
- basilisk/engine.py +14 -5
- basilisk/generic/abstract_custom.py +134 -0
- basilisk/generic/collisions.py +46 -0
- basilisk/generic/quat.py +77 -66
- basilisk/generic/vec3.py +91 -67
- basilisk/mesh/cube.py +20 -2
- basilisk/mesh/mesh.py +69 -54
- basilisk/mesh/mesh_from_data.py +106 -21
- basilisk/mesh/narrow_aabb.py +10 -1
- basilisk/mesh/narrow_bvh.py +9 -1
- basilisk/nodes/node.py +169 -30
- basilisk/nodes/node_handler.py +51 -30
- basilisk/particles/__init__.py +0 -0
- basilisk/particles/particle_handler.py +55 -0
- basilisk/particles/particle_renderer.py +87 -0
- basilisk/physics/impulse.py +7 -13
- basilisk/physics/physics_body.py +10 -2
- basilisk/physics/physics_engine.py +1 -2
- basilisk/render/batch.py +2 -2
- basilisk/render/camera.py +5 -0
- basilisk/render/chunk.py +19 -4
- basilisk/render/chunk_handler.py +33 -26
- basilisk/render/image.py +1 -1
- basilisk/render/image_handler.py +7 -5
- basilisk/render/light_handler.py +16 -11
- basilisk/render/material.py +25 -1
- basilisk/render/material_handler.py +22 -13
- basilisk/render/shader.py +7 -7
- basilisk/render/shader_handler.py +13 -12
- basilisk/render/sky.py +5 -3
- basilisk/scene.py +114 -32
- basilisk/shaders/batch.frag +40 -11
- basilisk/shaders/batch.vert +14 -7
- basilisk/shaders/normal.frag +5 -5
- basilisk/shaders/normal.vert +8 -3
- basilisk/shaders/particle.frag +72 -0
- basilisk/shaders/particle.vert +85 -0
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.2.dist-info}/METADATA +5 -5
- basilisk_engine-0.1.2.dist-info/RECORD +95 -0
- basilisk/shaders/image.png +0 -0
- basilisk_engine-0.1.0.dist-info/RECORD +0 -88
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.2.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.2.dist-info}/top_level.txt +0 -0
basilisk/draw/draw_handler.py
CHANGED
|
@@ -4,6 +4,7 @@ import glm
|
|
|
4
4
|
from math import cos, sin, atan2
|
|
5
5
|
from ..render.image import Image
|
|
6
6
|
from .font_renderer import FontRenderer
|
|
7
|
+
from ..render.shader import Shader
|
|
7
8
|
|
|
8
9
|
class DrawHandler():
|
|
9
10
|
engine: ...
|
|
@@ -27,8 +28,9 @@ class DrawHandler():
|
|
|
27
28
|
self.engine = scene.engine
|
|
28
29
|
self.ctx = scene.engine.ctx
|
|
29
30
|
|
|
30
|
-
# Get the
|
|
31
|
-
|
|
31
|
+
# Get the shader
|
|
32
|
+
root = self.engine.root
|
|
33
|
+
self.shader = self.scene.shader_handler.add(Shader(self.engine, root + '/shaders/draw.vert', root + '/shaders/draw.frag'))
|
|
32
34
|
|
|
33
35
|
# Initialize draw data as blank
|
|
34
36
|
self.draw_data = []
|
|
@@ -52,7 +54,7 @@ class DrawHandler():
|
|
|
52
54
|
|
|
53
55
|
# Create buffer and VAO
|
|
54
56
|
self.vbo = self.ctx.buffer(data)
|
|
55
|
-
self.vao = self.ctx.vertex_array(self.program, [(self.vbo, '2f 4f 1i', *['in_position', 'in_color', 'in_uses_image'])], skip_errors=True)
|
|
57
|
+
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '2f 4f 1i', *['in_position', 'in_color', 'in_uses_image'])], skip_errors=True)
|
|
56
58
|
|
|
57
59
|
# Render the VAO
|
|
58
60
|
self.ctx.enable(mgl.BLEND)
|
basilisk/engine.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from sys import platform
|
|
2
3
|
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
|
|
3
4
|
import pygame as pg
|
|
4
5
|
import moderngl as mgl
|
|
@@ -36,7 +37,7 @@ class Engine():
|
|
|
36
37
|
root: str
|
|
37
38
|
"""Path to the root directory containing internal data"""
|
|
38
39
|
|
|
39
|
-
def __init__(self, win_size=(800, 800), title="Basilisk Engine", vsync=
|
|
40
|
+
def __init__(self, win_size=(800, 800), title="Basilisk Engine", vsync=None, grab_mouse=True, headless=False) -> None:
|
|
40
41
|
"""
|
|
41
42
|
Basilisk Engine Class. Sets up the engine enviornment and allows the user to interact with Basilisk
|
|
42
43
|
Args:
|
|
@@ -49,6 +50,11 @@ class Engine():
|
|
|
49
50
|
headless: bool
|
|
50
51
|
Flag for headless rendering
|
|
51
52
|
"""
|
|
53
|
+
|
|
54
|
+
if platform == 'win32' : self.platform = 'windows'
|
|
55
|
+
elif platform == 'darwin': self.platform = 'mac'
|
|
56
|
+
else: self.platform = 'linux'
|
|
57
|
+
|
|
52
58
|
# Save the window size
|
|
53
59
|
self.win_size = win_size
|
|
54
60
|
|
|
@@ -57,6 +63,8 @@ class Engine():
|
|
|
57
63
|
pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
|
|
58
64
|
pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
|
|
59
65
|
pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
|
|
66
|
+
# Check vsync against platform defaults
|
|
67
|
+
if vsync == None: vsync = True if platform == 'linux' else False
|
|
60
68
|
# Pygame display init
|
|
61
69
|
if headless:
|
|
62
70
|
pg.display.set_mode((300, 50), vsync=vsync, flags=pg.OPENGL | pg.DOUBLEBUF)
|
|
@@ -93,6 +101,7 @@ class Engine():
|
|
|
93
101
|
|
|
94
102
|
# Load a default shader
|
|
95
103
|
self.shader = Shader(self, self.root + '/shaders/batch.vert', self.root + '/shaders/batch.frag')
|
|
104
|
+
self.shader.hash = self.shader.hash + hash('engine_shader')
|
|
96
105
|
|
|
97
106
|
# Set the scene to running
|
|
98
107
|
self.running = True
|
|
@@ -107,6 +116,9 @@ class Engine():
|
|
|
107
116
|
self.time += self.delta_time
|
|
108
117
|
pg.display.set_caption(f"FPS: {round(self.clock.get_fps())}")
|
|
109
118
|
|
|
119
|
+
# Update the previous input lists for the next frame
|
|
120
|
+
self.previous_keys = self.keys
|
|
121
|
+
|
|
110
122
|
# Get inputs and events
|
|
111
123
|
self.events = pg.event.get()
|
|
112
124
|
self.keys = pg.key.get_pressed()
|
|
@@ -130,8 +142,6 @@ class Engine():
|
|
|
130
142
|
# Render after the scene and engine has been updated
|
|
131
143
|
self.render()
|
|
132
144
|
|
|
133
|
-
# Update the previous input lists for the next frame
|
|
134
|
-
self.previous_keys = self.keys
|
|
135
145
|
|
|
136
146
|
def render(self) -> None:
|
|
137
147
|
"""
|
|
@@ -179,9 +189,8 @@ class Engine():
|
|
|
179
189
|
self._scene = value
|
|
180
190
|
if self._scene:
|
|
181
191
|
self._scene.set_engine(self)
|
|
182
|
-
self.shader.use()
|
|
183
192
|
|
|
184
193
|
@shader.setter
|
|
185
194
|
def shader(self, value):
|
|
186
195
|
self._shader = value
|
|
187
|
-
if self.scene: value.
|
|
196
|
+
if self.scene: value.set_main()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Custom():
|
|
6
|
+
|
|
7
|
+
def normalize(self):
|
|
8
|
+
"""
|
|
9
|
+
Inplace normalizes the vector
|
|
10
|
+
"""
|
|
11
|
+
self.data = glm.normalize(self.data)
|
|
12
|
+
return self
|
|
13
|
+
|
|
14
|
+
def apply_function(): ... # will be overridden by child custom classes
|
|
15
|
+
|
|
16
|
+
# math functions
|
|
17
|
+
def add(self, other):
|
|
18
|
+
def func(a, b): return a + b
|
|
19
|
+
return self.apply_function(other, func, 'addition')
|
|
20
|
+
|
|
21
|
+
def subtract(self, other):
|
|
22
|
+
def func(a, b): return a - b
|
|
23
|
+
return self.apply_function(other, func, 'subtraction')
|
|
24
|
+
|
|
25
|
+
def rhs_subtract(self, other):
|
|
26
|
+
def func(a, b): return b - a
|
|
27
|
+
return self.apply_function(other, func, 'subtraction')
|
|
28
|
+
|
|
29
|
+
def multiply(self, other):
|
|
30
|
+
def func(a, b): return a * b
|
|
31
|
+
return self.apply_function(other, func, 'multiplication')
|
|
32
|
+
|
|
33
|
+
def divide(self, other):
|
|
34
|
+
def func(a, b): return a / b
|
|
35
|
+
return self.apply_function(other, func, 'division')
|
|
36
|
+
|
|
37
|
+
def rhs_divide(self, other):
|
|
38
|
+
def func(a, b): return b / a
|
|
39
|
+
return self.apply_function(other, func, 'division')
|
|
40
|
+
|
|
41
|
+
def floor_divide(self, other):
|
|
42
|
+
def func(a, b): return a // b
|
|
43
|
+
return self.apply_function(other, func, 'division')
|
|
44
|
+
|
|
45
|
+
def rhs_floor_divide(self, other):
|
|
46
|
+
def func(a, b): return b // a
|
|
47
|
+
return self.apply_function(other, func, 'division')
|
|
48
|
+
|
|
49
|
+
def mod(self, other):
|
|
50
|
+
def func(a, b): return a % b
|
|
51
|
+
return self.apply_function(other, func, 'modulus')
|
|
52
|
+
|
|
53
|
+
def rhs_mod(self, other):
|
|
54
|
+
def func(a, b): return b % a
|
|
55
|
+
return self.apply_function(other, func, 'modulus')
|
|
56
|
+
|
|
57
|
+
def pow(self, other):
|
|
58
|
+
def func(a, b): return a ** b
|
|
59
|
+
return self.apply_function(other, func, 'power')
|
|
60
|
+
|
|
61
|
+
def rhs_pow(self, other):
|
|
62
|
+
def func(a, b): return b ** a
|
|
63
|
+
return self.apply_function(other, func, 'power')
|
|
64
|
+
|
|
65
|
+
def __add__(self, other): return self.add(other) # this + that
|
|
66
|
+
def __radd__(self, other): return self.add(other) # that + this
|
|
67
|
+
def __iadd__(self, other): # this += that
|
|
68
|
+
self = self.add(other)
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def __sub__(self, other): return self.subtract(other)
|
|
72
|
+
def __rsub__(self, other): return self.rhs_subtract(other) # non-commutative
|
|
73
|
+
def __isub__(self, other):
|
|
74
|
+
self = self.subtract(other)
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def __mul__(self, other): return self.multiply(other)
|
|
78
|
+
def __rmul__(self, other): return self.multiply(other)
|
|
79
|
+
def __imul__(self, other):
|
|
80
|
+
self = self.multiply(other)
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def __truediv__(self, other): return self.divide(other)
|
|
84
|
+
def __rtruediv__(self, other): return self.rhs_divide(other) # non-commutative
|
|
85
|
+
def __itruediv__(self, other):
|
|
86
|
+
self = self.divide(other)
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
def __floordiv__(self, other): return self.floor_divide(other)
|
|
90
|
+
def __rfloordiv__(self, other): return self.rhs_floor_divide(other) # non-commutative
|
|
91
|
+
def __ifloordiv__(self, other):
|
|
92
|
+
self = self.floor_divide(other)
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
def __mod__(self, other): return self.mod(other)
|
|
96
|
+
def __rmod__(self, other): return self.rhs_mod(other)
|
|
97
|
+
def __imod__(self, other):
|
|
98
|
+
self = self.mod(other)
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
def __pow__(self, other): return self.pow(other)
|
|
102
|
+
def __rpow__(self, other): return self.rhs_pow(other)
|
|
103
|
+
def __ipow__(self, other):
|
|
104
|
+
self = self.pow(other)
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
# comparison functions
|
|
108
|
+
def __eq__(self, other):
|
|
109
|
+
if isinstance(other, Custom): return self.data == other.data
|
|
110
|
+
return self.data == other
|
|
111
|
+
|
|
112
|
+
def __ne__(self, other):
|
|
113
|
+
if isinstance(other, Custom): return self.data != other.data
|
|
114
|
+
return self.data != other
|
|
115
|
+
|
|
116
|
+
def __lt__(self, other):
|
|
117
|
+
if isinstance(other, Custom): return self.data < other.data
|
|
118
|
+
return self.data < other
|
|
119
|
+
|
|
120
|
+
def __gt__(self, other):
|
|
121
|
+
if isinstance(other, Custom): return self.data > other.data
|
|
122
|
+
return self.data > other
|
|
123
|
+
|
|
124
|
+
def __le__(self, other):
|
|
125
|
+
if isinstance(other, Custom): return self.data <= other.data
|
|
126
|
+
return self.data <= other
|
|
127
|
+
|
|
128
|
+
def __ge__(self, other):
|
|
129
|
+
if isinstance(other, Custom): return self.data >= other.data
|
|
130
|
+
return self.data >= other
|
|
131
|
+
|
|
132
|
+
# unary operators
|
|
133
|
+
def __pos__(self):
|
|
134
|
+
return self
|
basilisk/generic/collisions.py
CHANGED
|
@@ -7,6 +7,52 @@ def collide_aabb_aabb(top_right1: glm.vec3, bottom_left1: glm.vec3, top_right2:
|
|
|
7
7
|
"""
|
|
8
8
|
return all(bottom_left1[i] <= top_right2[i] + epsilon and epsilon + top_right1[i] >= bottom_left2[i] for i in range(3))
|
|
9
9
|
|
|
10
|
+
def collide_aabb_line(top_right: glm.vec3, bottom_left: glm.vec3, position: glm.vec3, forward: glm.vec3) -> bool: # TODO check algorithm
|
|
11
|
+
"""
|
|
12
|
+
Determines if an infinite line intersects with an AABB
|
|
13
|
+
"""
|
|
14
|
+
tmin, tmax = -1e10, 1e10
|
|
15
|
+
for i in range(3):
|
|
16
|
+
if forward[i]: # if forward[i] is not 0 to avoid division errors
|
|
17
|
+
|
|
18
|
+
deno = 1 / forward[i]
|
|
19
|
+
tlow = (bottom_left[i] - position[i]) * deno
|
|
20
|
+
thigh = (top_right[i] - position[i]) * deno
|
|
21
|
+
if deno < 0: tlow, thigh = thigh, tlow
|
|
22
|
+
tmin = max(tmin, tlow)
|
|
23
|
+
tmax = min(tmax, thigh)
|
|
24
|
+
if tmax <= tmin: return False
|
|
25
|
+
|
|
26
|
+
elif position[i] + 1e-7 < bottom_left[i] or position[i] > top_right[i] + 1e-7: return False
|
|
27
|
+
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
def moller_trumbore(point:glm.vec3, vec:glm.vec3, triangle:list[glm.vec3], epsilon:float=1e-7) -> glm.vec3:
|
|
31
|
+
"""
|
|
32
|
+
Determines where a line intersects with a triangle and where that intersection occurred
|
|
33
|
+
"""
|
|
34
|
+
edge1, edge2 = triangle[1] - triangle[0], triangle[2] - triangle[0]
|
|
35
|
+
ray_cross = glm.cross(vec, edge2)
|
|
36
|
+
det = glm.dot(edge1, ray_cross)
|
|
37
|
+
|
|
38
|
+
# if the ray is parallel to the triangle
|
|
39
|
+
if abs(det) < epsilon: return None
|
|
40
|
+
|
|
41
|
+
inv_det = 1 / det
|
|
42
|
+
s = point - triangle[0]
|
|
43
|
+
u = glm.dot(s, ray_cross) * inv_det
|
|
44
|
+
|
|
45
|
+
if (u < 0 and abs(u) > epsilon) or (u > 1 and abs(u - 1) > epsilon): return None
|
|
46
|
+
|
|
47
|
+
s_cross = glm.cross(s, edge1)
|
|
48
|
+
v = glm.dot(vec, s_cross) * inv_det
|
|
49
|
+
|
|
50
|
+
if (v < 0 and abs(v) > epsilon) or (u + v > 1 and abs(u + v - 1) > epsilon): return None
|
|
51
|
+
|
|
52
|
+
t = glm.dot(edge2, s_cross) * inv_det
|
|
53
|
+
if t > epsilon: return point + vec * t
|
|
54
|
+
return None
|
|
55
|
+
|
|
10
56
|
def get_sat_axes(rotation1: glm.quat, rotation2: glm.quat) -> list[glm.vec3]:
|
|
11
57
|
"""
|
|
12
58
|
Gets the axes for SAT from obb rotation matrices
|
basilisk/generic/quat.py
CHANGED
|
@@ -1,82 +1,86 @@
|
|
|
1
1
|
import glm
|
|
2
2
|
import numpy as np
|
|
3
|
+
from .abstract_custom import Custom
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
class Quat():
|
|
6
|
+
class Quat(Custom):
|
|
6
7
|
def __init__(self, *args, callback=None):
|
|
7
8
|
self.callback = callback
|
|
8
9
|
self.prev_data = glm.quat(1, 0, 0, 0)
|
|
9
|
-
self.set_data(*args)
|
|
10
|
-
|
|
11
|
-
def normalize(self):
|
|
12
|
-
"""
|
|
13
|
-
Inplace normalizes the vector
|
|
14
|
-
"""
|
|
15
|
-
self.data = glm.normalize(self.data)
|
|
16
10
|
|
|
17
|
-
def set_data(self, *args):
|
|
18
|
-
"""
|
|
19
|
-
Sets the internal vector inplace
|
|
20
|
-
"""
|
|
21
|
-
# overload constructor TODO nvernest this, definitely possible
|
|
22
11
|
if len(args) == 1:
|
|
12
|
+
|
|
23
13
|
if isinstance(args[0], Quat):
|
|
24
14
|
self.data = glm.quat(args[0].data)
|
|
15
|
+
self.prev_data = glm.quat(args[0].prev_data)
|
|
25
16
|
self.callback = args[0].callback
|
|
26
|
-
|
|
17
|
+
|
|
18
|
+
elif isinstance(args[0], glm.quat):
|
|
19
|
+
self.data = args[0]
|
|
20
|
+
|
|
27
21
|
elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
|
|
28
|
-
|
|
22
|
+
assert 2 < len(args[0]) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}'
|
|
29
23
|
self.data = glm.quat(args[0])
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
|
|
25
|
+
else:
|
|
26
|
+
try: self.data = glm.quat(args[0])
|
|
27
|
+
except: raise ValueError(f'Quat: Unexpected incoming quaternion type {args[0]}')
|
|
28
|
+
|
|
29
|
+
elif 2 < len(args) < 5: self.data = glm.quat(*args)
|
|
30
|
+
else: raise ValueError(f'Quat: Expected either a vector, quaternion, or 3 or 4 numbers, got {len(args)} values')
|
|
31
|
+
|
|
32
|
+
def apply_function(self, other, func, func_name):
|
|
33
|
+
quat = glm.quat(self.data)
|
|
34
|
+
|
|
35
|
+
if isinstance(other, (glm.vec3, glm.quat)):
|
|
36
|
+
quat = func(quat, other)
|
|
37
|
+
|
|
38
|
+
elif isinstance(other, (tuple, list, np.ndarray)):
|
|
39
|
+
assert 2 < len(other) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(other)}'
|
|
40
|
+
quat = func(quat, other)
|
|
41
|
+
|
|
42
|
+
elif isinstance(other, Custom):
|
|
43
|
+
quat = func(quat, other.data)
|
|
44
|
+
|
|
45
|
+
else:
|
|
46
|
+
try: quat = func(quat, other)
|
|
47
|
+
except: raise ValueError(f'Quat: Not an accepted type for {func_name}, got {type(other)}')
|
|
48
|
+
return Quat(quat)
|
|
33
49
|
|
|
34
|
-
#
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
38
|
-
if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
|
|
39
|
-
self.data += other
|
|
40
|
-
elif isinstance(other, Quat): self.data += other.data
|
|
41
|
-
else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
|
|
42
|
-
return self
|
|
43
|
-
|
|
44
|
-
def __isub__(self, other):
|
|
45
|
-
if isinstance(other, glm.quat): self.data -= other
|
|
46
|
-
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
47
|
-
if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
|
|
48
|
-
self.data -= other
|
|
49
|
-
elif isinstance(other, Quat): self.data -= other.data
|
|
50
|
-
else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
|
|
51
|
-
return self
|
|
52
|
-
|
|
53
|
-
def __imul__(self, other):
|
|
54
|
-
# TODO add checks for number types
|
|
55
|
-
self.data *= other
|
|
56
|
-
return self
|
|
50
|
+
# unary operators
|
|
51
|
+
def __neg__(self):
|
|
52
|
+
return Quat(-self.data, callback=self.callback)
|
|
57
53
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
self.data /= other
|
|
61
|
-
return self
|
|
54
|
+
def __abs__(self):
|
|
55
|
+
return Quat(abs(self.data), callback=self.callback)
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
# TODO add checks for number types
|
|
65
|
-
self.data //= other
|
|
66
|
-
return self
|
|
67
|
-
|
|
68
|
-
# override [_] accessor
|
|
57
|
+
# accessor functions
|
|
69
58
|
def __getitem__(self, index):
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
60
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
72
61
|
return self.data[index]
|
|
73
62
|
|
|
74
63
|
def __setitem__(self, index, value):
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
65
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
77
66
|
try: self.data[index] = value
|
|
78
67
|
except: raise ValueError(f'Quat: Invalid element type, got {type(value)}')
|
|
79
68
|
|
|
69
|
+
def __delitem__(self, index): # cannot delete an index from a quaternion, so we set the value to zero instead
|
|
70
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
71
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
72
|
+
self.data[index] = 0
|
|
73
|
+
|
|
74
|
+
def __len__(self):
|
|
75
|
+
return 4
|
|
76
|
+
|
|
77
|
+
def __iter__(self):
|
|
78
|
+
return iter(self.data)
|
|
79
|
+
|
|
80
|
+
def __contains__(self, item):
|
|
81
|
+
return item in self.data
|
|
82
|
+
|
|
83
|
+
# override str operators
|
|
80
84
|
def __repr__(self):
|
|
81
85
|
return str(self.data)
|
|
82
86
|
|
|
@@ -95,33 +99,40 @@ class Quat():
|
|
|
95
99
|
def z(self): return self.data.z
|
|
96
100
|
|
|
97
101
|
@data.setter
|
|
98
|
-
def data(self, value: glm.vec3):
|
|
99
|
-
self._data = value
|
|
100
|
-
|
|
102
|
+
def data(self, value: glm.vec3 | glm.quat):
|
|
103
|
+
self._data = glm.quat(value)
|
|
101
104
|
cur = self._data
|
|
102
105
|
prev = self.prev_data
|
|
103
106
|
thresh = 1e-6
|
|
104
107
|
|
|
105
108
|
if self.callback and (abs(cur.w - prev.w) > thresh or abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
106
|
-
self.prev_data = self._data
|
|
109
|
+
self.prev_data = glm.quat(self._data)
|
|
107
110
|
self.callback()
|
|
108
111
|
|
|
109
112
|
@w.setter
|
|
110
113
|
def w(self, value):
|
|
111
114
|
self.data.w = value
|
|
112
|
-
if self.callback
|
|
115
|
+
if self.callback and abs(value - self.prev_data.w) > 1e-6:
|
|
116
|
+
self.prev_data.w = value
|
|
117
|
+
self.callback()
|
|
113
118
|
|
|
114
119
|
@x.setter
|
|
115
120
|
def x(self, value):
|
|
116
|
-
self.
|
|
117
|
-
if self.callback
|
|
121
|
+
self._data.x = value
|
|
122
|
+
if self.callback and abs(value - self.prev_data.x) > 1e-6:
|
|
123
|
+
self.prev_data.x = value
|
|
124
|
+
self.callback()
|
|
118
125
|
|
|
119
126
|
@y.setter
|
|
120
127
|
def y(self, value):
|
|
121
|
-
self.
|
|
122
|
-
if self.callback
|
|
128
|
+
self._data.y = value
|
|
129
|
+
if self.callback and abs(value - self.prev_data.y) > 1e-6:
|
|
130
|
+
self.prev_data.y = value
|
|
131
|
+
self.callback()
|
|
123
132
|
|
|
124
133
|
@z.setter
|
|
125
134
|
def z(self, value):
|
|
126
|
-
self.
|
|
127
|
-
if self.callback
|
|
135
|
+
self._data.z = value
|
|
136
|
+
if self.callback and abs(value - self.prev_data.z) > 1e-6:
|
|
137
|
+
self.prev_data.z = value
|
|
138
|
+
self.callback()
|