You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by js...@apache.org on 2026/08/06 14:48:10 UTC
(sis) 01/02: feat(Geometry): add polyhedrons faces and getFace method
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit f427b9b0e8684927e3fefee055760acfb9a7f96c
Author: jsorel <jo...@geomatys.com>
AuthorDate: Thu Aug 6 15:26:23 2026 +0200
feat(Geometry): add polyhedrons faces and getFace method
---
.../geometries/polyhedron/AbstractPolyhedron.java | 129 +++++++++++++++++-
.../sis/geometries/polyhedron/Dodecahedron.java | 64 +++++++++
.../sis/geometries/polyhedron/Hexahedron.java | 42 ++++++
.../sis/geometries/polyhedron/Icosahedron.java | 64 +++++++++
.../sis/geometries/polyhedron/Octahedron.java | 42 ++++++
.../polyhedron/RhombicTriacontahedron.java | 101 ++++++++++++++
.../sis/geometries/polyhedron/Tetrahedron.java | 36 +++++
.../polyhedron/TruncatedIcosahedron.java | 150 +++++++++++++++++++++
8 files changed, 627 insertions(+), 1 deletion(-)
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/AbstractPolyhedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/AbstractPolyhedron.java
index 96ca8aa219..9697bcc55c 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/AbstractPolyhedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/AbstractPolyhedron.java
@@ -17,9 +17,18 @@
package org.apache.sis.geometries.polyhedron;
import java.util.List;
+import org.apache.sis.geometries.GeometryFactory;
+import org.apache.sis.geometries.LinearRing;
import org.apache.sis.geometries.MultiPolygon;
+import org.apache.sis.geometries.Polygon;
import org.apache.sis.geometries.Polyhedron;
+import org.apache.sis.geometries.Sphere;
import org.apache.sis.geometries.internal.shared.AbstractGeometry;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.NDArrays;
+import org.apache.sis.geometries.math.ReadOnly;
+import org.apache.sis.geometries.math.Vector3D;
+import org.apache.sis.geometries.spherical.SphericalConvexPolygon;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -27,7 +36,40 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
*
* @author Johann Sorel (Geomatys)
*/
-public class AbstractPolyhedron extends AbstractGeometry implements Polyhedron{
+public abstract class AbstractPolyhedron extends AbstractGeometry implements Polyhedron{
+
+ /**
+ * Golden ratio, used to build the latitude constants of the icosahedral-
+ * symmetry solids (Icosahedron, Dodecahedron, RhombicTriacontahedron,
+ * TruncatedIcosahedron).
+ */
+ protected static final double PHI = (1 + Math.sqrt(5)) / 2;
+
+ /**
+ * Latitude of a cube corner (atan(1/sqrt(2)), equivalently asin(1/sqrt(3))),
+ * used by Tetrahedron, Hexahedron and Dodecahedron.
+ */
+ protected static final double CUBE_LAT = Math.atan(1 / Math.sqrt(2));
+
+ /**
+ * atan(phi), used by Icosahedron and RhombicTriacontahedron.
+ */
+ protected static final double ATAN_PHI = Math.atan(PHI);
+
+ /**
+ * atan(1/phi) = PI/2 - atan(phi), used by Icosahedron and RhombicTriacontahedron.
+ */
+ protected static final double ATAN_INV_PHI = Math.PI/2 - ATAN_PHI;
+
+ /**
+ * atan(phi^2), used by Dodecahedron and RhombicTriacontahedron.
+ */
+ protected static final double ATAN_PHI2 = Math.atan(PHI*PHI);
+
+ /**
+ * atan(1/phi^2) = PI/2 - atan(phi^2), used by Dodecahedron and RhombicTriacontahedron.
+ */
+ protected static final double ATAN_INV_PHI2 = Math.PI/2 - ATAN_PHI2;
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
@@ -59,4 +101,89 @@ public class AbstractPolyhedron extends AbstractGeometry implements Polyhedron{
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
+ /**
+ * @return number of faces on the polyhedron
+ */
+ public abstract int getFaceCount();
+
+ /**
+ * @return face polygon
+ */
+ public abstract Polygon getFace(int index);
+
+ /**
+ * Get the face index intersecting the given unit vector.
+ *
+ * @param unitVector a unit vector from the center of the polyhedron
+ * @return the face index intersecting the vector
+ */
+ public abstract int getFace(ReadOnly.Vector<?> unitVector);
+
+
+ /**
+ * Build a unit direction vector from a latitude/longitude, in radians.
+ *
+ * @param latRad latitude in radians, in range [-PI/2 .. PI/2]
+ * @param lonRad longitude in radians, in range [-PI .. PI]
+ * @return unit direction vector
+ */
+ protected static ReadOnly.Vector<?> fromLatLon(double latRad, double lonRad) {
+ return new Vector3D.Double().setFromLatLon(latRad, lonRad);
+ }
+
+ /**
+ * Build a direction vector from a latitude/longitude, in radians, scaled
+ * by the given radius.
+ *
+ * @param latRad latitude in radians, in range [-PI/2 .. PI/2]
+ * @param lonRad longitude in radians, in range [-PI .. PI]
+ * @param radius scale applied to the unit direction vector
+ * @return direction vector
+ */
+ protected static ReadOnly.Vector<?> fromLatLon(double latRad, double lonRad, double radius) {
+ return new Vector3D.Double().setFromLatLon(latRad, lonRad).scale(radius);
+ }
+
+ /**
+ * Find which face's solid-angle cone (as seen from the polyhedron center)
+ * contains the given unit vector.
+ * <p>
+ * Faces must be convex, with vertices in CCW order viewed from outside
+ * the polyhedron. For each face, the vector lies in its cone if it is on
+ * the inner side of every edge plane, following the same triple-product
+ * test as {@link org.apache.sis.geometries.spherical.SphericalTriangle#contains }
+ * generalized to polygons with any number of vertices. This holds for any
+ * convex polyhedron centered on the origin : each ray from the center
+ * crosses exactly one face.
+ *
+ * @param vertices polyhedron vertices
+ * @param faces polyhedron faces, as CCW vertex indices into {@code vertices}
+ * @param unitVector vector to test
+ * @return matching face index, or -1 if none matched
+ */
+ protected static int nearestFace(Array vertices, int[][] faces, ReadOnly.Vector<?> unitVector) {
+ final Sphere sphere = new Sphere(3);
+ for (int f = 0; f < faces.length; f++) {
+ final Array polyArray = NDArrays.subset(vertices, faces[f]);
+ final SphericalConvexPolygon face = new SphericalConvexPolygon(sphere, polyArray);
+ if (face.contains(unitVector)) {
+ return f;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Build the polygon of one face.
+ *
+ * @param vertices polyhedron vertices
+ * @param face face vertex indices into {@code vertices}, in CCW order viewed from outside
+ * @return face polygon
+ */
+ protected static Polygon toPolygon(Array vertices, int[] face) {
+ final Array positions = NDArrays.subset(vertices, face);
+ final LinearRing ring = GeometryFactory.createLinearRing(GeometryFactory.createSequence(positions));
+ return GeometryFactory.createPolygon(ring, null);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Dodecahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Dodecahedron.java
index 388b64da6c..ff7b851787 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Dodecahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Dodecahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/RegularDodecahedron.html
@@ -23,4 +29,62 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class Dodecahedron extends AbstractPolyhedron{
+ /**
+ * Vertices are the dual of {@link Icosahedron} (each icosahedron face
+ * centroid, normalized), so both solids share a consistent orientation.
+ */
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( ATAN_PHI2, 0),
+ fromLatLon( ATAN_PHI2, Math.PI),
+ fromLatLon( CUBE_LAT, Math.PI/4),
+ fromLatLon( CUBE_LAT, 3*Math.PI/4),
+ fromLatLon( ATAN_INV_PHI2, Math.PI/2),
+ fromLatLon( -ATAN_PHI2, 0),
+ fromLatLon( -ATAN_PHI2, Math.PI),
+ fromLatLon( -CUBE_LAT, Math.PI/4),
+ fromLatLon( -CUBE_LAT, 3*Math.PI/4),
+ fromLatLon(-ATAN_INV_PHI2, Math.PI/2),
+ fromLatLon( CUBE_LAT, -Math.PI/4),
+ fromLatLon( CUBE_LAT, -3*Math.PI/4),
+ fromLatLon( ATAN_INV_PHI2, -Math.PI/2),
+ fromLatLon( -CUBE_LAT, -Math.PI/4),
+ fromLatLon( -CUBE_LAT, -3*Math.PI/4),
+ fromLatLon(-ATAN_INV_PHI2, -Math.PI/2),
+ fromLatLon( 0, ATAN_INV_PHI2),
+ fromLatLon( 0, -ATAN_INV_PHI2),
+ fromLatLon( 0, Math.PI - ATAN_INV_PHI2),
+ fromLatLon( 0, -(Math.PI - ATAN_INV_PHI2))
+ }, true).build();
+
+ private static final int[][] FACES = {
+ { 3, 1, 0, 2, 4, 3},
+ {12, 10, 0, 1, 11, 12},
+ {16, 2, 0, 10, 17, 16},
+ {19, 11, 1, 3, 18, 19},
+ { 9, 4, 2, 16, 7, 9},
+ { 8, 18, 3, 4, 9, 8},
+ { 9, 7, 5, 6, 8, 9},
+ {14, 6, 5, 13, 15, 14},
+ {17, 13, 5, 7, 16, 17},
+ {18, 8, 6, 14, 19, 18},
+ {13, 17, 10, 12, 15, 13},
+ {15, 12, 11, 19, 14, 15}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Hexahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Hexahedron.java
index e970cd38d0..58f303a468 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Hexahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Hexahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/Cube.html
@@ -23,4 +29,40 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class Hexahedron extends AbstractPolyhedron{
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( CUBE_LAT, Math.PI/4),
+ fromLatLon(-CUBE_LAT, Math.PI/4),
+ fromLatLon( CUBE_LAT, -Math.PI/4),
+ fromLatLon(-CUBE_LAT, -Math.PI/4),
+ fromLatLon( CUBE_LAT, 3*Math.PI/4),
+ fromLatLon(-CUBE_LAT, 3*Math.PI/4),
+ fromLatLon( CUBE_LAT, -3*Math.PI/4),
+ fromLatLon(-CUBE_LAT, -3*Math.PI/4)
+ }, true).build();
+
+ private static final int[][] FACES = {
+ {1, 0, 2, 3, 1},
+ {4, 0, 1, 5, 4},
+ {2, 0, 4, 6, 2},
+ {5, 1, 3, 7, 5},
+ {3, 2, 6, 7, 3},
+ {6, 4, 5, 7, 6}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Icosahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Icosahedron.java
index a2fca45320..3f874e9c2e 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Icosahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Icosahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/RegularIcosahedron.html
@@ -23,4 +29,62 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class Icosahedron extends AbstractPolyhedron{
+ /**
+ * Vertices are all cyclic permutations of (0, +-1, +-phi), normalized,
+ * where phi is the golden ratio.
+ */
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( ATAN_PHI, Math.PI/2),
+ fromLatLon( -ATAN_PHI, Math.PI/2),
+ fromLatLon( ATAN_PHI, -Math.PI/2),
+ fromLatLon( -ATAN_PHI, -Math.PI/2),
+ fromLatLon( ATAN_INV_PHI, 0),
+ fromLatLon( ATAN_INV_PHI, Math.PI),
+ fromLatLon(-ATAN_INV_PHI, 0),
+ fromLatLon(-ATAN_INV_PHI, Math.PI),
+ fromLatLon( 0, ATAN_PHI),
+ fromLatLon( 0, -ATAN_PHI),
+ fromLatLon( 0, Math.PI - ATAN_PHI),
+ fromLatLon( 0, -(Math.PI - ATAN_PHI))
+ }, true).build();
+
+ private static final int[][] FACES = {
+ { 4, 0, 2, 4},
+ { 2, 0, 5, 2},
+ { 8, 0, 4, 8},
+ { 5, 0, 10, 5},
+ {10, 0, 8, 10},
+ { 3, 1, 6, 3},
+ { 7, 1, 3, 7},
+ { 6, 1, 8, 6},
+ {10, 1, 7, 10},
+ { 8, 1, 10, 8},
+ { 4, 2, 9, 4},
+ {11, 2, 5, 11},
+ { 9, 2, 11, 9},
+ { 9, 3, 6, 9},
+ { 7, 3, 11, 7},
+ {11, 3, 9, 11},
+ { 8, 4, 6, 8},
+ { 6, 4, 9, 6},
+ { 7, 5, 10, 7},
+ {11, 5, 7, 11}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Octahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Octahedron.java
index b097e0c12b..60cf48bfa9 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Octahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Octahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/RegularOctahedron.html
@@ -23,4 +29,40 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class Octahedron extends AbstractPolyhedron{
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( 0, 0),
+ fromLatLon( 0, Math.PI),
+ fromLatLon( 0, Math.PI/2),
+ fromLatLon( 0, -Math.PI/2),
+ fromLatLon( Math.PI/2, 0),
+ fromLatLon(-Math.PI/2, 0)
+ }, true).build();
+
+ private static final int[][] FACES = {
+ {4, 0, 2, 4},
+ {2, 0, 5, 2},
+ {3, 0, 4, 3},
+ {5, 0, 3, 5},
+ {2, 1, 4, 2},
+ {5, 1, 2, 5},
+ {4, 1, 3, 4},
+ {3, 1, 5, 3}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/RhombicTriacontahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/RhombicTriacontahedron.java
index a5c12fdc02..1903669c58 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/RhombicTriacontahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/RhombicTriacontahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/RhombicTriacontahedron.html
@@ -23,4 +29,99 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class RhombicTriacontahedron extends AbstractPolyhedron{
+ /**
+ * Scale applied to the 20 dodecahedron-direction vertices, so every face
+ * is a planar golden rhombus.
+ */
+ private static final double INNER_RADIUS = 0.9105929973100293;
+
+ /**
+ * The first 12 vertices are the {@link Icosahedron} vertices (degree 5),
+ * the last 20 are the {@link Dodecahedron} vertices (degree 3) scaled so
+ * every face is a planar golden rhombus.
+ */
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( ATAN_PHI, Math.PI/2),
+ fromLatLon( -ATAN_PHI, Math.PI/2),
+ fromLatLon( ATAN_PHI, -Math.PI/2),
+ fromLatLon( -ATAN_PHI, -Math.PI/2),
+ fromLatLon( ATAN_INV_PHI, 0),
+ fromLatLon( ATAN_INV_PHI, Math.PI),
+ fromLatLon( -ATAN_INV_PHI, 0),
+ fromLatLon( -ATAN_INV_PHI, Math.PI),
+ fromLatLon( 0, ATAN_PHI),
+ fromLatLon( 0, -ATAN_PHI),
+ fromLatLon( 0, Math.PI - ATAN_PHI),
+ fromLatLon( 0, -(Math.PI - ATAN_PHI)),
+ fromLatLon( ATAN_PHI2, 0, INNER_RADIUS),
+ fromLatLon( ATAN_PHI2, Math.PI, INNER_RADIUS),
+ fromLatLon( CUBE_LAT, Math.PI/4, INNER_RADIUS),
+ fromLatLon( CUBE_LAT, 3*Math.PI/4, INNER_RADIUS),
+ fromLatLon( ATAN_INV_PHI2, Math.PI/2, INNER_RADIUS),
+ fromLatLon( -ATAN_PHI2, 0, INNER_RADIUS),
+ fromLatLon( -ATAN_PHI2, Math.PI, INNER_RADIUS),
+ fromLatLon( -CUBE_LAT, Math.PI/4, INNER_RADIUS),
+ fromLatLon( -CUBE_LAT, 3*Math.PI/4, INNER_RADIUS),
+ fromLatLon(-ATAN_INV_PHI2, Math.PI/2, INNER_RADIUS),
+ fromLatLon( CUBE_LAT, -Math.PI/4, INNER_RADIUS),
+ fromLatLon( CUBE_LAT, -3*Math.PI/4, INNER_RADIUS),
+ fromLatLon( ATAN_INV_PHI2, -Math.PI/2, INNER_RADIUS),
+ fromLatLon( -CUBE_LAT, -Math.PI/4, INNER_RADIUS),
+ fromLatLon( -CUBE_LAT, -3*Math.PI/4, INNER_RADIUS),
+ fromLatLon(-ATAN_INV_PHI2, -Math.PI/2, INNER_RADIUS),
+ fromLatLon( 0, ATAN_INV_PHI2, INNER_RADIUS),
+ fromLatLon( 0, -ATAN_INV_PHI2, INNER_RADIUS),
+ fromLatLon( 0, Math.PI - ATAN_INV_PHI2, INNER_RADIUS),
+ fromLatLon( 0, -(Math.PI - ATAN_INV_PHI2), INNER_RADIUS)
+ }, true).build();
+
+ private static final int[][] FACES = {
+ {13, 2, 12, 0, 13},
+ { 0, 12, 4, 14, 0},
+ {15, 5, 13, 0, 15},
+ { 0, 14, 8, 16, 0},
+ {16, 10, 15, 0, 16},
+ { 1, 17, 3, 18, 1},
+ {19, 6, 17, 1, 19},
+ { 1, 18, 7, 20, 1},
+ {21, 8, 19, 1, 21},
+ { 1, 20, 10, 21, 1},
+ {22, 4, 12, 2, 22},
+ { 2, 13, 5, 23, 2},
+ {24, 9, 22, 2, 24},
+ { 2, 23, 11, 24, 2},
+ { 3, 17, 6, 25, 3},
+ {26, 7, 18, 3, 26},
+ { 3, 25, 9, 27, 3},
+ {27, 11, 26, 3, 27},
+ {29, 6, 28, 4, 29},
+ {28, 8, 14, 4, 28},
+ { 4, 22, 9, 29, 4},
+ { 5, 30, 7, 31, 5},
+ { 5, 15, 10, 30, 5},
+ {31, 11, 23, 5, 31},
+ { 6, 19, 8, 28, 6},
+ {29, 9, 25, 6, 29},
+ {30, 10, 20, 7, 30},
+ { 7, 26, 11, 31, 7},
+ {21, 10, 16, 8, 21},
+ { 9, 24, 11, 27, 9}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Tetrahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Tetrahedron.java
index 7c978a66ca..1e3d7d93c1 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Tetrahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/Tetrahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/Tetrahedron.html
@@ -23,4 +29,34 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class Tetrahedron extends AbstractPolyhedron{
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( CUBE_LAT, Math.PI/4),
+ fromLatLon(-CUBE_LAT, -Math.PI/4),
+ fromLatLon(-CUBE_LAT, 3*Math.PI/4),
+ fromLatLon( CUBE_LAT, -3*Math.PI/4)
+ }, true).build();
+
+ private static final int[][] FACES = {
+ {2, 0, 1, 2},
+ {1, 0, 3, 1},
+ {3, 0, 2, 3},
+ {2, 1, 3, 2}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}
diff --git a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/TruncatedIcosahedron.java b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/TruncatedIcosahedron.java
index aebe4cfe7a..0d5aa46141 100644
--- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/TruncatedIcosahedron.java
+++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/polyhedron/TruncatedIcosahedron.java
@@ -16,6 +16,12 @@
*/
package org.apache.sis.geometries.polyhedron;
+import org.apache.sis.geometries.Polygon;
+import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.ArrayFactoryJava;
+import org.apache.sis.geometries.math.DataType;
+import org.apache.sis.geometries.math.ReadOnly;
+
/**
*
* @see https://mathworld.wolfram.com/TruncatedIcosahedron.html
@@ -23,4 +29,148 @@ package org.apache.sis.geometries.polyhedron;
*/
public final class TruncatedIcosahedron extends AbstractPolyhedron{
+ /**
+ * atan(1/(3*phi)) : one of the two latitude values of the vertices
+ * shared between a pentagon and two hexagons.
+ */
+ private static final double ATAN_INV_3PHI = Math.atan(1 / (3*PHI));
+
+ /**
+ * Remaining distinct latitude/longitude magnitudes appearing in the
+ * vertices below. Unlike the other solids, a truncated icosahedron
+ * vertex is a 1/3-interpolation between two {@link Icosahedron}
+ * vertices, normalized back onto the unit sphere ; this does not reduce
+ * to a simple expression of PI or phi, so these are plain precise
+ * radian constants rather than derived formulas.
+ */
+ private static final double LAT_1 = 1.3676273807451667;
+ private static final double LAT_2 = 1.0250282040502057;
+ private static final double LAT_3 = 0.81835937075291465;
+ private static final double LAT_4 = 0.71147906437076591;
+ private static final double LAT_5 = 0.33257431379403996;
+ private static final double LAT_6 = 0.41539154898382641;
+ private static final double LON_1 = 0.89058134991397542;
+ private static final double LON_2 = 0.29970859976855624;
+ private static final double LON_3 = 1.3011353287662717;
+ private static final double LON_4 = 1.1296873864711223;
+
+ /**
+ * Vertices are obtained by truncating each {@link Icosahedron} edge at
+ * its 1/3 and 2/3 points (the uniform truncation ratio that keeps every
+ * new edge, pentagon and hexagon, the same length), then normalized.
+ */
+ private static final Array VERTICES = ArrayFactoryJava.INSTANCE.builder()
+ .dataType(DataType.DOUBLE).values(new ReadOnly.Vector<?>[]{
+ fromLatLon( LAT_1, Math.PI/2),
+ fromLatLon( LAT_1, -Math.PI/2),
+ fromLatLon( LAT_2, LON_1),
+ fromLatLon( LAT_3, LON_2),
+ fromLatLon( LAT_2, Math.PI - LON_1),
+ fromLatLon( LAT_3, Math.PI - LON_2),
+ fromLatLon( LAT_4, LON_3),
+ fromLatLon( LAT_5, LON_4),
+ fromLatLon( LAT_4, Math.PI - LON_3),
+ fromLatLon( LAT_5, Math.PI - LON_4),
+ fromLatLon( -LAT_1, Math.PI/2),
+ fromLatLon( -LAT_1, -Math.PI/2),
+ fromLatLon( -LAT_2, LON_1),
+ fromLatLon( -LAT_3, LON_2),
+ fromLatLon( -LAT_2, Math.PI - LON_1),
+ fromLatLon( -LAT_3, Math.PI - LON_2),
+ fromLatLon( -LAT_4, LON_3),
+ fromLatLon( -LAT_5, LON_4),
+ fromLatLon( -LAT_4, Math.PI - LON_3),
+ fromLatLon( -LAT_5, Math.PI - LON_4),
+ fromLatLon( LAT_2, -LON_1),
+ fromLatLon( LAT_3, -LON_2),
+ fromLatLon( LAT_2, -(Math.PI - LON_1)),
+ fromLatLon( LAT_3, -(Math.PI - LON_2)),
+ fromLatLon( LAT_4, -LON_3),
+ fromLatLon( LAT_5, -LON_4),
+ fromLatLon( LAT_4, -(Math.PI - LON_3)),
+ fromLatLon( LAT_5, -(Math.PI - LON_4)),
+ fromLatLon( -LAT_2, -LON_1),
+ fromLatLon( -LAT_3, -LON_2),
+ fromLatLon( -LAT_2, -(Math.PI - LON_1)),
+ fromLatLon( -LAT_3, -(Math.PI - LON_2)),
+ fromLatLon( -LAT_4, -LON_3),
+ fromLatLon( -LAT_5, -LON_4),
+ fromLatLon( -LAT_4, -(Math.PI - LON_3)),
+ fromLatLon( -LAT_5, -(Math.PI - LON_4)),
+ fromLatLon( ATAN_INV_3PHI, 0),
+ fromLatLon(-ATAN_INV_3PHI, 0),
+ fromLatLon( LAT_6, ATAN_INV_PHI2),
+ fromLatLon( ATAN_INV_3PHI, 2*ATAN_INV_PHI2),
+ fromLatLon( LAT_6, -ATAN_INV_PHI2),
+ fromLatLon( ATAN_INV_3PHI, -2*ATAN_INV_PHI2),
+ fromLatLon( ATAN_INV_3PHI, Math.PI),
+ fromLatLon(-ATAN_INV_3PHI, Math.PI),
+ fromLatLon( LAT_6, Math.PI - ATAN_INV_PHI2),
+ fromLatLon( ATAN_INV_3PHI, Math.PI - 2*ATAN_INV_PHI2),
+ fromLatLon( LAT_6, -(Math.PI - ATAN_INV_PHI2)),
+ fromLatLon( ATAN_INV_3PHI, -(Math.PI - 2*ATAN_INV_PHI2)),
+ fromLatLon( -LAT_6, ATAN_INV_PHI2),
+ fromLatLon(-ATAN_INV_3PHI, 2*ATAN_INV_PHI2),
+ fromLatLon( -LAT_6, -ATAN_INV_PHI2),
+ fromLatLon(-ATAN_INV_3PHI, -2*ATAN_INV_PHI2),
+ fromLatLon( -LAT_6, Math.PI - ATAN_INV_PHI2),
+ fromLatLon(-ATAN_INV_3PHI, Math.PI - 2*ATAN_INV_PHI2),
+ fromLatLon( -LAT_6, -(Math.PI - ATAN_INV_PHI2)),
+ fromLatLon(-ATAN_INV_3PHI, -(Math.PI - 2*ATAN_INV_PHI2)),
+ fromLatLon( 0, LAT_1),
+ fromLatLon( 0, Math.PI - LAT_1),
+ fromLatLon( 0, -LAT_1),
+ fromLatLon( 0, -(Math.PI - LAT_1))
+ }, true).build();
+
+ private static final int[][] FACES = {
+ { 8, 4, 0, 2, 6, 8},
+ {16, 12, 10, 14, 18, 16},
+ {24, 20, 1, 22, 26, 24},
+ {34, 30, 11, 28, 32, 34},
+ {36, 38, 3, 21, 40, 36},
+ {46, 23, 5, 44, 42, 46},
+ {50, 29, 13, 48, 37, 50},
+ {43, 52, 15, 31, 54, 43},
+ {17, 56, 7, 39, 49, 17},
+ {51, 41, 25, 58, 33, 51},
+ {53, 45, 9, 57, 19, 53},
+ {35, 59, 27, 47, 55, 35},
+ { 3, 2, 0, 1, 20, 21, 3},
+ { 1, 0, 4, 5, 23, 22, 1},
+ { 7, 6, 2, 3, 38, 39, 7},
+ { 5, 4, 8, 9, 45, 44, 5},
+ { 9, 8, 6, 7, 56, 57, 9},
+ {11, 10, 12, 13, 29, 28, 11},
+ {15, 14, 10, 11, 30, 31, 15},
+ {13, 12, 16, 17, 49, 48, 13},
+ {19, 18, 14, 15, 52, 53, 19},
+ {17, 16, 18, 19, 57, 56, 17},
+ {21, 20, 24, 25, 41, 40, 21},
+ {27, 26, 22, 23, 46, 47, 27},
+ {25, 24, 26, 27, 59, 58, 25},
+ {33, 32, 28, 29, 50, 51, 33},
+ {31, 30, 34, 35, 55, 54, 31},
+ {35, 34, 32, 33, 58, 59, 35},
+ {39, 38, 36, 37, 48, 49, 39},
+ {37, 36, 40, 41, 51, 50, 37},
+ {43, 42, 44, 45, 53, 52, 43},
+ {47, 46, 42, 43, 54, 55, 47}
+ };
+
+ @Override
+ public int getFaceCount() {
+ return FACES.length;
+ }
+
+ @Override
+ public Polygon getFace(int index) {
+ return toPolygon(VERTICES, FACES[index]);
+ }
+
+ @Override
+ public int getFace(ReadOnly.Vector<?> unitVector) {
+ return nearestFace(VERTICES, FACES, unitVector);
+ }
+
}