diff --git a/src/fluency/rendering/base.py b/src/fluency/rendering/base.py index 1974072..048e09b 100644 --- a/src/fluency/rendering/base.py +++ b/src/fluency/rendering/base.py @@ -97,7 +97,7 @@ class Renderer(ABC): faces: np.ndarray, color: Tuple[float, float, float] = (0.2, 0.4, 0.8), name: Optional[str] = None, - ) -> RenderObject: + ) -> str: """ Add a mesh to the scene. @@ -108,7 +108,7 @@ class Renderer(ABC): name: Optional name for the object Returns: - RenderObject representing the mesh + String ID of the mesh """ pass @@ -120,7 +120,7 @@ class Renderer(ABC): color: Tuple[float, float, float] = (1.0, 1.0, 1.0), line_width: float = 1.0, name: Optional[str] = None, - ) -> RenderObject: + ) -> str: """ Add a wireframe to the scene. @@ -132,7 +132,7 @@ class Renderer(ABC): name: Optional name for the object Returns: - RenderObject representing the wireframe + String ID of the wireframe """ pass @@ -143,7 +143,7 @@ class Renderer(ABC): color: Tuple[float, float, float] = (1.0, 0.0, 0.0), size: float = 5.0, name: Optional[str] = None, - ) -> RenderObject: + ) -> str: """ Add points to the scene. @@ -154,7 +154,7 @@ class Renderer(ABC): name: Optional name for the object Returns: - RenderObject representing the points + String ID of the points """ pass @@ -166,7 +166,7 @@ class Renderer(ABC): color: Tuple[float, float, float] = (1.0, 1.0, 1.0), line_width: float = 1.0, name: Optional[str] = None, - ) -> RenderObject: + ) -> str: """ Add line segments to the scene. @@ -178,7 +178,7 @@ class Renderer(ABC): name: Optional name for the object Returns: - RenderObject representing the lines + String ID of the lines """ pass @@ -312,13 +312,13 @@ class Renderer(ABC): size: float = 100.0, divisions: int = 10, color: Tuple[float, float, float] = (0.3, 0.3, 0.3), - ) -> RenderObject: - """Add a reference grid.""" + ) -> str: + """Add a reference grid. Returns the grid ID.""" pass @abstractmethod - def add_axes(self, size: float = 10.0, visible: bool = True) -> RenderObject: - """Add coordinate axes.""" + def add_axes(self, size: float = 10.0, visible: bool = True) -> str: + """Add coordinate axes. Returns the axes ID.""" pass @abstractmethod diff --git a/src/fluency/rendering/pygfx_renderer.py b/src/fluency/rendering/pygfx_renderer.py index 6727527..1c4cd4e 100644 --- a/src/fluency/rendering/pygfx_renderer.py +++ b/src/fluency/rendering/pygfx_renderer.py @@ -55,9 +55,17 @@ class PygfxRenderObject(RenderObject): geometry: Any = None material: Any = None - def __post_init__(self) -> None: - if self.scene_node is not None: - self._scene_node = self.scene_node + def __init__( + self, + scene_node: Any = None, + geometry: Any = None, + material: Any = None, + name: Optional[str] = None, + ): + super().__init__(name=name) + self.scene_node = scene_node + self.geometry = geometry + self.material = material class PygfxRenderer(Renderer): @@ -84,15 +92,15 @@ class PygfxRenderer(Renderer): """Initialize pygfx with Qt widget.""" try: import pygfx as gfx - from wgpu.gui.qt import WgpuCanvas + from rendercanvas.qt import RenderCanvas from PySide6.QtWidgets import QVBoxLayout - self._canvas = WgpuCanvas(parent=parent_widget) + self._canvas = RenderCanvas(parent=parent_widget) self._renderer = gfx.renderers.WgpuRenderer(self._canvas) self._scene = gfx.Scene() self._camera = gfx.PerspectiveCamera(50, 16 / 9) - self._camera.position.set(100, 100, 100) + self._camera.local.position = (100, 100, 100) self._controller = gfx.OrbitController(self._camera, register_events=self._renderer) @@ -110,6 +118,9 @@ class PygfxRenderer(Renderer): except Exception as e: print(f"Failed to initialize pygfx: {e}") + import traceback + + traceback.print_exc() return False def _setup_lighting(self) -> None: @@ -120,11 +131,11 @@ class PygfxRenderer(Renderer): self._scene.add(ambient) directional = gfx.DirectionalLight(intensity=1.0) - directional.position.set(100, 100, 100) + directional.local.position = (100, 100, 100) self._scene.add(directional) fill = gfx.DirectionalLight(intensity=0.5) - fill.position.set(-100, 50, 50) + fill.local.position = (-100, 50, 50) self._scene.add(fill) def _add_grid(self) -> None: @@ -151,9 +162,10 @@ class PygfxRenderer(Renderer): faces: np.ndarray, color: Tuple[float, float, float] = (0.2, 0.4, 0.8), name: Optional[str] = None, - ) -> PygfxRenderObject: - """Add a mesh to the scene.""" + ) -> str: + """Add a mesh to the scene. Returns the mesh ID.""" import pygfx as gfx + import uuid vertices = np.asarray(vertices, dtype=np.float32) faces = np.asarray(faces, dtype=np.int32) @@ -167,11 +179,12 @@ class PygfxRenderer(Renderer): mesh = gfx.Mesh(geometry, material) self._scene.add(mesh) - obj = PygfxRenderObject(name=name, scene_node=mesh, geometry=geometry, material=material) + mesh_id = name or f"mesh_{uuid.uuid4().hex[:8]}" + obj = PygfxRenderObject(name=mesh_id, scene_node=mesh, geometry=geometry, material=material) obj.color = RenderColor(*color) self._objects.append(obj) - return obj + return mesh_id def add_wireframe( self, @@ -180,9 +193,10 @@ class PygfxRenderer(Renderer): color: Tuple[float, float, float] = (1.0, 1.0, 1.0), line_width: float = 1.0, name: Optional[str] = None, - ) -> PygfxRenderObject: - """Add a wireframe to the scene.""" + ) -> str: + """Add a wireframe to the scene. Returns the wireframe ID.""" import pygfx as gfx + import uuid positions: List[List[float]] = [] for edge in edges: @@ -197,11 +211,14 @@ class PygfxRenderer(Renderer): lines = gfx.Line(geometry, material) self._scene.add(lines) - obj = PygfxRenderObject(name=name, scene_node=lines, geometry=geometry, material=material) + wireframe_id = name or f"wireframe_{uuid.uuid4().hex[:8]}" + obj = PygfxRenderObject( + name=wireframe_id, scene_node=lines, geometry=geometry, material=material + ) obj.color = RenderColor(*color) self._objects.append(obj) - return obj + return wireframe_id def add_points( self, @@ -209,9 +226,10 @@ class PygfxRenderer(Renderer): color: Tuple[float, float, float] = (1.0, 0.0, 0.0), size: float = 5.0, name: Optional[str] = None, - ) -> PygfxRenderObject: - """Add points to the scene.""" + ) -> str: + """Add points to the scene. Returns the points ID.""" import pygfx as gfx + import uuid points_arr = np.asarray(points, dtype=np.float32) @@ -221,13 +239,14 @@ class PygfxRenderer(Renderer): points_obj = gfx.Points(geometry, material) self._scene.add(points_obj) + points_id = name or f"points_{uuid.uuid4().hex[:8]}" obj = PygfxRenderObject( - name=name, scene_node=points_obj, geometry=geometry, material=material + name=points_id, scene_node=points_obj, geometry=geometry, material=material ) obj.color = RenderColor(*color) self._objects.append(obj) - return obj + return points_id def add_lines( self, @@ -236,9 +255,10 @@ class PygfxRenderer(Renderer): color: Tuple[float, float, float] = (1.0, 1.0, 1.0), line_width: float = 1.0, name: Optional[str] = None, - ) -> PygfxRenderObject: - """Add line segments to the scene.""" + ) -> str: + """Add line segments to the scene. Returns the lines ID.""" import pygfx as gfx + import uuid positions = np.hstack([start_points, end_points]).flatten() positions = positions.reshape(-1, 3).astype(np.float32) @@ -249,11 +269,14 @@ class PygfxRenderer(Renderer): lines = gfx.Line(geometry, material) self._scene.add(lines) - obj = PygfxRenderObject(name=name, scene_node=lines, geometry=geometry, material=material) + lines_id = name or f"lines_{uuid.uuid4().hex[:8]}" + obj = PygfxRenderObject( + name=lines_id, scene_node=lines, geometry=geometry, material=material + ) obj.color = RenderColor(*color) self._objects.append(obj) - return obj + return lines_id def remove_object(self, obj: RenderObject) -> bool: """Remove an object from the scene.""" @@ -319,15 +342,14 @@ class PygfxRenderer(Renderer): up: Tuple[float, float, float] = (0, 0, 1), ) -> None: """Set camera position and orientation.""" - self._camera.position.set(*position) - self._camera.look_at(*target) - self._camera.up.set(*up) + self._camera.local.position = position + self._camera.look_at(target) def get_camera_position(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """Get camera position, target, and up vector.""" - pos = np.array(self._camera.position.to_array()) + pos = np.array(self._camera.local.position) target = np.array([0, 0, 0]) - up = np.array(self._camera.up.to_array()) + up = np.array(self._camera.local.up) return pos, target, up def fit_camera(self, padding: float = 1.1) -> None: @@ -348,8 +370,8 @@ class PygfxRenderer(Renderer): center = (min_pos + max_pos) / 2 size = np.linalg.norm(max_pos - min_pos) * padding - self._camera.position.set(center[0] + size, center[1] + size, center[2] + size) - self._camera.look_at(*center) + self._camera.local.position = (center[0] + size, center[1] + size, center[2] + size) + self._camera.look_at(tuple(center)) def set_camera_perspective( self, fov: float = 50.0, near: float = 0.1, far: float = 10000.0 @@ -392,8 +414,8 @@ class PygfxRenderer(Renderer): size: float = 100.0, divisions: int = 10, color: Tuple[float, float, float] = (0.3, 0.3, 0.3), - ) -> PygfxRenderObject: - """Add a reference grid.""" + ) -> str: + """Add a reference grid. Returns the grid ID.""" import pygfx as gfx grid = gfx.GridHelper( @@ -402,10 +424,11 @@ class PygfxRenderer(Renderer): self._scene.add(grid) obj = PygfxRenderObject(name="grid", scene_node=grid) - return obj + self._objects.append(obj) + return "grid" - def add_axes(self, size: float = 10.0, visible: bool = True) -> PygfxRenderObject: - """Add coordinate axes.""" + def add_axes(self, size: float = 10.0, visible: bool = True) -> str: + """Add coordinate axes. Returns the axes ID.""" import pygfx as gfx axes = gfx.AxesHelper(size=size) @@ -413,7 +436,8 @@ class PygfxRenderer(Renderer): self._scene.add(axes) obj = PygfxRenderObject(name="axes", scene_node=axes) - return obj + self._objects.append(obj) + return "axes" def get_screen_size(self) -> Tuple[int, int]: """Get the screen size in pixels."""