/*
* 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, by EADS France
*/
package org.jcae.vtk;
import java.awt.Color;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.vecmath.Point3f;
import org.jcae.geometry.Transform3D;
import vtk.vtkActor;
import vtk.vtkExtractSelectedPolyDataIds;
import vtk.vtkIdTypeArray;
import vtk.vtkPainterPolyDataMapper;
import vtk.vtkPolyData;
import vtk.vtkPolyDataMapper;
import vtk.vtkProperty;
import vtk.vtkSelection;
import vtk.vtkSelectionNode;
/**
* TODO replace Color by javax.vecmath.Color3f
* @author Julian Ibarz
*/
public class LeafNode extends AbstractNode
{
private final static Logger LOGGER = Logger.getLogger(LeafNode.class.getName());
public static class DataProvider
{
private long modifiedTime = System.nanoTime();
private float[] nodesTransformed = new float[0];
protected float[] nodes = new float[0];
protected float[] normals;
protected int[] vertices = new int[0];
protected int[] lines = new int[0];
protected int[] polys = new int[0];
protected int nbrOfPolys;
protected int nbrOfLines;
protected int nbrOfVertices;
private Transform3D transform;
public static final DataProvider EMPTY = new DataProvider()
{
@Override
public void setVertices(int[] vertices)
{
throw new RuntimeException("DataProvider.EMPTY is immutable");
}
@Override
public void setLines(int[] lines)
{
throw new RuntimeException("DataProvider.EMPTY is immutable");
}
@Override
public void setNodes(float[] nodes)
{
throw new RuntimeException("DataProvider.EMPTY is immutable");
}
@Override
public void setPolys(int nbrOfPolys, int[] polys)
{
throw new RuntimeException("DataProvider.EMPTY is immutable");
}
};
public void setVertices(int[] vertices)
{
this.vertices = vertices;
nbrOfVertices = vertices.length / 2;
modified();
}
public void setLines(int[] lines)
{
this.lines = lines;
nbrOfLines = lines.length / 3;
modified();
}
public void setNodes(float[] nodes)
{
this.nodes = nodes;
makeTransform();
modified();
}
private void makeTransform()
{
if(transform != null)
{
this.nodesTransformed = new float[this.nodes.length];
Point3f point = new Point3f();
int j = 0;
for(int i = 0 ; i < this.nodes.length ; )
{
point.x = this.nodes[i++];
point.y = this.nodes[i++];
point.z = this.nodes[i++];
transform.transform(point);
nodesTransformed[j++] = point.x;
nodesTransformed[j++] = point.y;
nodesTransformed[j++] = point.z;
}
}
else
this.nodesTransformed = this.nodes;
}
public void setTransform(Transform3D transform)
{
this.transform = transform;
makeTransform();
}
public void setPolys(int nbrOfPolys, int[] polys)
{
this.nbrOfPolys = nbrOfPolys;
this.polys = polys;
modified();
}
public int getNbrOfPolys()
{
return nbrOfPolys;
}
public int getNbrOfLines()
{
return nbrOfLines;
}
public int getNbrOfVertices()
{
return nbrOfVertices;
}
public float[] getNodes()
{
return nodesTransformed;
}
public int[] getPolys()
{
return polys;
}
public int[] getLines()
{
return lines;
}
public int[] getVertices()
{
return vertices;
}
public float[] getNormals()
{
return normals;
}
public void load()
{
// Do nothing
}
public void unLoad()
{
// Do nothing
}
protected void clean()
{
nodes = new float[0];
nodesTransformed = new float[0];
normals = null;
vertices = new int[0];
lines = new int[0];
polys = new int[0];
nbrOfVertices = 0;
nbrOfLines = 0;
nbrOfPolys = 0;
modified();
}
protected void modified()
{
modifiedTime = System.nanoTime();
}
protected long getModifiedTime()
{
return modifiedTime;
}
}
private int [] selection = new int[0];
private Color color;
private DataProvider dataProvider;
public LeafNode(Node parent, DataProvider dataProvider, Color color)
{
super(parent);
parent.addChild(this);
this.dataProvider = dataProvider;
this.color = color;
}
@Override
public List<LeafNode> getLeaves()
{
return Collections.singletonList(this);
}
public Color getColor()
{
return color;
}
public void setColor(Color color)
{
if(this.color.equals(color))
return;
this.color = color;
if(actor != null)
Utils.vtkPropertySetColor(actor.GetProperty(), color);
else
timeStampModified();
}
public void setDataProvider(LeafNode.DataProvider data)
{
this.dataProvider = data;
// When data provider is modified, we must ensure that
// dataTime <= data.getModifiedTime(), and parent
// has also to be notified.
timeStampData();
dataTime = Long.MIN_VALUE;
}
public DataProvider getDataProvider()
{
return dataProvider;
}
public void setTransform(Transform3D transform)
{
dataProvider.setTransform(transform);
}
@Override
public void refresh()
{
if (!isManager())
{
lastUpdate = System.nanoTime();
return;
}
if (LOGGER.isLoggable(Level.FINER))
LOGGER.log(Level.FINER, "Refreshing leaf: "+this);
// Were data modified?
if (dataTime <= dataProvider.getModifiedTime())
refreshData();
// Was actor modified?
if (lastUpdate <= modificationTime)
refreshActor();
// Did selection happen?
if (lastUpdate <= selectionTime)
refreshHighlight();
lastUpdate = System.nanoTime();
}
private void refreshData()
{
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, "Refresh data for "+this);
dataProvider.load();
createData(dataProvider);
dataProvider.unLoad();
timeStampData();
if(mapper == null)
mapper = new vtkPainterPolyDataMapper();
getMapperCustomiser().customiseMapper(mapper);
mapper.SetInputDataObject(data);
mapper.Update();
}
// Must always be called after refreshData
private void refreshActor()
{
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, "Refresh actor for "+this);
boolean actorCreated = (actor == null);
if(actorCreated)
actor = createActor();
getActorCustomiser().customiseActor(actor);
actor.SetMapper(mapper);
actor.SetVisibility(Utils.booleanToInt(visible));
actor.SetPickable(Utils.booleanToInt(pickable));
if (actorCreated)
{
fireActorCreated(actor);
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, "New actor created: vtkActor@"+Integer.toHexString(actor.hashCode()));
}
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, "Attach color "+color+
" (opacity="+color.getAlpha()+") to actor @"+Integer.toHexString(actor.hashCode()));
Utils.vtkPropertySetColor(actor.GetProperty(), color);
}
private void refreshHighlight()
{
if (LOGGER.isLoggable(Level.FINEST))
LOGGER.log(Level.FINEST, "Refresh highlight for "+this);
if (selected)
{
// Highlight actor
getSelectionActorCustomiser().customiseActor(actor);
getSelectionMapperCustomiser().customiseMapper(mapper);
deleteSelectionActor();
}
else
{
// Reset original color
vtkProperty p = actor.GetProperty();
Utils.vtkPropertySetColor(p, color);
getActorCustomiser().customiseActor(actor);
getMapperCustomiser().customiseMapper(mapper);
refreshSelectionActor();
}
}
private void refreshSelectionActor()
{
if (selection.length == 0)
{
deleteSelectionActor();
return;
}
if (selectionActor == null)
{
selectionActor = new vtkActor();
selectionActor.PickableOff();
// fireActorCreated is called before creating its mapper
// to not take clipping planes into account
fireActorCreated(selectionActor);
}
getSelectionActorCustomiser().customiseActor(selectionActor);
if(selectionMapper == null)
selectionMapper = new vtkPainterPolyDataMapper();
selectionActor.SetMapper(selectionMapper);
getSelectionMapperCustomiser().customiseMapper(selectionMapper);
vtkSelection sel = new vtkSelection();
vtkSelectionNode selectionNode = new vtkSelectionNode();
//sel.ReleaseDataFlagOn();
// 4 MEANS INDICES (see the enumeration)
selectionNode.GetProperties().Set(selectionNode.CONTENT_TYPE(), 4);
// 0 MEANS CELLS
selectionNode.GetProperties().Set(selectionNode.FIELD_TYPE(), 0);
// list of cells to be selected
vtkIdTypeArray arr = Utils.setValues(selection);
selectionNode.SetSelectionList(arr);
sel.AddNode(selectionNode);
vtkExtractSelectedPolyDataIds selFilter = new vtkExtractSelectedPolyDataIds();
selFilter.ReleaseDataFlagOn();
selFilter.SetInputData(1, sel);
selFilter.SetInputData(0, data);
selectionMapper.SetInputConnection(selFilter.GetOutputPort());
}
@Override
void setCellSelection(PickContext pickContext, int [] cellSelection)
{
selection = new int[cellSelection.length];
System.arraycopy(cellSelection, 0, selection, 0, cellSelection.length);
pickContext.addToSelectedNodes(this);
timeStampSelected();
}
int [] getCellSelection()
{
return selection;
}
@Override
public void clearCellSelection()
{
selection = new int[0];
timeStampSelected();
}
public boolean hasCellSelection()
{
return selection.length != 0;
}
@Override
public void select()
{
selection = new int[0];
super.select();
}
}