#!/usr/bin/env python3
"""
VTK Assembly Demo with Cube and Axes
Features:
- Assembly at world position (100, 100, 100)
- Toggle visibility with 'a' key
- Display clipping range and bounding boxes in screen coordinates
"""

import vtk


class AssemblyDemo:
    def __init__(self):
        # Create renderer
        self.renderer = vtk.vtkRenderer()
        self.renderer.SetBackground(0.1, 0.1, 0.2)
        
        # Create render window
        self.render_window = vtk.vtkRenderWindow()
        self.render_window.AddRenderer(self.renderer)
        self.render_window.SetSize(800, 600)
        self.render_window.SetWindowName("VTK Assembly Demo - Press 'a' to toggle")
        
        # Create interactor
        self.interactor = vtk.vtkRenderWindowInteractor()
        self.interactor.SetRenderWindow(self.render_window)
        
        # State for toggling
        self.axes_visible = False
        
        # Create the assembly
        self.assembly = vtk.vtkAssembly()
        
        # Create cube (1-unit size, centered at origin)
        cube_source = vtk.vtkCubeSource()
        cube_source.SetXLength(1.0)
        cube_source.SetYLength(1.0)
        cube_source.SetZLength(1.0)
        cube_source.SetCenter(0, 0, 0)
        
        cube_mapper = vtk.vtkPolyDataMapper()
        cube_mapper.SetInputConnection(cube_source.GetOutputPort())
        
        self.cube_actor = vtk.vtkActor()
        self.cube_actor.SetMapper(cube_mapper)
        self.cube_actor.GetProperty().SetColor(0.8, 0.4, 0.2)
        self.cube_actor.GetProperty().SetOpacity(1.0)
        
        # Create axes actor at cube's origin
        self.axes_actor = vtk.vtkAxesActor()
        self.axes_actor.SetTotalLength(1.5, 1.5, 1.5)
        self.axes_actor.SetShaftType(0)  # Cylinder shaft
        self.axes_actor.SetVisibility(0)  # Initially hidden
        
        # Add actors to assembly
        self.assembly.AddPart(self.cube_actor)
        self.assembly.AddPart(self.axes_actor)
        
        # Move assembly to world position (100, 100, 100)
        self.assembly.SetPosition(100, 100, 100)
        
        # Add assembly to renderer
        self.renderer.AddActor(self.assembly)
        
        # Setup camera
        camera = self.renderer.GetActiveCamera()
        camera.SetFocalPoint(100, 100, 100)
        camera.SetPosition(100, 100, 110)
        camera.SetViewUp(0, 1, 0)
        self.renderer.ResetCamera()
        camera.Dolly(0.8)
        
        # Create text actor for displaying information
        self.text_actor = vtk.vtkTextActor()
        self.text_actor.SetPosition(10, 10)
        self.text_actor.GetTextProperty().SetFontSize(12)
        self.text_actor.GetTextProperty().SetColor(1.0, 1.0, 1.0)
        self.text_actor.GetTextProperty().SetFontFamilyToArial()
        self.renderer.AddActor2D(self.text_actor)
        
        # Add keyboard observer
        self.interactor.AddObserver("KeyPressEvent", self.on_key_press)
        
        # Update display
        self.update_display()
    
    def on_key_press(self, obj, event):
        key = self.interactor.GetKeySym()
        
        if key == 'a' or key == 'A':
            self.toggle_axes()
    
    def toggle_axes(self):
        self.axes_visible = not self.axes_visible
        
        if self.axes_visible:
            # Show axes, reduce cube opacity
            self.axes_actor.SetVisibility(1)
            self.cube_actor.GetProperty().SetOpacity(0.3)
        else:
            # Hide axes, restore cube opacity
            self.axes_actor.SetVisibility(0)
            self.cube_actor.GetProperty().SetOpacity(1.0)
        
        self.update_display()
        self.render_window.Render()
    
    def update_display(self):
        # Force render to update bounds
        self.render_window.Render()
        
        # Get camera clipping range
        camera = self.renderer.GetActiveCamera()
        self.renderer.ResetCameraClippingRange() 
 
        clipping_range = camera.GetClippingRange()
        
        # Get bounding boxes
        assembly_bounds = self.assembly.GetBounds()
        cube_bounds = self.cube_actor.GetBounds()
        axes_bounds = self.axes_actor.GetBounds()
        
        # Format information text
        info_text = f"""Camera Clipping Range: ({clipping_range[0]:.3f}, {clipping_range[1]:.3f})

Assembly Bounding Box:
  X: [{assembly_bounds[0]:.3f}, {assembly_bounds[1]:.3f}]
  Y: [{assembly_bounds[2]:.3f}, {assembly_bounds[3]:.3f}]
  Z: [{assembly_bounds[4]:.3f}, {assembly_bounds[5]:.3f}]

Cube Actor Bounding Box:
  X: [{cube_bounds[0]:.3f}, {cube_bounds[1]:.3f}]
  Y: [{cube_bounds[2]:.3f}, {cube_bounds[3]:.3f}]
  Z: [{cube_bounds[4]:.3f}, {cube_bounds[5]:.3f}]

Axes Actor Bounding Box:
  X: [{axes_bounds[0]:.3f}, {axes_bounds[1]:.3f}]
  Y: [{axes_bounds[2]:.3f}, {axes_bounds[3]:.3f}]
  Z: [{axes_bounds[4]:.3f}, {axes_bounds[5]:.3f}]

Press 'a' to toggle axes visibility"""
        
        self.text_actor.SetInput(info_text)
    
    def run(self):
        # Initialize and start interaction
        self.interactor.Initialize()
        self.render_window.Render()
        self.interactor.Start()


if __name__ == "__main__":
    demo = AssemblyDemo()
    demo.run()
