require "vtk/Gtk"
require "numru/gphys"


###
# Now actually create the GUI
Gtk.init
Gtk::GL.init
root = Gtk::Window.new(Gtk::Window::TOPLEVEL)
top = Gtk::VBox.new
root.add(top)
@renWin = GtkGLExtVTKRenderWindow.new
top.pack_start(@renWin)


# Start by loading some data.
if ARGV.length==1
  gphys = NumRu::GPhys::IO.open_gturl(ARGV[0])
elsif ARGV.length==2
  fname,vname = ARGV[0..1]
  gphys = NumRu::GPhys::IO.open(fname,vname)
else
  raise "Usage: ruby #$0 filename varname"
end
gphys.rank<3 && raise("data must have at least 3 dimensions")
ind = Array.new(gphys.rank,0)
ind[0] = true
ind[1] = true
ind[2] = true
gphys = gphys[*ind]
shape = gphys.shape
min = gphys.min.val
max = gphys.max.val
data = (gphys.val-min)/(max-min)
NArrayMiss===data && data = data.get_array

x = gphys.coord(0).val
x = (x-x.min)/(x.max-x.min)
y = gphys.coord(1).val
y = (y-y.min)/(y.max-y.min)
z = gphys.coord(2).val
z = (z-z.min)/(z.max-z.min)

grid = Vtk::RectilinearGrid.new
grid.SetDimensions(*shape)
grid.SetXCoordinates(x.to_va)
grid.SetYCoordinates(y.to_va)
grid.SetZCoordinates(z.to_va)
grid.GetPointData.SetScalars(data.reshape!(shape[0]*shape[1]*shape[2]).to_va)

@xMin = x.min
@xMax = x.max
@yMin = y.min
@yMax = y.max
@zMin = z.min
@zMax = z.max




# An outline is shown for context.
outline = Vtk::OutlineFilter.new
outline.SetInput(grid)

outlineMapper = Vtk::PolyDataMapper.new
outlineMapper.SetInput(outline.GetOutput())

outlineActor = Vtk::Actor.new
outlineActor.SetMapper(outlineMapper)

# The 3 image plane widgets are used to probe the dataset.
@planeWidgetX = Vtk::Plane.new
@planeWidgetX.SetOrigin((@xMax+@xMin)/2,(@yMax+@yMin)/2,(@zMax+@zMin)/2)
@planeWidgetX.SetNormal(1,0,0)
cutX = Vtk::Cutter.new
cutX.SetInput(grid)
cutX.SetCutFunction(@planeWidgetX)
mapX = Vtk::PolyDataMapper.new
mapX.SetInput(cutX.GetOutput)
mapX.SetScalarRange(0,1)
actX = Vtk::Actor.new
actX.SetMapper(mapX)

@planeWidgetY = Vtk::Plane.new
@planeWidgetY.SetOrigin((@xMax+@xMin)/2,(@yMax+@yMin)/2,(@zMax+@zMin)/2)
@planeWidgetY.SetNormal(0,1,0)
cutY = Vtk::Cutter.new
cutY.SetInput(grid)
cutY.SetCutFunction(@planeWidgetY)
mapY = Vtk::PolyDataMapper.new
mapY.SetInput(cutY.GetOutput)
mapY.SetScalarRange(0,1)
actY = Vtk::Actor.new
actY.SetMapper(mapY)

@planeWidgetZ = Vtk::Plane.new
@planeWidgetZ.SetOrigin((@xMax+@xMin)/2,(@yMax+@yMin)/2,(@zMax+@zMin)/2)
@planeWidgetZ.SetNormal(0,0,1)
cutZ = Vtk::Cutter.new
cutZ.SetInput(grid)
cutZ.SetCutFunction(@planeWidgetZ)
mapZ = Vtk::PolyDataMapper.new
mapZ.SetInput(cutZ.GetOutput)
mapZ.SetScalarRange(0,1)
actZ = Vtk::Actor.new
actZ.SetMapper(mapZ)



# Create the RenderWindow and Renderer
@ren = Vtk::Renderer.new
@renWin.GetRenderWindow.AddRenderer(@ren)

# Add the outline actor to the renderer, set the background color and size
@ren.AddActor(outlineActor)
@ren.AddActor(actX)
@ren.AddActor(actY)
@ren.AddActor(actZ)
@renWin.set_size_request(500, 500)
@ren.SetBackground(0.1, 0.1, 0.2)

@current_Axis = "Z"

# Create the GUI
# We first create the supporting functions (callbacks) for the GUI
#
# Align the camera so that it faces the desired widget
def AlignCamera
  cx = (@xMax+@xMin)/2
  cy = (@yMax+@yMin)/2
  cz = (@zMax+@zMin)/2
  vx, vy, vz = 0, 0, 0
  nx, ny, nz = 0, 0, 0
  case @current_Axis
  when "X"
    vz = 1
    nx = (@xMax-@xMin)/2
    cx = @xMin + (@xMax-@xMin)*@x_adjust.value
  when "Y"
    vz = 1
    ny = (@yMax-@yMin)/2
    cy = @yMin + (@yMax-@yMin)*@y_adjust.value
  when "Z"
    vy = 1
    nz = (@zMax-@zMin)/2
    cz = @zMin + (@zMax-@zMin)*@z_adjust.value
  end
 
  px = cx+nx*5.2
  py = cy+ny*5.2
  pz = cz+nz*5.3

  camera = @ren.GetActiveCamera()
  camera.SetViewUp(vx, vy, vz)
  camera.SetFocalPoint(cx, cy, cz)
  camera.SetPosition(px, py, pz)
  camera.OrthogonalizeViewUp()
  @ren.ResetCameraClippingRange()
  @renWin.Render()
end
 
# Capture the display and place in a PNG
def captureImage()
  w2i = Vtk::WindowToImageFilter.new
  writer = Vtk::PNGWriter.new
  w2i.SetInput(@renWin.GetRenderWindow)
  w2i.Update()
  writer.SetInput(w2i.GetOutput())
  writer.SetFileName("image.png")
  @renWin.Render()
  writer.Write()
end
 

# Align the widget back into orthonormal position,
# set the slider to reflect the widget's position,
# call AlignCamera to set the camera facing the widget
def alignXaxis()
  @current_Axis = "X"
  AlignCamera()
end

def alignYaxis()
  @current_Axis = "Y"
  AlignCamera()
end
 
def alignZaxis()
  @current_Axis = "Z"
  AlignCamera()
end


        
def SetSlice(adj,i)
  case i
  when 0
    @planeWidgetX.SetOrigin(@xMin+(@xMax-@xMin)*adj.value,0.5,0.5)
  when 1
    @planeWidgetY.SetOrigin(0.5,@yMin+(@yMax-@yMin)*adj.value,0.5)
  when 2
    @planeWidgetZ.SetOrigin(0.5,0.5,@zMin+(@zMax-@zMin)*adj.value)
  end
  @ren.ResetCameraClippingRange()
  @renWin.Render()
end




# Buttons
ctrl_buttons = Gtk::HBox.new

quit_button = Gtk::Button.new("Quit")
quit_button.signal_connect("clicked"){ Gtk.main_quit }
capture_button = Gtk::Button.new("PNG")
capture_button.signal_connect("clicked"){ captureImage }
x_button = Gtk::Button.new("x")
x_button.signal_connect("clicked"){ alignXaxis }
y_button = Gtk::Button.new("y")
y_button.signal_connect("clicked"){ alignYaxis }
z_button = Gtk::Button.new("z")
z_button.signal_connect("clicked"){ alignZaxis }
ctrl_buttons.pack_start(quit_button)
ctrl_buttons.pack_start(capture_button)
ctrl_buttons.pack_start(x_button)
ctrl_buttons.pack_start(y_button)
ctrl_buttons.pack_start(z_button)
top.pack_start(ctrl_buttons)



# Add a slice scale to browse the current slice stack

x_slice = Gtk::HScale.new(0,1,0.01)
y_slice = Gtk::HScale.new(0,1,0.01)
z_slice = Gtk::HScale.new(0,1,0.01)
x_slice.value = 0.5
y_slice.value = 0.5
z_slice.value = 0.5
@x_adjust = x_slice.adjustment
@y_adjust = y_slice.adjustment
@z_adjust = z_slice.adjustment
@x_adjust.signal_connect("value-changed"){|w| SetSlice(w,0) }
@y_adjust.signal_connect("value-changed"){|w| SetSlice(w,1) }
@z_adjust.signal_connect("value-changed"){|w| SetSlice(w,2) }
top.pack_start(x_slice)
top.pack_start(y_slice)
top.pack_start(z_slice)

# Done with the GUI. 
###


# Create an initial interesting view
cam1 = @ren.GetActiveCamera()
cam1.Elevation(-60)
cam1.SetViewUp(0, 0, 1)
cam1.Azimuth(30)
@ren.ResetCameraClippingRange()



# Start Tkinter event loop
root.show_all
Gtk.main
