You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
4.6 KiB
151 lines
4.6 KiB
from __future__ import annotations |
|
|
|
import math |
|
import uuid |
|
from typing import TYPE_CHECKING, Any, List, Optional, Union |
|
|
|
if TYPE_CHECKING: |
|
from .scene import Scene, SceneObject |
|
|
|
|
|
class Object3D: |
|
current_scene: Optional[Scene] = None |
|
|
|
def __init__(self, type_: str, *args: Any) -> None: |
|
self.type = type_ |
|
self.id = str(uuid.uuid4()) |
|
self.name: Optional[str] = None |
|
assert self.current_scene is not None |
|
self.scene: Scene = self.current_scene |
|
self.scene.objects[self.id] = self |
|
self.parent: Union[Object3D, SceneObject] = self.scene.stack[-1] |
|
self.args: List = list(args) |
|
self.color: str = '#ffffff' |
|
self.opacity: float = 1.0 |
|
self.side_: str = 'front' |
|
self.visible_: bool = True |
|
self.draggable_: bool = False |
|
self.x: float = 0 |
|
self.y: float = 0 |
|
self.z: float = 0 |
|
self.R: List[List[float]] = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] |
|
self.sx: float = 1 |
|
self.sy: float = 1 |
|
self.sz: float = 1 |
|
self._create() |
|
|
|
def with_name(self, name: str): |
|
self.name = name |
|
self._name() |
|
return self |
|
|
|
def send(self) -> None: |
|
self._create() |
|
self._name() |
|
self._material() |
|
self._move() |
|
self._rotate() |
|
self._scale() |
|
self._visible() |
|
self._draggable() |
|
|
|
def __enter__(self): |
|
self.scene.stack.append(self) |
|
return self |
|
|
|
def __exit__(self, *_): |
|
self.scene.stack.pop() |
|
|
|
def _create(self) -> None: |
|
self.scene.run_method('create', self.type, self.id, self.parent.id, *self.args) |
|
|
|
def _name(self) -> None: |
|
self.scene.run_method('name', self.id, self.name) |
|
|
|
def _material(self) -> None: |
|
self.scene.run_method('material', self.id, self.color, self.opacity, self.side_) |
|
|
|
def _move(self) -> None: |
|
self.scene.run_method('move', self.id, self.x, self.y, self.z) |
|
|
|
def _rotate(self) -> None: |
|
self.scene.run_method('rotate', self.id, self.R) |
|
|
|
def _scale(self) -> None: |
|
self.scene.run_method('scale', self.id, self.sx, self.sy, self.sz) |
|
|
|
def _visible(self) -> None: |
|
self.scene.run_method('visible', self.id, self.visible_) |
|
|
|
def _draggable(self) -> None: |
|
self.scene.run_method('draggable', self.id, self.draggable_) |
|
|
|
def _delete(self) -> None: |
|
self.scene.run_method('delete', self.id) |
|
|
|
def material(self, color: str = '#ffffff', opacity: float = 1.0, side: str = 'front'): |
|
if self.color != color or self.opacity != opacity or self.side_ != side: |
|
self.color = color |
|
self.opacity = opacity |
|
self.side_ = side |
|
self._material() |
|
return self |
|
|
|
def move(self, x: float = 0.0, y: float = 0.0, z: float = 0.0): |
|
if self.x != x or self.y != y or self.z != z: |
|
self.x = x |
|
self.y = y |
|
self.z = z |
|
self._move() |
|
return self |
|
|
|
@staticmethod |
|
def rotation_matrix_from_euler(r_x: float, r_y: float, r_z: float) -> List[List[float]]: |
|
sx, cx = math.sin(r_x), math.cos(r_x) |
|
sy, cy = math.sin(r_y), math.cos(r_y) |
|
sz, cz = math.sin(r_z), math.cos(r_z) |
|
return [ |
|
[cz * cy, -sz * cx + cz * sy * sx, sz * sx + cz * sy * cx], |
|
[sz * cy, cz * cx + sz * sy * sx, -cz * sx + sz * sy * cx], |
|
[-sy, cy * sx, cy * cx], |
|
] |
|
|
|
def rotate(self, r_x: float, r_y: float, r_z: float) -> None: |
|
return self.rotate_R(self.rotation_matrix_from_euler(r_x, r_y, r_z)) |
|
|
|
def rotate_R(self, R: List[List[float]]): |
|
if self.R != R: |
|
self.R = R |
|
self._rotate() |
|
return self |
|
|
|
def scale(self, sx: float = 1.0, sy: Optional[float] = None, sz: Optional[float] = None): |
|
if sy is None: |
|
sy = sx |
|
if sz is None: |
|
sz = sx |
|
if self.sx != sx or self.sy != sy or self.sz != sz: |
|
self.sx = sx |
|
self.sy = sy |
|
self.sz = sz |
|
self._scale() |
|
return self |
|
|
|
def visible(self, value: bool = True): |
|
if self.visible_ != value: |
|
self.visible_ = value |
|
self._visible() |
|
return self |
|
|
|
def draggable(self, value: bool = True): |
|
if self.draggable_ != value: |
|
self.draggable_ = value |
|
self._draggable() |
|
return self |
|
|
|
def delete(self) -> None: |
|
children = [object for object in self.scene.objects.values() if object.parent == self] |
|
for child in children: |
|
child.delete() |
|
del self.scene.objects[self.id] |
|
self._delete()
|
|
|