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 <[email protected]> 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); + } + }
