# This example shows how to visualise the variation in shape in a set of objects using
# vtkPCAAnalysisFilter.
#
# We make three ellipsoids by distorting and translating a sphere and then align them together
# using vtkProcrustesAlignmentFilter, and then pass the output to vtkPCAAnalysisFilter. We visualise
# the first and second modes - the major sources of variation that were in the training set.

package require vtk
package require vtkinteraction

vtkSphereSource sphere
  sphere SetPhiResolution 36
  sphere SetThetaResolution 36
  sphere Update

# make two copies of the shape and distort them a little

vtkTransform transform1
    transform1 Translate 0.2 0.1 0.3
    transform1 Scale 1.3 1.1 0.8

vtkTransform transform2
    transform2 Translate 0.3 0.7 0.1
    transform2 Scale 1.0 0.1 1.8

vtkTransformPolyDataFilter transformer1
    transformer1 SetInputConnection [sphere GetOutputPort]
    transformer1 SetTransform transform1
    transformer1 Update

vtkTransformPolyDataFilter transformer2
    transformer2 SetInputConnection [sphere GetOutputPort]
    transformer2 SetTransform transform2
    transformer2 Update

#------------------------------------------------------------------
# map these three shapes into the first renderer
#------------------------------------------------------------------
vtkPolyDataMapper map1a
    map1a SetInputConnection [sphere GetOutputPort]
vtkActor Actor1a
    Actor1a SetMapper map1a
    [Actor1a GetProperty] SetDiffuseColor 1.0000 0.3882 0.2784

vtkPolyDataMapper map1b
    map1b SetInputConnection [transformer1 GetOutputPort]
vtkActor Actor1b
    Actor1b SetMapper map1b
    [Actor1b GetProperty] SetDiffuseColor 0.3882 1.0000 0.2784

vtkPolyDataMapper map1c
    map1c SetInputConnection [transformer2 GetOutputPort]
vtkActor Actor1c
    Actor1c SetMapper map1c
    [Actor1c GetProperty] SetDiffuseColor 0.3882 0.2784 1.0000

#------------------------------------------------------------------
# align the shapes using Procrustes (using SetModeToRigidBody)
# and map the aligned shapes into the second renderer
#------------------------------------------------------------------
vtkMultiBlockDataGroupFilter group
    group AddInputConnection [sphere GetOutputPort]
    group AddInputConnection [transformer1 GetOutputPort]
    group AddInputConnection [transformer2 GetOutputPort]

vtkProcrustesAlignmentFilter procrustes
    procrustes SetInputConnection [group GetOutputPort]
    [procrustes GetLandmarkTransform] SetModeToRigidBody
    procrustes Update

vtkPolyDataMapper map2a
    map2a SetInputData [[procrustes GetOutput] GetBlock 0]
vtkActor Actor2a
    Actor2a SetMapper map2a
    [Actor2a GetProperty] SetDiffuseColor 1.0000 0.3882 0.2784

vtkPolyDataMapper map2b
    map2b SetInputData [[procrustes GetOutput] GetBlock 1]
vtkActor Actor2b
    Actor2b SetMapper map2b
    [Actor2b GetProperty] SetDiffuseColor 0.3882 1.0000 0.2784

vtkPolyDataMapper map2c
    map2c SetInputData [[procrustes GetOutput] GetBlock 2]
vtkActor Actor2c
    Actor2c SetMapper map2c
    [Actor2c GetProperty] SetDiffuseColor 0.3882 0.2784 1.0000

#------------------------------------------------------------------
# pass the output of Procrustes to vtkPCAAnalysisFilter
#------------------------------------------------------------------
vtkPCAAnalysisFilter pca
    pca SetInputConnection [procrustes GetOutputPort]

pca Update
# we need to call Update because GetParameterisedShape is not
# part of the normal SetInput/GetOutput pipeline

#------------------------------------------------------------------
# map the first mode into the third renderer:
# -3,0,3 standard deviations on the first mode
# illustrate the extremes around the average shape
#------------------------------------------------------------------
vtkFloatArray params
    params SetNumberOfComponents 1
    params SetNumberOfTuples 1
    params SetTuple1 0 0.0
vtkPolyData shapea
  shapea DeepCopy [sphere GetOutput]
pca GetParameterisedShape params shapea
vtkPolyDataNormals normalsa
  normalsa SetInputData shapea
vtkPolyDataMapper map3a
    map3a SetInputConnection [normalsa GetOutputPort]
vtkActor Actor3a
    Actor3a SetMapper map3a
    [Actor3a GetProperty] SetDiffuseColor 1 1 1

   params SetTuple1 0 -3.0
vtkPolyData shapeb
  shapeb DeepCopy [sphere GetOutput]
pca GetParameterisedShape params shapeb
vtkPolyDataNormals normalsb
  normalsb SetInputData shapeb
vtkPolyDataMapper map3b
    map3b SetInputConnection [normalsb GetOutputPort]
vtkActor Actor3b
    Actor3b SetMapper map3b
    [Actor3b GetProperty] SetDiffuseColor 1 1 1

   params SetTuple1 0 3.0
vtkPolyData shapec
  shapec DeepCopy [sphere GetOutput]
pca GetParameterisedShape params shapec
vtkPolyDataNormals normalsc
  normalsc SetInputData shapec
vtkPolyDataMapper map3c
    map3c SetInputConnection [normalsc GetOutputPort]
vtkActor Actor3c
    Actor3c SetMapper map3c
    [Actor3c GetProperty] SetDiffuseColor 1 1 1

#------------------------------------------------------------------
# map the second mode into the fourth renderer:
#------------------------------------------------------------------
vtkFloatArray params4
    params4 SetNumberOfComponents 1
    params4 SetNumberOfTuples 2
    params4 SetTuple1 0 0.0
    params4 SetTuple1 1 -3.0
vtkPolyData shape4a
  shape4a DeepCopy [sphere GetOutput]
pca GetParameterisedShape params4 shape4a
vtkPolyDataNormals normals4a
  normals4a SetInputData shape4a
vtkPolyDataMapper map4a
    map4a SetInputConnection [normals4a GetOutputPort]
vtkActor Actor4a
    Actor4a SetMapper map4a
    [Actor4a GetProperty] SetDiffuseColor 1 1 1

   params4 SetTuple1 1 0.0
vtkPolyData shape4b
  shape4b DeepCopy [sphere GetOutput]
pca GetParameterisedShape params4 shape4b
vtkPolyDataNormals normals4b
  normals4b SetInputData shape4b
vtkPolyDataMapper map4b
    map4b SetInputConnection [normals4b GetOutputPort]
vtkActor Actor4b
    Actor4b SetMapper map4b
    [Actor4b GetProperty] SetDiffuseColor 1 1 1

   params4 SetTuple1 1 3.0
vtkPolyData shape4c
  shape4c DeepCopy [sphere GetOutput]
pca GetParameterisedShape params4 shape4c
vtkPolyDataNormals normals4c
  normals4c SetInputData shape4c
vtkPolyDataMapper map4c
    map4c SetInputConnection [normals4c GetOutputPort]
vtkActor Actor4c
    Actor4c SetMapper map4c
    [Actor4c GetProperty] SetDiffuseColor 1 1 1

#------------------------------------------------------------------
# Create the RenderWindow and its four Renderers
#------------------------------------------------------------------

vtkRenderer ren1
vtkRenderer ren2
vtkRenderer ren3
vtkRenderer ren4
vtkRenderWindow renWin
    renWin AddRenderer ren1
    renWin AddRenderer ren2
    renWin AddRenderer ren3
    renWin AddRenderer ren4
    renWin SetSize 600 200
vtkRenderWindowInteractor iren
    iren SetRenderWindow renWin

# Add the actors to the renderer

ren1 AddActor Actor1a
ren1 AddActor Actor1b
ren1 AddActor Actor1c

ren2 AddActor Actor2a
ren2 AddActor Actor2b
ren2 AddActor Actor2c

ren3 AddActor Actor3a
ren3 AddActor Actor3b
ren3 AddActor Actor3c

ren4 AddActor Actor4a
ren4 AddActor Actor4b
ren4 AddActor Actor4c

# set the properties of the renderers

ren1 SetBackground 1 1 1
ren1 SetViewport 0.0 0.0 0.25 1.0
ren1 ResetCamera
[ren1 GetActiveCamera] SetPosition 1 -1 0
ren1 ResetCamera

ren2 SetBackground 1 1 1
ren2 SetViewport 0.25 0.0 0.5 1.0
ren2 ResetCamera
[ren2 GetActiveCamera] SetPosition 1 -1 0
ren2 ResetCamera

ren3 SetBackground 1 1 1
ren3 SetViewport 0.5 0.0 0.75 1.0
ren3 ResetCamera
[ren3 GetActiveCamera] SetPosition 1 -1 0
ren3 ResetCamera

ren4 SetBackground 1 1 1
ren4 SetViewport 0.75 0.0 1.0 1.0
ren4 ResetCamera
[ren4 GetActiveCamera] SetPosition 1 -1 0
ren4 ResetCamera

# render the image
#
iren AddObserver UserEvent {wm deiconify .vtkInteract}

renWin Render

# output the image to file (used to generate the initial regression image)
#vtkWindowToImageFilter to_image
#to_image SetInput renWin
#vtkPNGWriter to_png
#to_png SetFileName "TestPCA.png"
#to_png SetInputConnection [to_image GetOutputPort]
#to_png Write

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