#!/usr/bin/env python

# This example shows how to use the InteractorStyleImage and add your
# own event handling.  The InteractorStyleImage is a special
# interactor designed to be used with vtkImageActor in a rendering
# window context. It forces the camera to stay perpendicular to the
# x-y plane.

import vtk
from vtk.util.misc import vtkGetDataRoot
VTK_DATA_ROOT = vtkGetDataRoot()

# Create the image
reader = vtk.vtkPNGReader()
reader.SetDataSpacing(0.8, 0.8, 1.5)
reader.SetFileName(VTK_DATA_ROOT + "/Data/fullhead15.png")

shiftScale = vtk.vtkImageShiftScale()
shiftScale.SetInputConnection(reader.GetOutputPort())
shiftScale.SetShift(0)
shiftScale.SetScale(0.07)
shiftScale.SetOutputScalarTypeToUnsignedChar()

ia = vtk.vtkImageActor()
ia.SetInput(shiftScale.GetOutput())

# Create the RenderWindow, Renderer and both Actors
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.Initialize()

# Add the actors to the renderer, set the background and size
ren.AddActor(ia)
ren.SetBackground(0.1, 0.2, 0.4)
renWin.SetSize(400, 400)

renWin.Render()

cam1 = ren.GetActiveCamera()

ren.ResetCameraClippingRange()
renWin.Render()

### Supporting data for callbacks
pts = vtk.vtkPoints()
pts.SetNumberOfPoints(4)
lines = vtk.vtkCellArray()
lines.InsertNextCell(5)
lines.InsertCellPoint(0)
lines.InsertCellPoint(1)
lines.InsertCellPoint(2)
lines.InsertCellPoint(3)
lines.InsertCellPoint(0)
pd = vtk.vtkPolyData()
pd.SetPoints(pts)
pd.SetLines(lines)
bboxMapper = vtk.vtkPolyDataMapper2D()
bboxMapper.SetInput(pd)
bboxActor = vtk.vtkActor2D()
bboxActor.SetMapper(bboxMapper)
bboxActor.GetProperty().SetColor(1, 0, 0)
ren.AddViewProp(bboxActor)

### Functions for callbacks 
X = 0
Y = 0
bboxEnabled = 0

def StartZoom(obj, event):
    global X, Y, bboxEnabled
    xy = iren.GetEventPosition()
    X, Y = xy

    pts.SetPoint(0, X, Y, 0)
    pts.SetPoint(1, X, Y, 0)
    pts.SetPoint(2, X, Y, 0)
    pts.SetPoint(3, X, Y, 0)

    bboxEnabled = 1
    bboxActor.VisibilityOn()
 

def MouseMove(obj, event):
    global X, Y, bboxEnabled

    if bboxEnabled:
        xy = iren.GetEventPosition()
        x, y = xy
        pts.SetPoint(1, x, Y, 0)
        pts.SetPoint(2, x, y, 0)
        pts.SetPoint(3, X, y, 0)
        renWin.Render() 
 

# Do the hard stuff: pan and dolly
def EndZoom(obj, event):
    global bboxEnabled

    p1 = pts.GetPoint(0)

    p2 = pts.GetPoint(2)
    x1, y1, z1 = p1
    x2, y2, z2 = p2

    ren.SetDisplayPoint(x1, y1, 0)
    ren.DisplayToWorld()
    p1 = ren.GetWorldPoint()
    ren.SetDisplayPoint(x2, y2, 0)
    ren.DisplayToWorld()
    p2 = ren.GetWorldPoint()

    p1X, p1Y, p1Z = p1[:3]

    p2X, p2Y, p2Z = p2[:3]

    camera = ren.GetActiveCamera()
    focalPt = camera.GetFocalPoint()
    focalX, focalY, focalZ = focalPt
    
    position = camera.GetPosition()
    positionX, positionY, positionZ = position

    deltaX = focalX-(p1X+p2X)/2.0
    deltaY = focalY-(p1Y+p2Y)/2.0

    # Set camera focal point to the center of the box
    camera.SetFocalPoint((p1X+p2X)/2.0, (p1Y+p2Y)/2.0, focalZ)
    camera.SetPosition(positionX-deltaX, positionY-deltaY,positionZ)

    # Now dolly the camera to fill the box
    # This is a half-assed hack for demonstration purposes
    if p1X > p2X:
        deltaX = p1X-p2X
    else:
        deltaX = p2X-p1X
    if p1Y > p2Y:
        deltaY = p1Y-p2Y
    else:
       deltaY = p2Y-p1Y
 
    winSize = renWin.GetSize()
    winX, winY = winSize

    sx = deltaX/winX
    sy = deltaY/winY


    if sx > sy:
        dolly = 1.0+1.0/(2.0*sx)
    else:
        dolly = 1.0+1.0/(2.0*sy)
 
    camera.Dolly(dolly)
    ren.ResetCameraClippingRange()

    bboxEnabled = 0
    bboxActor.VisibilityOff()
    renWin.Render()

# Create an image interactor style and associate it with the
# interactive renderer. Then assign some callbacks with the
# appropriate events. THe callbacks are implemented as Python functions.
interactor = vtk.vtkInteractorStyleImage()
iren.SetInteractorStyle(interactor)
interactor.AddObserver("LeftButtonPressEvent", StartZoom)
interactor.AddObserver("MouseMoveEvent", MouseMove)
interactor.AddObserver("LeftButtonReleaseEvent", EndZoom)

renWin.Render()
iren.Start()
