/*
* Project Info: http://jcae.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* (C) Copyright 2008-2009, by EADS France
*/
package org.jcae.vtk;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import vtk.vtkActor;
import vtk.vtkActorCollection;
import vtk.vtkAxesActor;
import vtk.vtkCamera;
import vtk.vtkCellPicker;
import vtk.vtkOrientationMarkerWidget;
import vtk.vtkRenderer;
import vtk.vtkTransform;
/**
* TODO : Integrate it directly in RubberBandHelper ?
* @author ibarz
*/
public class CameraManager
{
private static class ScreenshotCamera
{
private ImageIcon snapshot;
private final static int height = 100;
private final static int width = 100;
public ImageIcon getSnapshot()
{
return snapshot;
}
public void shot(BufferedImage aSnapshot)
{
int h = aSnapshot.getHeight();
int w = aSnapshot.getWidth();
BufferedImage buffer = new BufferedImage(
width, height, BufferedImage.TYPE_INT_RGB);
double scaleH = ((double) height) / h;
double scaleW = ((double) width) / w;
double scale = Math.min(scaleH, scaleW);
//scale transform to get always the same image size
AffineTransform s = new AffineTransform();
s.scale(scale, scale);
//translate transform to center the image
AffineTransform t = new AffineTransform();
t.setToTranslation((width - scale * w) / 2.0, (height - scale * h) / 2.0);
t.concatenate(s);
Graphics2D g2D = buffer.createGraphics();
g2D.drawRenderedImage(aSnapshot, t);
snapshot = new ImageIcon(buffer);
}
}
// Different classic cameras
private static final vtkCamera[] defaultCameras =
new vtkCamera[Orientation.values().length];
private final Canvas canvas;
private final vtkRenderer renderer;
private final ArrayList<vtkCamera> cameras = new ArrayList<vtkCamera>();
private final ArrayList<ScreenshotCamera> screenshots =
new ArrayList<ScreenshotCamera>();
private final vtkAxesActor originAxes;
private boolean isVisibleRelativeAxis = true;
private final vtkAxesActor relativeAxes;
private final vtkOrientationMarkerWidget marker;
private double originAxesFactor = 0.2;
public enum Orientation
{
TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK
}
public void setAxisLabelColor(Color c)
{
double r = c.getRed() / 255.0;
double g = c.getGreen() / 255.0;
double b = c.getBlue() / 255.0;
relativeAxes.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
originAxes.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
relativeAxes.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
originAxes.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
relativeAxes.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
originAxes.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(
r, g, b);
}
public CameraManager(Canvas canvas)
{
this.canvas = canvas;
renderer = canvas.GetRenderer();
originAxes = new vtkAxesActor();
originAxes.AxisLabelsOff();
renderer.AddActor(originAxes);
// A display orientation
relativeAxes = new vtkAxesActor();
relativeAxes.AxisLabelsOn();
setAxisLabelColor(Color.getColor("org.jcae.vtk.color.label", Color.WHITE));
// FIXME: I do not know why, but originAxes has 6
// pickable actors whereas relativeAxes has none.
// As originAxes.PickableOff() does nothing, try
// another way.
vtkActorCollection actors = new vtkActorCollection();
originAxes.GetActors(actors);
actors.InitTraversal();
for (vtkActor prop; (prop = actors.GetNextActor()) != null; )
{
prop.PickableOff();
}
actors = null;
marker = new vtkOrientationMarkerWidget();
marker.SetOrientationMarker(relativeAxes);
marker.SetViewport(0., 0., .3, .3);
marker.SetInteractor(canvas.getIren());
marker.EnabledOn();
marker.InteractiveOff();
// We connect the "RenderEvent" event to the renderEvent method of this
canvas.getIren().AddObserver("RenderEvent", this, "renderEvent");
/**
* The camera orientation
* camera[i*2] is the vector position of camera i
* camera[i*2+1] is the vector up of camera i
*
*/
double[][] cameraOrientation = {
// TOP
{0., 0., 1.},
{0., 1., 0.},
// BOTTOM
{0., 0., -1.},
{0., 1., 0.},
// LEFT
{0., -1., 0.},
{0., 0., 1.},
// RIGHT
{0., 1., 0.},
{0., 0., 1.},
// FRONT
{-1., 0., 0.},
{0., 0., 1.},
// BACK
{1., 0., 0.},
{0., 0., 1.}};
for (int i = 0; i < Orientation.values().length; ++i)
{
defaultCameras[i] = new vtkCamera();
defaultCameras[i].SetPosition(cameraOrientation[i * 2]);
defaultCameras[i].SetViewUp(cameraOrientation[i * 2 + 1]);
}
}
/** This method is called by the VTK JNI code. Do not remove. */
private void renderEvent()
{
scaleOriginaAxis();
}
/** scale the origin axis to make his screen size constant */
private void scaleOriginaAxis()
{
// Find the distance from the camera of the origin axes
vtkCamera camera = renderer.GetActiveCamera();
double zDistance;
if(camera.GetParallelProjection()!=0)
zDistance = camera.GetParallelScale()/2;
else
{
vtkTransform modelView = camera.GetViewTransformObject();
double[] point = modelView.TransformDoublePoint(0, 0, 0);
zDistance = Math.abs(point[2]) * originAxesFactor;
}
originAxes.SetTotalLength(zDistance, zDistance, zDistance);
}
public void setRelativeAxisVisible(boolean visibility)
{
this.isVisibleRelativeAxis = visibility;
canvas.lock();
marker.SetEnabled(Utils.booleanToInt(visibility));
relativeAxes.SetVisibility(Utils.booleanToInt(visibility));
canvas.RenderSecured();
canvas.unlock();
}
public void setRotationCenter(double x, double y, double z)
{
double[] position = new double[]{x,y,z};
canvas.lock();
canvas.GetRenderer().GetActiveCamera().SetFocalPoint(position);
canvas.RenderSecured();
canvas.unlock();
}
public void zoomTo(float x, float y, float z, float r)
{
double rr = r / Math.sqrt(3);
canvas.lock();
canvas.GetRenderer().ResetCamera(x-rr, x+rr, y-rr, y+rr, z-rr, z+rr);
canvas.Render();
canvas.unlock();
}
public void setOriginAxisVisible(boolean visibility)
{
int iVisibility = (visibility) ? 1 : 0;
originAxes.SetVisibility(iVisibility);
canvas.RenderSecured();
}
public boolean isOriginAxisVisible()
{
return (originAxes.GetVisibility() == 0) ? false : true;
}
public boolean isRelativeAxisVisible()
{
return (relativeAxes.GetVisibility() == 0) ? false : true;
}
/**
* There is no function in VTK to copy a camera so created our proper copy of camera.
* WARNING : this function is not complete and function only if we manipulate the parameters that the function copy.
* @param c
* @return
*/
vtkCamera copy(vtkCamera c)
{
vtkCamera rep = new vtkCamera();
rep.SetPosition(c.GetPosition());
rep.SetFocalPoint(c.GetFocalPoint());
rep.SetViewUp(c.GetViewUp());
return rep;
}
/**
* Set the factor of the size of the origin axes. When the axes is very far
* it is scaled to the originAxesFactor where originAxesFactor define the size of
* the actor in function of the size of the viewport. A value of 1 will make
* the axes as large as the size of the viewport. By default this value is set to 1/15
* @param originAxesSizeFactor
*/
public void setOriginAxesSizeFactor(double originAxesSizeFactor)
{
this.originAxesFactor = originAxesSizeFactor;
}
private void refresh()
{
canvas.lock();
renderer.ResetCamera();
canvas.unlock();
//RenderSecured with the new position of the camera
canvas.RenderSecured();
}
/**
* Save the current camera
*/
public void saveCurrentCamera()
{
try {
cameras.add(copy(renderer.GetActiveCamera()));
ScreenshotCamera screenshot = new ScreenshotCamera();
screenshot.shot(Utils.takeScreenshot(canvas));
screenshots.add(screenshot);
} catch (IOException ex) {
Logger.getLogger(CameraManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ImageIcon getScreenshotCamera(int index)
{
return screenshots.get(index).getSnapshot();
}
public void removeCamera(int index)
{
cameras.remove(index);
screenshots.remove(index);
}
public int getNumberOfCameras()
{
return cameras.size();
}
public void setCameraOrientation(CameraManager.Orientation orientation)
{
canvas.lock();
vtkCamera current = renderer.GetActiveCamera();
int parallel = current.GetParallelProjection();
vtkCamera c = copy(defaultCameras[orientation.ordinal()]);
c.SetParallelProjection(parallel);
renderer.SetActiveCamera(c);
canvas.unlock();
refresh();
}
public void fitAll()
{
canvas.lock();
int visibility = originAxes.GetVisibility();
originAxes.SetVisibility(0);
double[] bounds = canvas.GetRenderer().ComputeVisiblePropBounds();
if(visibility != 0)
for(int i = 0; i<3; i++)
{
if(bounds[i*2]>0)
bounds[i*2] = 0;
if(bounds[i*2+1]<0)
bounds[i*2+1] = 0;
}
canvas.GetRenderer().ResetCamera(bounds);
originAxes.SetVisibility(visibility);
scaleOriginaAxis();
canvas.GetRenderer().ResetCameraClippingRange();
canvas.RenderSecured();
canvas.unlock();
}
/**
* Set the position rotation of the camera to the center of the bouding box
* of the cell selected
*
* @param pickPosition
*/
public void centerRotationSelection(Point pickPosition)
{
//backup actors pickable status and set it to true
vtkActorCollection vtkActors = canvas.GetRenderer().GetActors();
vtkActors.InitTraversal();
int n = vtkActors.GetNumberOfItems();
vtkActor[] actors = new vtkActor[n];
int[] pickableBackup = new int[n];
int k = 0;
while(k < n)
{
actors[k] = vtkActors.GetNextActor();
pickableBackup[k] = actors[k].GetPickable();
actors[k].SetPickable(1);
k++;
}
vtkCellPicker picker = new vtkCellPicker();
canvas.lock();
picker.Pick(pickPosition.getX(), pickPosition.getY(), 0., canvas.GetRenderer());
double[] position = picker.GetPickPosition();
canvas.GetRenderer().GetActiveCamera().SetFocalPoint(position);
canvas.getIren().GetInteractorStyle();
canvas.RenderSecured();
canvas.unlock();
//restore actor pickable status
for(int i = 0; i<n; i++)
actors[i].SetPickable(pickableBackup[i]);
}
public Canvas getCanvas()
{
return canvas;
}
public void setCamera(int index)
{
canvas.lock();
renderer.SetActiveCamera(copy(cameras.get(index)));
canvas.unlock();
refresh();
}
public boolean isParallelProjection()
{
vtkCamera c = renderer.GetActiveCamera();
boolean toReturn = c.GetParallelProjection() != 0;
return toReturn;
}
public void setParallelProjection(boolean b)
{
vtkCamera c = renderer.GetActiveCamera();
c.SetParallelProjection(b ? 1 : 0);
}
}