#
# First we include the VTK Tcl packages which will make available 
# all of the vtk commands to Tcl
#
package require vtk
package require vtkinteraction

#
# Prevent the tk window from showing up then start the event loop
#
wm withdraw .

#
# Create the toplevel window
#
toplevel .top
wm title .top "Sphere Puzzle"
wm protocol .top WM_DELETE_WINDOW ::vtk::cb_exit

#
# Create some frames
#
frame .top.f1 
frame .top.f2

pack .top.f1 \
        -side top -anchor n \
        -expand 1 -fill both

pack .top.f2 \
        -side bottom -anchor s \
        -expand t -fill x

#
# Create the Tk render widget, and bind the events
#
vtkRenderWindow renWin

vtkRenderer ren1
    renWin AddRenderer ren1

vtkTkRenderWidget .top.f1.rw \
        -width 400 \
        -height 400 \
        -rw renWin

::vtk::bind_tk_render_widget .top.f1.rw

pack .top.f1.rw \
        -expand t -fill both

#
# Display some infos
#
label .top.f2.l1 -text "Position cursor over the rotation plane."
label .top.f2.l2 -text "Moving pieces will be highlighted."
label .top.f2.l3 -text "Press 'm' to make a move."

button .top.f2.reset \
        -text "Reset" \
	-command {puzzle Reset; renWin Render}

button .top.f2.b1 \
        -text "Quit" \
        -command ::vtk::cb_exit

pack .top.f2.l1 .top.f2.l2 .top.f2.l3 .top.f2.reset .top.f2.b1 \
	-side top \
        -expand t -fill x

#
# Create the pipeline
#
vtkSpherePuzzle puzzle

vtkPolyDataMapper mapper
   mapper SetInput [puzzle GetOutput]

vtkActor actor
    actor SetMapper mapper

vtkSpherePuzzleArrows arrows

vtkPolyDataMapper mapper2
   mapper2 SetInput [arrows GetOutput]

vtkActor actor2
    actor2 SetMapper mapper2

#
# Add the actors to the renderer, set the background and size
#
ren1 AddActor actor
ren1 AddActor actor2
ren1 SetBackground 0.1 0.2 0.4

ren1 ResetCamera
set cam [ren1 GetActiveCamera]
$cam Elevation -40
renWin Render

#
# Modify some bindings, use the interactor style 'switch'
#
set iren [renWin GetInteractor]
set istyle [vtkInteractorStyleSwitch istyleswitch]
$iren SetInteractorStyle $istyle
$istyle SetCurrentStyleToTrackballCamera

$iren AddObserver MouseMoveEvent MotionCallback
$iren AddObserver CharEvent CharCallback

#
# Highlight pieces
#
proc MotionCallback {} {

    global in_piece_rotation
    if {[info exists in_piece_rotation]} {
        return
    }

    global LastVal

    set iren [renWin GetInteractor]
    set istyle [[$iren GetInteractorStyle] GetCurrentStyle]

    # Return if the user is performing interaction

    if {[$istyle GetState]} {
        return
    }

    # Get mouse position

    set pos [$iren GetEventPosition]
    set x [lindex $pos 0]
    set y [lindex $pos 1]

    # Get world point

    ren1 SetDisplayPoint $x $y [ren1 GetZ $x $y]
    ren1 DisplayToWorld
    set pt [ren1 GetWorldPoint]

    set val [puzzle SetPoint [lindex $pt 0] [lindex $pt 1] [lindex $pt 2]]
    if {![info exists LastVal] || $val != $LastVal} {
	renWin Render
	set LastVal $val
    }
}

#
# Rotate the puzzle
#
proc CharCallback {} {

    set iren [renWin GetInteractor]

    set keycode [$iren GetKeyCode]
    if {$keycode != "m" && $keycode != "M"} {
        return
    }

    set pos [$iren GetEventPosition]
    ButtonCallback [lindex $pos 0] [lindex $pos 1]
}

proc ButtonCallback {x y} {

    global in_piece_rotation
    if {[info exists in_piece_rotation]} {
        return
    }
    set in_piece_rotation 1

    # Get world point

    ren1 SetDisplayPoint $x $y [ren1 GetZ $x $y]
    ren1 DisplayToWorld
    set pt [ren1 GetWorldPoint]

    set x [lindex $pt 0]
    set y [lindex $pt 1]
    set z [lindex $pt 2]

    for { set i 0} {$i <= 100} {set i [expr $i + 10]} {
	puzzle SetPoint $x $y $z
	puzzle MovePoint $i
	renWin Render
        update
    }

    unset in_piece_rotation
}

update

#
# Shuffle the puzzle
#
ButtonCallback 218 195
ButtonCallback 261 128
ButtonCallback 213 107
ButtonCallback 203 162
ButtonCallback 134 186

tkwait window .top
