diff --git a/pom.xml b/pom.xml index 53f4c2442c..9615c72992 100644 --- a/pom.xml +++ b/pom.xml @@ -219,6 +219,10 @@ org.scijava scripting-javascript + + org.scijava + vecmath + diff --git a/src/main/java/net/imagej/ops/geom/CentroidTuple3D.java b/src/main/java/net/imagej/ops/geom/CentroidTuple3D.java new file mode 100644 index 0000000000..32a9770c71 --- /dev/null +++ b/src/main/java/net/imagej/ops/geom/CentroidTuple3D.java @@ -0,0 +1,32 @@ +package net.imagej.ops.geom; + +import net.imagej.ops.Ops; +import net.imagej.ops.special.function.AbstractUnaryFunctionOp; +import org.scijava.plugin.Plugin; +import org.scijava.vecmath.Tuple3d; +import org.scijava.vecmath.Vector3d; + +import java.util.Collection; + +/** + * Calculates the centroid (geometrical centre) of the given tuples + * + * @author Richard Domander (Royal Veterinary College, London) + */ +@Plugin(type = Ops.Geometric.Centroid.class) +public class CentroidTuple3D extends AbstractUnaryFunctionOp, Vector3d> + implements Ops.Geometric.Centroid { + @Override + public Vector3d compute1(final Collection input) { + final int vectors = input.size(); + if (vectors == 0) { + return new Vector3d(Double.NaN, Double.NaN, Double.NaN); + } + + final Vector3d sum = new Vector3d(0.0, 0.0, 0.0); + input.stream().forEach(sum::add); + sum.scale(1.0 / vectors); + + return sum; + } +} diff --git a/src/main/java/net/imagej/ops/geom/GeomNamespace.java b/src/main/java/net/imagej/ops/geom/GeomNamespace.java index 690265efbf..9b99f3cf55 100644 --- a/src/main/java/net/imagej/ops/geom/GeomNamespace.java +++ b/src/main/java/net/imagej/ops/geom/GeomNamespace.java @@ -51,6 +51,10 @@ import net.imglib2.util.Pair; import org.scijava.plugin.Plugin; +import org.scijava.vecmath.Tuple3d; +import org.scijava.vecmath.Vector3d; + +import java.util.Collection; /** * Namespace for Geom. @@ -221,6 +225,11 @@ public RealLocalizable centroid(final Mesh in) { return result; } + @OpMethod(op = net.imagej.ops.geom.CentroidTuple3D.class) + public Vector3d centroid(final Collection in) { + return (Vector3d) ops().run(net.imagej.ops.Ops.Geometric.Centroid.class, in); + } + @OpMethod(op = net.imagej.ops.geom.geom2d.DefaultCircularity.class) public DoubleType circularity(final Polygon in) { final DoubleType result = diff --git a/src/test/java/net/imagej/ops/geom/CentroidTuple3DTest.java b/src/test/java/net/imagej/ops/geom/CentroidTuple3DTest.java new file mode 100644 index 0000000000..fa67648311 --- /dev/null +++ b/src/test/java/net/imagej/ops/geom/CentroidTuple3DTest.java @@ -0,0 +1,59 @@ +package net.imagej.ops.geom; + +import net.imagej.ops.AbstractOpTest; +import org.junit.Test; +import org.scijava.vecmath.Vector3d; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.DoubleStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for {@link CentroidTuple3D} + * + * @author Richard Domander (Royal Veterinary College, London) + */ +public class CentroidTuple3DTest extends AbstractOpTest { + @Test + public void testCompute1EmptyCollection() throws Exception { + final List emptyList = Collections.emptyList(); + + final Vector3d result = (Vector3d) ops.run(CentroidTuple3D.class, emptyList); + + assertTrue("Coordinates should all be NaN", + DoubleStream.of(result.x, result.y, result.z).allMatch(Double::isNaN)); + } + + @Test + public void testCompute1SingleVector() throws Exception { + final Vector3d vector = new Vector3d(1.0, 2.0, 3.0); + final List vectors = Collections.singletonList(vector); + + final Vector3d result = (Vector3d) ops.run(CentroidTuple3D.class, vectors); + + assertEquals("Incorrect centroid vector", vector, result); + } + + @Test + public void testCompute1() throws Exception { + final Vector3d expected = new Vector3d(0.5, 0.5, 0.5); + final List cubeVectors = Arrays.asList( + new Vector3d(0.0, 0.0, 0.0), + new Vector3d(1.0, 0.0, 0.0), + new Vector3d(1.0, 1.0, 0.0), + new Vector3d(0.0, 1.0, 0.0), + new Vector3d(0.0, 0.0, 1.0), + new Vector3d(1.0, 0.0, 1.0), + new Vector3d(1.0, 1.0, 1.0), + new Vector3d(0.0, 1.0, 1.0) + ); + + final Vector3d result = (Vector3d) ops.run(CentroidTuple3D.class, cubeVectors); + + assertEquals("Incorrect centroid vector", expected, result); + } +} \ No newline at end of file