Commit 3458c2aa authored by Amandine  TOURNAY's avatar Amandine TOURNAY
Browse files

Initial commit

parents
.idea/
.settings/
build/
target/
*.iml
.classpath
.project
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.bioimageanalysis.icy</groupId>
<artifactId>icy-3d-mesh-roi</artifactId>
<version>1.4.7</version>
<name>3D Mesh ROI</name>
<description>
SDK for 3D ROI creation and manipulation using polygonal (surface) and polyhedral (volume) meshes.
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<outputJar>${project.build.outputDirectory}/../plugin</outputJar>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<outputDirectory>${outputJar}</outputDirectory>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>plugins.adufour.roi.mesh.ROI3DMeshPlugin</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bioimageanalysis.icy</groupId>
<artifactId>icy-kernel</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.bioimageanalysis.icy</groupId>
<artifactId>icy-quickhull</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.bioimageanalysis.icy</groupId>
<artifactId>icy-vecmath</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>icy</id>
<url>https://icy-nexus.pasteur.fr/repository/Icy/</url>
</repository>
</repositories>
<distributionManagement>
<snapshotRepository>
<id>icy-dev</id>
<name>icy-dev</name>
<url>https://icy-nexus-dev.pasteur.cloud/repository/icy-core/</url>
</snapshotRepository>
<repository>
<id>icy-prod</id>
<name>icy-prod</name>
<url>https://icy-nexus.pasteur.fr/repository/icy-core/</url>
</repository>
</distributionManagement>
</project>
\ No newline at end of file
package plugins.adufour.roi.mesh;
import plugins.adufour.roi.mesh.polygon.Polygon3D;
import plugins.adufour.roi.mesh.polyhedron.Polyhedron3D;
/**
* Structural element of a generic {@link ROI3DMesh 3D mesh}.
*
* @see Polygon3D
* @see Polyhedron3D
* @author Alexandre Dufour
*/
public abstract class Cell3D
{
/**
* The indices of the vertex forming this cell, in arbitrary order (it is up to the overriding
* classes to define how vertices should be ordered)
*/
public final int[] vertexIndices;
/**
* The number of vertices in this face (point: 1, edge: 2, triangle: 3, quad: 4, etc.)
*/
public final int size;
/**
* Creates a new mesh cell from the specified vertex indices
*
* @param vertexIndices
*/
protected Cell3D(int... vertexIndices)
{
this.vertexIndices = new int[vertexIndices.length];
System.arraycopy(vertexIndices, 0, this.vertexIndices, 0, vertexIndices.length);
size = vertexIndices.length;
}
/**
* Creates a copy of this cell.
*/
public abstract Cell3D clone();
/**
* Indicates whether the specified vertex index belongs to this face
*
* @param index
* the vertex index to look for
* @return <code>true</code> if the index is present in this face, <code>false</code> otherwise
*/
public boolean contains(int index)
{
for (int i : vertexIndices)
if (i == index)
return true;
return false;
}
/**
* @param vertexIndex
* @return a zero-based index where the specified vertex index has been found, or
* <code>-1</code> if the specified vertex index was not found
*/
public int indexOf(int vertexIndex)
{
for (int i = 0; i < size; i++)
if (vertexIndices[i] == vertexIndex)
return i;
return -1;
}
/**
* Replaces a vertex index by another (or simply returns if the index to replace is not found in
* this cell)
*
* @param oldIndex
* the index of the old vertex
* @param newIndex
* the index of the new vertex
*/
public void replace(int oldIndex, int newIndex)
{
int position = indexOf(oldIndex);
if (position >= 0)
vertexIndices[position] = newIndex;
}
/**
* Replaces the vertex indices with the specified indexes
*
* @param vertexIndices
* @throws IllegalArgumentException
* if the size of the specified list is different from the size of this cell
*/
public void setIndices(int... vertexIndices) throws IllegalArgumentException
{
if (vertexIndices.length != size)
throw new IllegalArgumentException("Invalid cell size: " + vertexIndices.length);
System.arraycopy(vertexIndices, 0, this.vertexIndices, 0, size);
}
}
package plugins.adufour.roi.mesh;
import plugins.adufour.roi.mesh.polygon.ROI3DTriangularMesh;
/**
* Exception that occurs when the topology of a {@link ROI3DMesh} is inconsistent. A typical example
* is when a {@link ROI3DTriangularMesh} is splitting as a result of a resampling operation, giving
* rise to two new meshes.
*
* @see ROI3DTriangularMesh#reSampleToAverageDistance(double, double)
* @author Alexandre Dufour
*/
public class MeshTopologyException extends Exception
{
private static final long serialVersionUID = 1L;
public final ROI3DMesh<?> source;
public final ROI3DMesh<?>[] children;
/**
* Creates a new Topology exception for the specified contour
*
* @param contour
* the contour undergoing a topology break
* @param children
* an array containing zero or more contours that should replace the contour raising
* the exception (typically when a mesh vanishes or splits as a result of a
* resampling operation)
*/
public <C extends Cell3D> MeshTopologyException(ROI3DMesh<C> contour, ROI3DMesh<C>[] children)
{
super("Topology break detected in contour " + contour.hashCode());
this.source = contour;
this.children = children;
}
}
package plugins.adufour.roi.mesh;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Matrix3d;
import javax.vecmath.Point3d;
import javax.vecmath.Tuple3d;
import javax.vecmath.Vector3d;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import icy.canvas.IcyCanvas;
import icy.painter.VtkPainter;
import icy.roi.BooleanMask2D;
import icy.roi.BooleanMask3D;
import icy.roi.ROI3D;
import icy.roi.ROIUtil;
import icy.sequence.Sequence;
import icy.type.point.Point5D;
import icy.type.rectangle.Rectangle3D;
import icy.util.XMLUtil;
import icy.vtk.IcyVtkPanel;
import icy.vtk.VtkUtil;
import plugins.adufour.roi.mesh.polygon.Polygon3D;
import plugins.adufour.roi.mesh.polygon.ROI3DPolygonalMesh;
import plugins.adufour.roi.mesh.polyhedron.ROI3DPolyhedralMesh;
import plugins.kernel.canvas.VtkCanvas;
import plugins.kernel.roi.roi2d.ROI2DArea;
import plugins.kernel.roi.roi2d.ROI2DArea.ROI2DAreaPainter;
import plugins.kernel.roi.roi3d.ROI3DArea;
import vtk.CellType;
import vtk.vtkActor;
import vtk.vtkCell;
import vtk.vtkCellArray;
import vtk.vtkMapper;
import vtk.vtkOutlineFilter;
import vtk.vtkPointSet;
import vtk.vtkPoints;
import vtk.vtkPolyDataMapper;
import vtk.vtkProp;
import vtk.vtkUnsignedCharArray;
import vtk.vtkXMLDataSetWriter;
/**
* Generic class defining a 3D region of interest using a discrete 3D geometry representation. This
* library provides the two major types of mesh representation:
* <ul>
* <li>Polygonal meshes (defining the region via its surface, see {@link ROI3DPolygonalMesh})</li>
* <li>Polyhederal meshes (defining the region by its interior, see {@link ROI3DPolyhedralMesh})
* </li>
* </ul>
* Be aware than ROI in Icy <b>should not</b> consider pixel size (except for 3D rendering part) so it's important than 3D ROI are always expressed in
* <b>voxel</b>.<br>
* The VTK library provided with Icy already contains all the necessary tools to manipulate and
* visualize 3D data sets, yet its low-levelness can be complex for beginners, while potential bugs
* caused in the C++ back-end may cause the entire application to crash instead of generating
* elegant bug reports. The main benefits of this library are listed below: <br/>
* <br/>
* <ul>
* <li>Intermediate Java API, much simpler to use than the VTK back-end</li>
* <li>Native integration with Icy's Sequence / Viewer / ROI system</li>
* <li>Eliminates most of the boiler-plate to simplify user code as much as possible</li>
* </ul>
*
* @author Alexandre Dufour
*/
public abstract class ROI3DMesh<C extends Cell3D> extends ROI3D
{
// single lock for VTK access
protected final static Object vtkInternalLock = new Object();
public static Point3d vecMul(Point3d source, Tuple3d scale, Point3d dest)
{
final Point3d result = (dest == null) ? new Point3d() : dest;
if (scale == null)
result.set(source);
else
result.set(source.x * scale.x, source.y * scale.y, source.z * scale.z);
return result;
}
// /**
// * Returns a native java array from the specified {@link vtkDataArray}.
// */
// public static Object getJavaArray(vtkDataArray dataArray)
// {
// switch (dataArray.GetDataType())
// {
// case VtkUtil.VTK_UNSIGNED_CHAR:
// return ((vtkUnsignedCharArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_SHORT:
// return ((vtkShortArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_UNSIGNED_SHORT:
// return ((vtkUnsignedShortArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_INT:
// return ((vtkIntArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_UNSIGNED_INT:
// return ((vtkUnsignedIntArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_LONG:
// return ((vtkLongArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_UNSIGNED_LONG:
// return ((vtkUnsignedLongArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_FLOAT:
// return ((vtkFloatArray) dataArray).GetJavaArray();
// case VtkUtil.VTK_DOUBLE:
// return ((vtkDoubleArray) dataArray).GetJavaArray();
// default:
// return null;
// }
// }
// /**
// * Returns a {@link BooleanMask3D} from a binary (0/1 values) {@link vtkImageData}.
// */
// public static BooleanMask3D getBooleanMaskFromBinaryImage(vtkImageData image)
// {
// final vtkDataArray data = image.GetPointData().GetScalars();
// final double[] origin = image.GetOrigin();
// final int[] dim = image.GetDimensions();
// final int sizeX = dim[0];
// final int sizeY = dim[1];
// final int sizeZ = dim[2];
// final int sizeXY = sizeX * sizeY;
// final Rectangle bounds2D = new Rectangle((int) origin[0], (int) origin[1], sizeX, sizeY);
// final BooleanMask2D[] masks = new BooleanMask2D[sizeZ];
//
// // more than 200M ? use simple iterator (slower but consume less memory)
// if ((sizeXY * sizeZ) > (200 * 1024 * 1024))
// {
// int off = 0;
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < sizeXY; xy++)
// mask[xy] = (data.GetTuple1(off++) != 0d);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// }
// else
// {
// final Object javaArray = getJavaArray(data);
// int off = 0;
//
// switch (ArrayUtil.getDataType(javaArray))
// {
// case BYTE:
// final byte[] javaByteArray = (byte[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaByteArray[off++] != 0);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// case SHORT:
// final short[] javaShortArray = (short[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaShortArray[off++] != 0);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// case INT:
// final int[] javaIntArray = (int[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaIntArray[off++] != 0);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// case LONG:
// final long[] javaLongArray = (long[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaLongArray[off++] != 0L);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// case FLOAT:
// final float[] javaFloatArray = (float[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaFloatArray[off++] != 0f);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// case DOUBLE:
// final double[] javaDoubleArray = (double[]) javaArray;
//
// for (int z = 0; z < sizeZ; z++)
// {
// final boolean[] mask = new boolean[sizeXY];
//
// for (int xy = 0; xy < mask.length; xy++)
// mask[xy] = (javaDoubleArray[off++] != 0d);
//
// masks[z] = new BooleanMask2D(new Rectangle(bounds2D), mask);
// }
// break;
//
// default:
// // nothing to do here
// break;
// }
// }
//
// // don't optimize bounds (we prefer plain image size for the mask here)
// return new BooleanMask3D(new Rectangle3D.Integer(bounds2D.x, bounds2D.y, (int) origin[2], sizeX, sizeY, sizeZ),
// masks);
// }
public abstract class MeshPainter extends ROIPainter implements VtkPainter
{
protected boolean showBoundingBoxIn2D;
/**
* The size of a pixel in real units.<br>
* USed to detect change in pixel size for VTK correct aspect rendering.
*/
protected final Tuple3d pixelSize;
protected final vtkActor mainActor;
protected final vtkActor outlineActor;
protected final vtkMapper mapper;
/**
* Internal VTK mesh pixel size scaled for rendering
*/
protected final vtkPointSet vtkRenderingMesh;
/**
* Filter used to create the bounding box of the ROI (shown when the ROI is selected)
*/
protected final vtkOutlineFilter vtkOutlineFilter;
/**
* Custom coloring
*/
protected vtkUnsignedCharArray vtkColors;
/**
* <ul>
* <li><code>true</code> mesh vertices are colored according to the {@link #updateVTKColor()}
* method</li>
* <li><code>false</code> mesh vertices are colored uniformly (at the VTK actor level) using the
* ROI color</li>
* </ul>
*/
protected boolean vtkCustomColoring;
protected boolean needRebuild;
protected boolean needColorUpdate;
protected WeakReference<VtkCanvas> canvas3d;
public MeshPainter()
{
super();
pixelSize = new Point3d();
showBoundingBoxIn2D = false;
mainActor = new vtkActor();
outlineActor = new vtkActor();
vtkColors = new vtkUnsignedCharArray();
vtkColors.SetNumberOfComponents(3);
// cells used for rendering (scaled using pixel size)
vtkRenderingMesh = createVTKMesh();
vtkOutlineFilter = new vtkOutlineFilter();
vtkOutlineFilter.SetInputData(vtkRenderingMesh);
vtkCustomColoring = false;
// create a new mapper for the mesh data
mapper = createVTKMapper();
mapper.SetInputDataObject(vtkRenderingMesh);
mainActor.SetMapper(mapper);
mainActor.PickableOn();
// outline (shown when the ROI is selected)
final vtkPolyDataMapper outlineMapper = new vtkPolyDataMapper();
outlineMapper.SetInputConnection(vtkOutlineFilter.GetOutputPort());
outlineActor.SetMapper(outlineMapper);
outlineActor.PickableOff();
outlineActor.VisibilityOff();
needRebuild = true;
needColorUpdate = true;
canvas3d = new WeakReference<VtkCanvas>(null);
}
@Override
protected void finalize() throws Throwable
{
super.finalize();
// release allocated VTK resources
if (mainActor != null)
mainActor.Delete();
// release allocated VTK resources
if (mapper != null)
mapper.Delete();
if (vtkRenderingMesh != null)
{
vtkRenderingMesh.GetPointData().GetScalars().Delete();
vtkRenderingMesh.GetPointData().Delete();
vtkRenderingMesh.Delete();
}
if (outlineActor != null)
{
if (outlineActor.GetMapper() != null)
outlineActor.GetMapper().Delete();
outlineActor.SetPropertyKeys(null);
outlineActor.Delete();
}
if (vtkOutlineFilter != null)
vtkOutlineFilter.Delete();
if (vtkColors != null)
vtkColors.Delete();
};
public IcyVtkPanel getVtkPanel()
{
final VtkCanvas canvas = canvas3d.get();
// canvas not yet open or closed
if (canvas