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

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;
}
}
This diff is collapsed.
package plugins.adufour.roi.mesh;
import icy.plugin.abstract_.Plugin;
import icy.plugin.interface_.PluginLibrary;
/**
* Marks the entire package as an Icy plugin
*
* @author Alexandre Dufour
*/
public class ROI3DMeshPlugin extends Plugin implements PluginLibrary
{
}
package plugins.adufour.roi.mesh;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.swing.filechooser.FileFilter;
import icy.common.exception.UnsupportedFormatException;
import icy.file.FileUtil;
import icy.gui.frame.progress.FileFrame;
import icy.gui.viewer.Viewer;
import icy.image.IcyBufferedImage;
import icy.plugin.abstract_.PluginFileImporter;
import icy.sequence.Sequence;
import icy.system.thread.ThreadUtil;
import icy.type.DataType;
import icy.type.rectangle.Rectangle3D;
import plugins.adufour.roi.mesh.polygon.ROI3DPolygonalMesh;
import plugins.kernel.canvas.VtkCanvasPlugin;
public class VTKMeshImporter extends PluginFileImporter
{
@Override
public boolean acceptFile(String path)
{
if (FileUtil.isDirectory(path)) return false;
return path.endsWith("vtk") || path.endsWith("vtk.txt");
}
@Override
public List<FileFilter> getFileFilters()
{
FileFilter vtkFilter = new FileFilter()
{
@Override
public String getDescription()
{
return "VTK mesh files";
}
@Override
public boolean accept(File f)
{
return f.isDirectory() || acceptFile(f.getPath());
}
};
return Arrays.asList(vtkFilter);
}
@Override
public boolean load(String path, FileFrame loadingFrame) throws UnsupportedFormatException, IOException
{
Sequence sequence = getActiveSequence();
boolean newSequence = false;
if (sequence == null)
{
// Create a fake Sequence
sequence = new Sequence(FileUtil.getFileName(path, false));
addSequence(sequence);
newSequence = true;
}
if (loadingFrame != null) loadingFrame.setTitle("Importing " + FileUtil.getFileName(path) + "...");
final ROI3DMesh<?> mesh = new ROI3DPolygonalMesh();
// mesh.setPixelSize(new Point3d(sequence.getPixelSizeX(), sequence.getPixelSizeY(), sequence.getPixelSizeZ()));
mesh.loadFromVTK(new File(path));
if (newSequence)
{
Rectangle3D.Integer bounds = mesh.getBounds();
// Add a fake image
sequence.addImage(new IcyBufferedImage(bounds.sizeX, bounds.sizeY, 1, DataType.UBYTE));
final Sequence theSequence = sequence;
ThreadUtil.invokeLater(new Runnable()
{
@Override
public void run()
{
Viewer viewer = theSequence.getFirstViewer();
if (viewer != null) viewer.setCanvas(VtkCanvasPlugin.class.getName());
}
}, true);
}
sequence.addROI(mesh);
return true;
}
}
package plugins.adufour.roi.mesh;
import java.util.HashSet;
import java.util.Set;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
/**
* Structural element of a {@link ROI3DMesh 3D mesh}, representing a position in 3D space. This
* class can be overridden to provide additional functionalities, under the condition that
* subclasses also override the {@link #clone()} method accordingly.
*
* @author Alexandre Dufour
*/
public class Vertex3D
{
public final Point3d position = new Point3d();
public final Vector3d normal = new Vector3d();
public final Set<Integer> neighbors;
protected Vertex3D(Vertex3D v)
{
this(v.position, v.neighbors);
}
protected Vertex3D(Point3d position)
{
this(position, 0);
}
protected Vertex3D(Point3d position, int nbNeighbors)
{
// if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
// {
// System.out.println("oups !");
// }
this.position.set(position);
this.neighbors = new HashSet<Integer>(nbNeighbors);
}
public Vertex3D(Point3d position, Set<Integer> neighbors)
{
this(position, neighbors.size());
for (Integer i : neighbors)
this.neighbors.add(i.intValue());
}
public Vertex3D clone()
{
return new Vertex3D(this);
}
public double distanceTo(Vertex3D v)
{
return position.distance(v.position);
}
public double distanceSqTo(Vertex3D v)
{
return position.distanceSquared(v.position);
}
public String toString()
{
return "Vertex {" + position.x + "," + position.y + "," + position.z + "} (" + neighbors.size() + " neighbors)";
}
}
package plugins.adufour.roi.mesh.polygon;
import java.util.List;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import plugins.adufour.roi.mesh.Cell3D;
import plugins.adufour.roi.mesh.Vertex3D;
/**
* Structural element of a {@link ROI3DPolygonalMesh 3D polygonal mesh}, representing a flat polygon
* in 3D space. A polygon does not store the vertex locations <i>per se</i>, but rather stores their
* indices in the vertex buffer of the corresponding mesh. By convention, polygon vertices are
* stored in counter-clockwise order when looking at the front of the face
*
* @author Alexandre Dufour
*/
public class Polygon3D extends Cell3D
{
/**
* Creates a new polygon from the specified vertex indices. This constructor is protected as it
* should not be used directly by client code. Client code should use
* {@link ROI3DPolygonalMesh#createCell(int[])} instead
*
* @param vertexIndices
* the vertex indices, in counter-clockwise order
*/
protected Polygon3D(int... vertexIndices)
{
super(vertexIndices);
}
@Override
public Polygon3D clone()
{
return new Polygon3D(this.vertexIndices);
}
/**
* Indicates whether the edge formed by the specified vertex indices belongs to this face
*
* @param vertexIndex1
* the index of the edge's first vertex
* @param vertexIndex2
* the index of the edge's second vertex
* @param inThatOrder
* <code>true</code> if the two indices must be found in the specified order
* (suggesting counter-clockwise ordering by convention), or <code>false</code> if
* the order is unimportant
* @return
*/
public boolean containsEdge(int vertexIndex1, int vertexIndex2, boolean inThatOrder)
{
int i1 = indexOf(vertexIndex1);
int i2 = indexOf(vertexIndex2);
if (i1 == -1 || i2 == -1) return false;
return !inThatOrder || ((i1 + 1) % size == i2);
}
/**
* Calculate the surface of this polygon, using the specified vertex list to fetch the vertex
* positions
*
* @param vertices
* the vertex list where to fetch the positions from
* @return the surface of this polygon
*/
public double getArea(List<Vertex3D> vertices)
{
Vector3d v12 = new Vector3d();
Vector3d v13 = new Vector3d();
Vector3d cross = new Vector3d();
// if the face has more than 3 vertices,
// split it into a triangle fan
// the first vertex of the fan never changes
// => has to be a vector for the final cross product
Vector3d v1 = new Vector3d(vertices.get(vertexIndices[0]).position);
double surface = 0;
for (int i = 1; i < size - 1; i++)
{
Point3d v2 = vertices.get(vertexIndices[i]).position;
Point3d v3 = vertices.get(vertexIndices[i + 1]).position;
v12.sub(v2, v1);
v13.sub(v3, v1);
cross.cross(v12, v13);
double surf = cross.length() * 0.5f;
surface += surf;
}
return surface;
}
/**
* Indicates whether the specified edge follows the ordering convention of this cell (this is
* implementation dependent)
*
* @param v1
* the first vertex index
* @param v2
* the second vertex index
* @return <code>true</code> if <code>v1</code> and <code>v2</code> are ordered following the
* convention in this cell (counter-clockwise in the present case), and
* <code>false</code> otherwise
* @throws IllegalArgumentException
* if one of the given indices does not belong to this cell
*/
public boolean isEdgeOrdered(int v1, int v2) throws IllegalArgumentException
{
int i1 = indexOf(v1);
if (i1 == -1) throw new IllegalArgumentException("Vertex index " + i1 + " does not belong to this face");
int i2 = indexOf(v2);
if (i2 == -1) throw new IllegalArgumentException("Vertex index " + i2 + " does not belong to this face");
return (i1 + 1) % size == i2;
}
}
package plugins.adufour.roi.mesh.polyhedron;
import java.util.List;
import javax.vecmath.Point3d;
import plugins.adufour.roi.mesh.Cell3D;
import plugins.adufour.roi.mesh.ROI3DMesh;
import plugins.adufour.roi.mesh.Vertex3D;
import vtk.vtkCell3D;
/**
* Generic structural element of a {@link ROI3DPolyhedralMesh 3D polyhedral mesh}
*
* @see Tetrahedron3D
* @author Alexandre Dufour
*/
public abstract class Polyhedron3D extends Cell3D
{
/**
* Creates a new polygon from the specified vertex indices. This constructor is protected as it
* should not be used directly by client code. Client code should use
* {@link ROI3DPolyhedralMesh#createCell(int[])} instead
*
* @param vertexIndices
* the vertex indices, in an order allowed by the local convention
*/
protected Polyhedron3D(int... vertexIndices)
{
super(vertexIndices);
}
/**
* @return a VTK representation of this polyhedron
*/
public abstract vtkCell3D createVTKCell();
}
package plugins.adufour.roi.mesh.polyhedron;
import vtk.vtkCell3D;
import vtk.vtkPyramid;