scene_object3d.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. from __future__ import annotations
  2. import math
  3. import uuid
  4. from typing import TYPE_CHECKING, Any, List, Optional, Union
  5. if TYPE_CHECKING:
  6. from .scene import Scene, SceneObject
  7. class Object3D:
  8. current_scene: Optional[Scene] = None
  9. def __init__(self, type: str, *args: Any) -> None:
  10. self.type = type
  11. self.id = str(uuid.uuid4())
  12. self.name: Optional[str] = None
  13. assert self.current_scene is not None
  14. self.scene: Scene = self.current_scene
  15. self.scene.objects[self.id] = self
  16. self.parent: Union[Object3D, SceneObject] = self.scene.stack[-1]
  17. self.args: List = list(args)
  18. self.color: str = '#ffffff'
  19. self.opacity: float = 1.0
  20. self.side_: str = 'front'
  21. self.visible_: bool = True
  22. self.draggable_: bool = False
  23. self.x: float = 0
  24. self.y: float = 0
  25. self.z: float = 0
  26. self.R: List[List[float]] = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
  27. self.sx: float = 1
  28. self.sy: float = 1
  29. self.sz: float = 1
  30. self._create()
  31. def with_name(self, name: str):
  32. self.name = name
  33. self._name()
  34. return self
  35. def send(self) -> None:
  36. self._create()
  37. self._name()
  38. self._material()
  39. self._move()
  40. self._rotate()
  41. self._scale()
  42. self._visible()
  43. self._draggable()
  44. def __enter__(self):
  45. self.scene.stack.append(self)
  46. return self
  47. def __exit__(self, *_):
  48. self.scene.stack.pop()
  49. def _create(self) -> None:
  50. self.scene.run_method('create', self.type, self.id, self.parent.id, *self.args)
  51. def _name(self) -> None:
  52. self.scene.run_method('name', self.id, self.name)
  53. def _material(self) -> None:
  54. self.scene.run_method('material', self.id, self.color, self.opacity, self.side_)
  55. def _move(self) -> None:
  56. self.scene.run_method('move', self.id, self.x, self.y, self.z)
  57. def _rotate(self) -> None:
  58. self.scene.run_method('rotate', self.id, self.R)
  59. def _scale(self) -> None:
  60. self.scene.run_method('scale', self.id, self.sx, self.sy, self.sz)
  61. def _visible(self) -> None:
  62. self.scene.run_method('visible', self.id, self.visible_)
  63. def _draggable(self) -> None:
  64. self.scene.run_method('draggable', self.id, self.draggable_)
  65. def _delete(self) -> None:
  66. self.scene.run_method('delete', self.id)
  67. def material(self, color: str = '#ffffff', opacity: float = 1.0, side: str = 'front'):
  68. if self.color != color or self.opacity != opacity or self.side_ != side:
  69. self.color = color
  70. self.opacity = opacity
  71. self.side_ = side
  72. self._material()
  73. return self
  74. def move(self, x: float = 0.0, y: float = 0.0, z: float = 0.0):
  75. if self.x != x or self.y != y or self.z != z:
  76. self.x = x
  77. self.y = y
  78. self.z = z
  79. self._move()
  80. return self
  81. @staticmethod
  82. def rotation_matrix_from_euler(r_x: float, r_y: float, r_z: float) -> List[List[float]]:
  83. sx, cx = math.sin(r_x), math.cos(r_x)
  84. sy, cy = math.sin(r_y), math.cos(r_y)
  85. sz, cz = math.sin(r_z), math.cos(r_z)
  86. return [
  87. [cz * cy, -sz * cx + cz * sy * sx, sz * sx + cz * sy * cx],
  88. [sz * cy, cz * cx + sz * sy * sx, -cz * sx + sz * sy * cx],
  89. [-sy, cy * sx, cy * cx],
  90. ]
  91. def rotate(self, r_x: float, r_y: float, r_z: float) -> None:
  92. return self.rotate_R(self.rotation_matrix_from_euler(r_x, r_y, r_z))
  93. def rotate_R(self, R: List[List[float]]):
  94. if self.R != R:
  95. self.R = R
  96. self._rotate()
  97. return self
  98. def scale(self, sx: float = 1.0, sy: Optional[float] = None, sz: Optional[float] = None):
  99. if sy is None:
  100. sy = sx
  101. if sz is None:
  102. sz = sx
  103. if self.sx != sx or self.sy != sy or self.sz != sz:
  104. self.sx = sx
  105. self.sy = sy
  106. self.sz = sz
  107. self._scale()
  108. return self
  109. def visible(self, value: bool = True):
  110. if self.visible_ != value:
  111. self.visible_ = value
  112. self._visible()
  113. return self
  114. def draggable(self, value: bool = True):
  115. if self.draggable_ != value:
  116. self.draggable_ = value
  117. self._draggable()
  118. return self
  119. def delete(self) -> None:
  120. children = [object for object in self.scene.objects.values() if object.parent == self]
  121. for child in children:
  122. child.delete()
  123. del self.scene.objects[self.id]
  124. self._delete()