compas-threejs 1.0.1__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.
Files changed (45) hide show
  1. compas_threejs/__init__.py +8 -0
  2. compas_threejs/lights/__init__.py +15 -0
  3. compas_threejs/lights/ambientlight.py +19 -0
  4. compas_threejs/lights/light.py +11 -0
  5. compas_threejs/lights/pointlight.py +76 -0
  6. compas_threejs/lights/rectlight.py +73 -0
  7. compas_threejs/lights/sky.py +57 -0
  8. compas_threejs/lights/spotlight.py +143 -0
  9. compas_threejs/lights/sunlight.py +72 -0
  10. compas_threejs/materials/__init__.py +6 -0
  11. compas_threejs/materials/generic_material.py +11 -0
  12. compas_threejs/materials/line_material.py +30 -0
  13. compas_threejs/materials/material.py +161 -0
  14. compas_threejs/materials/physical_material.py +377 -0
  15. compas_threejs/materials/point_material.py +34 -0
  16. compas_threejs/metadata/__init__.py +4 -0
  17. compas_threejs/metadata/metadata.py +22 -0
  18. compas_threejs/tag/__init__.py +3 -0
  19. compas_threejs/tag/tag.py +51 -0
  20. compas_threejs/text/__init__.py +3 -0
  21. compas_threejs/text/text_geometry.py +72 -0
  22. compas_threejs/ui/__init__.py +8 -0
  23. compas_threejs/ui/button.py +27 -0
  24. compas_threejs/ui/checkbox.py +36 -0
  25. compas_threejs/ui/load_json_button.py +27 -0
  26. compas_threejs/ui/number_field.py +56 -0
  27. compas_threejs/ui/select_menu.py +33 -0
  28. compas_threejs/ui/slider.py +37 -0
  29. compas_threejs/ui/ui_element.py +11 -0
  30. compas_threejs/viewer/__init__.py +5 -0
  31. compas_threejs/viewer/app.py +454 -0
  32. compas_threejs/viewer/frontend/assets/compas_icon_white.png +0 -0
  33. compas_threejs/viewer/frontend/assets/index.css +2 -0
  34. compas_threejs/viewer/frontend/assets/index.js +4170 -0
  35. compas_threejs/viewer/frontend/index.html +25 -0
  36. compas_threejs/viewer/inbox.py +153 -0
  37. compas_threejs/viewer/outbox.py +79 -0
  38. compas_threejs/viewer/remote.py +279 -0
  39. compas_threejs/viewer/server.py +158 -0
  40. compas_threejs/viewer/workspace.py +566 -0
  41. compas_threejs-1.0.1.dist-info/METADATA +89 -0
  42. compas_threejs-1.0.1.dist-info/RECORD +45 -0
  43. compas_threejs-1.0.1.dist-info/WHEEL +5 -0
  44. compas_threejs-1.0.1.dist-info/licenses/LICENSE +21 -0
  45. compas_threejs-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,8 @@
1
+ """COMPAS ThreeJS - A lightweight Three.js viewer for COMPAS using Protobuf."""
2
+
3
+ __version__ = "1.0.1"
4
+ __author__ = "Eric Gozzi, Nicolas Benjamin Boscoboinik"
5
+ __email__ = "eric.gozzi@arch.ethz.ch"
6
+ __license__ = "MIT"
7
+
8
+ __all__ = ["__version__", "__author__", "__email__", "__license__"]
@@ -0,0 +1,15 @@
1
+ from .ambientlight import AmbientLight
2
+ from .pointlight import PointLight
3
+ from .rectlight import RectLight
4
+ from .sky import Sky
5
+ from .spotlight import SpotLight
6
+ from .sunlight import Sunlight
7
+
8
+ __all__ = [
9
+ "PointLight",
10
+ "SpotLight",
11
+ "RectLight",
12
+ "Sunlight",
13
+ "Sky",
14
+ "AmbientLight",
15
+ ]
@@ -0,0 +1,19 @@
1
+ import uuid
2
+
3
+ from compas.colors import Color
4
+
5
+
6
+ class AmbientLight:
7
+ def __init__(self, color: Color = Color.white(), intensity: float = 500):
8
+ self.color = color
9
+ self.intensity = intensity
10
+ self.guid = str(uuid.uuid4())
11
+
12
+ def as_dict(self) -> dict:
13
+ return {
14
+ "dispatch": "light",
15
+ "type": "ambient_light",
16
+ "guid": self.guid,
17
+ "color": self.color.hex,
18
+ "intensity": self.intensity,
19
+ }
@@ -0,0 +1,11 @@
1
+ from abc import ABC
2
+ from abc import abstractmethod
3
+
4
+
5
+ class Light(ABC):
6
+ def __init__(self, **kwargs):
7
+ self.attributes = kwargs
8
+
9
+ @abstractmethod
10
+ def as_dict(self):
11
+ raise NotImplementedError
@@ -0,0 +1,76 @@
1
+ from uuid import uuid4
2
+
3
+ from compas.colors import Color
4
+ from compas.geometry import Point
5
+
6
+ from .light import Light
7
+
8
+
9
+ class PointLight(Light):
10
+ """
11
+
12
+ Constructs a new PointLight instance.
13
+
14
+ Parameters
15
+ ----------
16
+ color : int | Color | str, optional
17
+ The color of the light. Can be specified as an integer, a Color object, or a string.
18
+ Default is 0xffffff (white).
19
+
20
+ intensity : float, optional
21
+ The intensity or strength of the light, measured in candela (cd).
22
+ Default is 1.
23
+
24
+ distance : float, optional
25
+ The maximum range of the light. A value of 0 indicates no limit.
26
+ Default is 0.
27
+
28
+ decay : float, optional
29
+ The rate at which the light diminishes over distance.
30
+ Default is 2.
31
+
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ point: Point = Point(0, 0, 0),
37
+ color: Color = Color.white(),
38
+ intensity: float = 100,
39
+ distance: float = 0,
40
+ decay: float = 2,
41
+ helper: bool = False,
42
+ **kwargs,
43
+ ):
44
+ super().__init__(**kwargs)
45
+ self.point = point
46
+ self.color = color
47
+ self.intensity = intensity
48
+ self.distance = distance
49
+ self.decay = decay
50
+ self.helper = helper
51
+ self.guid = str(uuid4())
52
+
53
+ def as_dict(self) -> dict:
54
+ return {
55
+ "dispatch": "light",
56
+ "type": "point_light",
57
+ "x": self.point.x,
58
+ "y": self.point.y,
59
+ "z": self.point.z,
60
+ "color": self.color.hex,
61
+ "intensity": self.intensity,
62
+ "distance": self.distance,
63
+ "decay": self.decay,
64
+ "helper": self.helper,
65
+ "guid": self.guid,
66
+ }
67
+
68
+ @property
69
+ def color(self) -> Color:
70
+ return self._color
71
+
72
+ @color.setter
73
+ def color(self, value: Color):
74
+ if not isinstance(value, Color):
75
+ raise TypeError("color must be an instance of Color")
76
+ self._color = value
@@ -0,0 +1,73 @@
1
+ import uuid
2
+
3
+ from compas.colors import Color
4
+ from compas.geometry import Point
5
+
6
+ from .light import Light
7
+
8
+
9
+ class RectLight(Light):
10
+ """
11
+ A rectangular light source.
12
+
13
+ The `RectLight` class represents a light source with a rectangular shape,
14
+ defined by its position, target direction, color, intensity, width, and height.
15
+
16
+ Attributes
17
+ ----------
18
+ point : :class:`compas.geometry.Point`
19
+ The position of the light source in 3D space. Defaults to the origin (0, 0, 0).
20
+ target : :class:`compas.geometry.Point`
21
+ The target point that the light is directed towards. Defaults to (0, -1, 0).
22
+ color : :class:`compas.colors.Color`
23
+ The color of the light. Defaults to white.
24
+ intensity : float
25
+ The intensity of the light. Defaults to 1.
26
+ width : float
27
+ The width of the rectangular light source. Defaults to 10.
28
+ height : float
29
+ The height of the rectangular light source. Defaults to 10.
30
+ helper : bool
31
+ Whether to display a helper visualization for the light. Defaults to False.
32
+ guid : str
33
+ A unique identifier for the light instance, automatically generated.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ point: Point = Point(0, 0, 0),
39
+ target: Point = Point(0, -1, 0),
40
+ color: Color = Color.white(),
41
+ intensity: float = 1,
42
+ width: float = 10,
43
+ height: float = 10,
44
+ helper: bool = False,
45
+ **kwargs,
46
+ ):
47
+ super().__init__(**kwargs)
48
+ self.point = point
49
+ self.target = target
50
+ self.color = color
51
+ self.intensity = intensity
52
+ self.width = width
53
+ self.height = height
54
+ self.helper = helper
55
+ self.guid = str(uuid.uuid4())
56
+
57
+ def as_dict(self) -> dict:
58
+ return {
59
+ "dispatch": "light",
60
+ "type": "rect_light",
61
+ "x": self.point.x,
62
+ "y": self.point.y,
63
+ "z": self.point.z,
64
+ "tx": self.target.x,
65
+ "ty": self.target.y,
66
+ "tz": self.target.z,
67
+ "color": self.color.hex,
68
+ "intensity": self.intensity,
69
+ "width": self.width,
70
+ "height": self.height,
71
+ "helper": self.helper,
72
+ "guid": self.guid,
73
+ }
@@ -0,0 +1,57 @@
1
+ import uuid
2
+
3
+ from .light import Light
4
+
5
+
6
+ class Sky(Light):
7
+ """
8
+ The Sky class represents a type of light source that simulates the sky's appearance
9
+ based on various atmospheric parameters.
10
+
11
+ Attributes:
12
+ turbidity (float): The amount of particles in the atmosphere that scatter light.
13
+ Higher values result in a hazier sky. Default is 5.
14
+ rayleigh (float): The scattering coefficient for Rayleigh scattering, which affects
15
+ the sky's blue color intensity. Default is 0.15.
16
+ mie_coefficient (float): The scattering coefficient for Mie scattering, which affects
17
+ the appearance of haze and fog. Default is 0.002.
18
+ mie_directional_g (float): The anisotropy factor for Mie scattering, controlling the
19
+ directionality of scattered light. Default is 0.95.
20
+ azimuth (float): The horizontal angle of the sun in degrees, measured clockwise from
21
+ the north. Default is 45.
22
+ elevation (float): The vertical angle of the sun in degrees, measured from the horizon.
23
+ Default is 20.
24
+ guid (str): A unique identifier for the Sky instance, generated automatically.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ turbidity: float = 5,
30
+ rayleigh: float = 0.15,
31
+ mie_coefficient: float = 0.002,
32
+ mie_directional_g: float = 0.95,
33
+ azimuth: float = 45,
34
+ elevation: float = 20,
35
+ **kwargs,
36
+ ):
37
+ super().__init__(**kwargs)
38
+ self.turbidity = turbidity
39
+ self.rayleigh = rayleigh
40
+ self.mie_coefficient = mie_coefficient
41
+ self.mie_directional_g = mie_directional_g
42
+ self.azimuth = azimuth
43
+ self.elevation = elevation
44
+ self.guid = str(uuid.uuid4())
45
+
46
+ def as_dict(self) -> dict:
47
+ return {
48
+ "dispatch": "light",
49
+ "type": "sky",
50
+ "turbidity": self.turbidity,
51
+ "rayleigh": self.rayleigh,
52
+ "mie_coefficient": self.mie_coefficient,
53
+ "mie_directional_g": self.mie_directional_g,
54
+ "azimuth": self.azimuth,
55
+ "elevation": self.elevation,
56
+ "guid": self.guid,
57
+ }
@@ -0,0 +1,143 @@
1
+ import math
2
+ import uuid
3
+
4
+ from compas.colors import Color
5
+ from compas.geometry import Point
6
+
7
+
8
+ class SpotLight:
9
+ """
10
+ A class representing a spotlight in a 3D environment.
11
+
12
+ The SpotLight class defines a light source that emits light in a specific direction
13
+ with a conical shape. It includes properties for position, target, color, intensity,
14
+ distance, angle, penumbra, and decay, among others.
15
+
16
+ Attributes
17
+ ----------
18
+ point: Point
19
+ The position of the spotlight in 3D space.
20
+ target: Point
21
+ The target point that the spotlight is directed towards.
22
+ color: Color
23
+ The color of the light emitted by the spotlight.
24
+ intensity: float
25
+ The brightness of the light. Must be non-negative.
26
+ distance: float
27
+ The maximum range of the light. Must be non-negative.
28
+ angle: float
29
+ The angle of the spotlight's cone in radians. Must be between 0 and π/2.
30
+ penumbra: float
31
+ The softness of the spotlight's edge. Must be between 0 and 1.
32
+ decay: float
33
+ The rate at which the light intensity decreases over distance. Must be non-negative.
34
+ helper: bool
35
+ Whether to display a helper visualization for the spotlight.
36
+ guid: str
37
+ A unique identifier for the spotlight instance.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ point: Point = Point(0, 0, 0),
43
+ target: Point = Point(0, 0, -1),
44
+ color: Color = Color.white(),
45
+ intensity: float = 100,
46
+ distance: float = 0,
47
+ angle: float = math.pi / 7,
48
+ penumbra: float = 0,
49
+ decay: float = 1,
50
+ helper: bool = False,
51
+ **kwargs,
52
+ ):
53
+ super().__init__(**kwargs)
54
+ self.point = point
55
+ self.target = target
56
+ self.color = color
57
+ self.intensity = intensity
58
+ self.distance = distance
59
+ self.angle = angle
60
+ self.penumbra = penumbra
61
+ self.decay = decay
62
+ self.helper = helper
63
+ self.guid = str(uuid.uuid4())
64
+
65
+ def as_dict(self) -> dict:
66
+ return {
67
+ "dispatch": "light",
68
+ "type": "spot_light",
69
+ "x": self.point.x,
70
+ "y": self.point.y,
71
+ "z": self.point.z,
72
+ "tx": self.target.x,
73
+ "ty": self.target.y,
74
+ "tz": self.target.z,
75
+ "color": self.color.hex,
76
+ "intensity": self.intensity,
77
+ "distance": self.distance,
78
+ "angle": self.angle,
79
+ "penumbra": self.penumbra,
80
+ "decay": self.decay,
81
+ "helper": self.helper,
82
+ "guid": self.guid,
83
+ }
84
+
85
+ @property
86
+ def color(self) -> Color:
87
+ return self._color
88
+
89
+ @color.setter
90
+ def color(self, value: Color):
91
+ if not isinstance(value, Color):
92
+ raise TypeError("color must be an instance of Color")
93
+ self._color = value
94
+
95
+ @property
96
+ def intensity(self) -> float:
97
+ return self._intensity
98
+
99
+ @intensity.setter
100
+ def intensity(self, value: float):
101
+ if value < 0:
102
+ raise ValueError("Intensity must be non-negative.")
103
+ self._intensity = value
104
+
105
+ @property
106
+ def distance(self) -> float:
107
+ return self._distance
108
+
109
+ @distance.setter
110
+ def distance(self, value: float):
111
+ if value < 0:
112
+ raise ValueError("Distance must be non-negative.")
113
+ self._distance = value
114
+
115
+ @property
116
+ def angle(self) -> float:
117
+ return self._angle
118
+
119
+ @angle.setter
120
+ def angle(self, value: float):
121
+ if not (0 < value <= math.pi / 2):
122
+ raise ValueError("Angle must be between 0 and π/2 radians.")
123
+ self._angle = value
124
+
125
+ @property
126
+ def penumbra(self) -> float:
127
+ return self._penumbra
128
+
129
+ @penumbra.setter
130
+ def penumbra(self, value: float):
131
+ if not (0 <= value <= 1):
132
+ raise ValueError("Penumbra must be between 0 and 1.")
133
+ self._penumbra = value
134
+
135
+ @property
136
+ def decay(self) -> float:
137
+ return self._decay
138
+
139
+ @decay.setter
140
+ def decay(self, value: float):
141
+ if value < 0:
142
+ raise ValueError("Decay must be non-negative.")
143
+ self._decay = value
@@ -0,0 +1,72 @@
1
+ import uuid
2
+
3
+ from compas.colors import Color
4
+ from compas.geometry import Point
5
+ from compas.geometry import Vector
6
+
7
+ from .light import Light
8
+
9
+
10
+ class Sunlight(Light):
11
+ """
12
+ A class representing sunlight in a 3D scene.
13
+
14
+ The Sunlight class is a type of light source that simulates sunlight. It is defined
15
+ by its color, position, direction, intensity, and an optional helper for visualization.
16
+
17
+ Attributes
18
+ ----------
19
+ color : :class:`compas.colors.Color`
20
+ The color of the sunlight. Defaults to white.
21
+ point : :class:`compas.geometry.Point`
22
+ The position of the sunlight source in 3D space. Defaults to the origin (0, 0, 0).
23
+ direction : :class:`compas.geometry.Vector`
24
+ The direction vector of the sunlight. Defaults to (-1, -1, -1).
25
+ intensity : float
26
+ The intensity of the sunlight. Defaults to 500.
27
+ helper : bool
28
+ A flag indicating whether a helper visualization is enabled. Defaults to False.
29
+ guid : str
30
+ A unique identifier for the sunlight instance, automatically generated.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ color: Color = Color.white(),
36
+ point: Point = Point(0, 0, 0),
37
+ direction: Vector = Vector(-1, -1, -1),
38
+ intensity: float = 500,
39
+ helper: bool = False,
40
+ **kwargs,
41
+ ):
42
+ super().__init__(**kwargs)
43
+ self.color = color
44
+ self.point = point
45
+ self.direction = direction
46
+ self.intensity = intensity
47
+ self.helper = helper
48
+ self.guid = str(uuid.uuid4())
49
+
50
+ @property
51
+ def target(self) -> Point:
52
+ return Point(
53
+ self.point.x + self.direction.x,
54
+ self.point.y + self.direction.y,
55
+ self.point.z + self.direction.z,
56
+ )
57
+
58
+ def as_dict(self) -> dict:
59
+ return {
60
+ "dispatch": "light",
61
+ "type": "sunlight",
62
+ "color": self.color.hex,
63
+ "x": self.point.x,
64
+ "y": self.point.y,
65
+ "z": self.point.z,
66
+ "tx": self.target.x,
67
+ "ty": self.target.y,
68
+ "tz": self.target.z,
69
+ "intensity": self.intensity,
70
+ "helper": self.helper,
71
+ "guid": self.guid,
72
+ }
@@ -0,0 +1,6 @@
1
+ from .line_material import LineMaterial
2
+ from .material import Material
3
+ from .physical_material import PhysicalMaterial
4
+ from .point_material import PointMaterial
5
+
6
+ __all__ = ["Material", "LineMaterial", "PhysicalMaterial", "PointMaterial"]
@@ -0,0 +1,11 @@
1
+ from abc import ABC
2
+ from abc import abstractmethod
3
+
4
+
5
+ class GenericMaterial(ABC):
6
+ def __init__(self, **kwargs):
7
+ self.attributes = kwargs
8
+
9
+ @abstractmethod
10
+ def as_dict(self):
11
+ raise NotImplementedError
@@ -0,0 +1,30 @@
1
+ from uuid import uuid4
2
+
3
+ from compas.colors import Color
4
+
5
+ from .generic_material import GenericMaterial
6
+
7
+
8
+ class LineMaterial(GenericMaterial):
9
+ """
10
+ Represents the material properties for rendering lines in a 3D scene.
11
+
12
+ Parameters
13
+ ----------
14
+ color : Color, optional
15
+ The color of the line. Default is blue.
16
+ """
17
+
18
+ def __init__(self, color: Color = Color.blue(), linewidth: int = 2):
19
+ self.color = color
20
+ self._geometry_guid = ""
21
+ self.guid = str(uuid4())
22
+
23
+ def as_dict(self) -> dict:
24
+ return {
25
+ "dispatch": "material",
26
+ "type": "line_material",
27
+ "geometry_guid": self._geometry_guid,
28
+ "color": self.color.hex,
29
+ "guid": self.guid,
30
+ }
@@ -0,0 +1,161 @@
1
+ from uuid import uuid4
2
+
3
+ from compas.colors import Color
4
+
5
+ from .generic_material import GenericMaterial
6
+
7
+
8
+ class Material(GenericMaterial):
9
+ """
10
+ Represents the standard material properties for a 3D object.
11
+
12
+ Parameters
13
+ ----------
14
+ color : Color, optional
15
+ The base color of the material. Default is white.
16
+ metalness : float, optional
17
+ The metalness of the material, between 0 and 1. Default is 0 (non-metallic).
18
+ roughness : float, optional
19
+ The roughness of the material, between 0 and 1. Default is 1 (fully rough).
20
+ emissive : Color, optional
21
+ The emissive color of the material. Default is black (no emission).
22
+ emissive_intensity : float, optional
23
+ The intensity of the emissive color. Default is 0 (no emission).
24
+ flat_shading : bool, optional
25
+ Whether to use flat shading. Default is False (smooth shading).
26
+ wireframe : bool, optional
27
+ Whether to render the material as a wireframe. Default is False (solid rendering).
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ color: Color = Color.white(),
33
+ metalness: float = 0,
34
+ roughness: float = 1,
35
+ emissive: Color = Color.black(),
36
+ emissive_intensity: float = 0,
37
+ flat_shading: bool = False,
38
+ wireframe: bool = False,
39
+ transparent: bool = False,
40
+ opacity: float = 1,
41
+ **kwargs,
42
+ ):
43
+ super().__init__(**kwargs)
44
+ self.color = color
45
+ self.metalness = metalness
46
+ self.roughness = roughness
47
+ self.emissive = emissive
48
+ self.emissive_intensity = emissive_intensity
49
+ self.flat_shading = flat_shading
50
+ self.wireframe = wireframe
51
+ self._geometry_guid = ""
52
+ self.transparent = transparent
53
+ self.opacity = opacity
54
+ self.guid = str(uuid4())
55
+
56
+ def as_dict(self) -> dict:
57
+ return {
58
+ "dispatch": "material",
59
+ "type": "standard_material",
60
+ "geometry_guid": self._geometry_guid,
61
+ "color": self.color.hex,
62
+ "metalness": self.metalness,
63
+ "roughness": self.roughness,
64
+ "emissive": self.emissive.hex,
65
+ "emissive_intensity": self.emissive_intensity,
66
+ "flat_shading": self.flat_shading,
67
+ "wireframe": self.wireframe,
68
+ "transparent": self.transparent,
69
+ "opacity": self.opacity,
70
+ "guid": self.guid,
71
+ }
72
+
73
+ @property
74
+ def color(self) -> Color:
75
+ return self._color
76
+
77
+ @color.setter
78
+ def color(self, value: Color):
79
+ if not isinstance(value, Color):
80
+ raise TypeError("color must be an instance of Color")
81
+ self._color = value
82
+
83
+ @property
84
+ def emissive(self) -> Color:
85
+ return self._emissive
86
+
87
+ @emissive.setter
88
+ def emissive(self, value: Color):
89
+ if not isinstance(value, Color):
90
+ raise TypeError("emissive must be an instance of Color")
91
+ self._emissive = value
92
+
93
+ @property
94
+ def metalness(self) -> float:
95
+ return self._metalness
96
+
97
+ @metalness.setter
98
+ def metalness(self, value: float):
99
+ if not (0 <= value <= 1):
100
+ raise ValueError("metalness must be between 0 and 1")
101
+ self._metalness = value
102
+
103
+ @property
104
+ def roughness(self) -> float:
105
+ return self._roughness
106
+
107
+ @roughness.setter
108
+ def roughness(self, value: float):
109
+ if not (0 <= value <= 1):
110
+ raise ValueError("roughness must be between 0 and 1")
111
+ self._roughness = value
112
+
113
+ @property
114
+ def emissive_intensity(self) -> float:
115
+ return self._emissive_intensity
116
+
117
+ @emissive_intensity.setter
118
+ def emissive_intensity(self, value: float):
119
+ if value < 0:
120
+ raise ValueError("emissive_intensity must be non-negative")
121
+ self._emissive_intensity = value
122
+
123
+ @property
124
+ def flat_shading(self) -> bool:
125
+ return self._flat_shading
126
+
127
+ @flat_shading.setter
128
+ def flat_shading(self, value: bool):
129
+ if not isinstance(value, bool):
130
+ raise TypeError("flat_shading must be a boolean")
131
+ self._flat_shading = value
132
+
133
+ @property
134
+ def wireframe(self) -> bool:
135
+ return self._wireframe
136
+
137
+ @wireframe.setter
138
+ def wireframe(self, value: bool):
139
+ if not isinstance(value, bool):
140
+ raise TypeError("wireframe must be a boolean")
141
+ self._wireframe = value
142
+
143
+ @property
144
+ def transparent(self) -> bool:
145
+ return self._transparent
146
+
147
+ @transparent.setter
148
+ def transparent(self, value: bool):
149
+ if not isinstance(value, bool):
150
+ raise TypeError("transparent must be a boolean")
151
+ self._transparent = value
152
+
153
+ @property
154
+ def opacity(self) -> float:
155
+ return self._opacity
156
+
157
+ @opacity.setter
158
+ def opacity(self, value: float):
159
+ if not (0 <= value <= 1):
160
+ raise ValueError("opacity must be between 0 and 1")
161
+ self._opacity = value