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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 15377fcb44 refactor(Geometry): remove MultiMeshPrimitive, replaced by 
GeometryCollection
15377fcb44 is described below

commit 15377fcb4437f2f5f59276eebc7e709260ccc28e
Author: jsorel <[email protected]>
AuthorDate: Tue Nov 3 14:37:55 2026 +0100

    refactor(Geometry): remove MultiMeshPrimitive, replaced by 
GeometryCollection
---
 .../main/org/apache/sis/geometries/Geometries.java |  35 ++-
 .../apache/sis/geometries/GeometryCollection.java  |   4 +-
 .../apache/sis/geometries/adapter/JTSAdapter.java  |  25 +-
 .../geometries/mesh/MeshPrimitiveComparator.java   |   9 +-
 .../sis/geometries/mesh/MeshPrimitiveVisitor.java  |  15 +-
 .../sis/geometries/mesh/MultiMeshPrimitive.java    |  96 ------
 .../sis/geometries/operation/ComputeAttribute.java |  30 +-
 .../geometries/operation/GeometryProcessor.java    |  13 +-
 .../sis/geometries/operation/Intersection.java     |   5 +-
 .../sis/geometries/operation/ToPrimitive.java      |  41 +--
 .../apache/sis/geometries/operation/Transform.java |  22 +-
 .../operation/extrude/TerrainExtruder.java         |  13 +-
 .../main/org/apache/sis/geometries/summary.adoc    | 323 ++++++++++++++-------
 .../sis/geometries/GeometryCollectionTest.java     |  34 +++
 .../sis/geometries/adapter/JTSAdapterTest.java     |  68 +++++
 .../sis/geometries/operation/ToPrimitiveTest.java  |  15 +-
 .../sis/geometries/operation/TransformTest.java    |   7 +-
 .../operation/extrude/TerrainExtruderTest.java     |   8 +-
 18 files changed, 467 insertions(+), 296 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
index f4c610a684..176da49173 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
@@ -49,7 +49,6 @@ import org.apache.sis.geometries.adapter.JTSAdapter;
 import org.apache.sis.geometries.adapter.ShapeAdapter;
 import org.apache.sis.geometries.adapter.ShapeConverter;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometry.wrapper.jts.JTS;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.Matrix3D;
@@ -390,9 +389,9 @@ public final class Geometries {
      *
      * @param mesh not null
      */
-    public static void smoothVertices(MultiMeshPrimitive<?> mesh) {
+    public static void smoothVertices(GeometryCollection<? extends 
MeshPrimitive> mesh) {
         ArgumentChecks.ensureNonNull("mesh", mesh);
-        for (MeshPrimitive p : mesh.getComponents()) {
+        for (MeshPrimitive p : mesh) {
             p.removeDuplicatesByPosition();
         }
     }
@@ -660,6 +659,36 @@ public final class Geometries {
         
primitive.setIndex(NDArrays.ofUnsigned(primitive.getIndex().getSampleSystem(), 
index));
     }
 
+    /**
+     * Check geometry definition.
+     */
+    public static void validate(GeometryCollection<? extends MeshPrimitive> 
collection) {
+        for (MeshPrimitive p : collection) {
+            p.validate();
+        }
+    }
+
+    /**
+     * Set each vertex normal by computing the normal of the triangle
+     * where it is used.
+     * This method should not be used if vertices are shared by multiple
+     * triangles or it will cause visual glitches.
+     */
+    public static void computeFaceNormals(GeometryCollection<? extends 
MeshPrimitive> collection) {
+        for (MeshPrimitive p : collection) {
+            p.computeFaceNormals();
+        }
+    }
+
+    /**
+     * Set each vertex normal by computing the normal of the triangle
+     * where it is used.
+     */
+    public static void computeSmoothNormals(GeometryCollection<? extends 
MeshPrimitive> collection) {
+        for (MeshPrimitive p : collection) {
+            p.computeSmoothNormals();
+        }
+    }
     public static MeshPrimitive.Triangles 
toPrimitive(List<org.locationtech.jts.geom.Polygon> triangles, 
CoordinateReferenceSystem crs) {
         final int dimension = crs.getCoordinateSystem().getDimension();
         final float[] values = new float[Math.multiplyExact(triangles.size(), 
3 * dimension)];
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
index e2a888a6d4..f74ad8d24d 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/GeometryCollection.java
@@ -413,14 +413,14 @@ public sealed interface GeometryCollection<T extends 
Geometry> extends Geometry,
                 if (index >= count) {
                     throw new NoSuchElementException();
                 }
-                return getGeometryN(index);
+                return getGeometryN(index++);
             }
 
             @Override
             public void forEachRemaining(final Consumer<? super T> action) {
                 Objects.requireNonNull(action);
                 while (index < count) {
-                    action.accept(getGeometryN(index));
+                    action.accept(getGeometryN(index++));
                 }
             }
         };
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/JTSAdapter.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/JTSAdapter.java
index ba8fa4a965..4e1ffebd06 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/JTSAdapter.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/adapter/JTSAdapter.java
@@ -222,15 +222,18 @@ public final class JTSAdapter {
     private static class JTSDataPoints implements DataPoints {
 
         private final CoordinateSequence jts;
-        private final CoordinateReferenceSystem crs;
         private final int dim;
-        private final DataPointsType type;
+        private CoordinateReferenceSystem crs;
+        private DataPointsType type;
 
         private JTSDataPoints(CoordinateSequence jts, 
CoordinateReferenceSystem crs) {
             this.jts = jts;
-            this.crs = crs;
             this.dim = jts.getDimension();
+            setCRS(crs);
+        }
 
+        private void setCRS(CoordinateReferenceSystem crs) {
+            this.crs = crs;
             final DataPointsType.Template at = new DataPointsType.Template();
             at.addOrReplaceAttribute(DataPointsType.ATT_POSITION, 
SampleSystem.of(crs), DataType.DOUBLE);
             type = at;
@@ -241,9 +244,18 @@ public final class JTSAdapter {
             return crs;
         }
 
+        /**
+         * Relabels the positions with the given CRS, the JTS coordinates are 
not modified.
+         *
+         * @throws IllegalArgumentException if the CRS dimension differs from 
the current one.
+         */
         @Override
         public void setCoordinateReferenceSystem(CoordinateReferenceSystem cs) 
throws IllegalArgumentException {
-            throw new UnsupportedOperationException("Not supported.");
+            final int dimension = crs.getCoordinateSystem().getDimension();
+            if (cs.getCoordinateSystem().getDimension() != dimension) {
+                throw new IllegalArgumentException("Coordinate reference 
system must have " + dimension + " dimensions.");
+            }
+            setCRS(cs);
         }
 
         @Override
@@ -263,8 +275,9 @@ public final class JTSAdapter {
 
         @Override
         public Vector getPosition(int index) {
+            // The JTS sequence may have more ordinates than the CRS, for 
example an unused z.
             final double[] v = new 
double[crs.getCoordinateSystem().getDimension()];
-            for (int i = 0; i < dim; i++) {
+            for (int i = 0, n = Math.min(dim, v.length); i < n; i++) {
                 v[i] = jts.getOrdinate(index, i);
             }
             return Vectors.create(v, DataType.DOUBLE);
@@ -272,7 +285,7 @@ public final class JTSAdapter {
 
         @Override
         public void setPosition(int index, Vector value) {
-            for (int i = 0; i < dim; i++) {
+            for (int i = 0, n = Math.min(dim, value.getDimension()); i < n; 
i++) {
                 jts.setOrdinate(index, i, value.get(i));
             }
         }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveComparator.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveComparator.java
index 678efce761..c39d080231 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveComparator.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveComparator.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Objects;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
 import org.apache.sis.geometries.Point;
 import org.apache.sis.geometries.curve.LineString;
 import org.apache.sis.geometries.internal.shared.ArrayDataPoints;
@@ -157,10 +158,10 @@ public final class MeshPrimitiveComparator {
     }
 
     /**
-     * Compare two multi-primitive.
+     * Compare two collections of primitives.
      * @throws IllegalArgumentException if they are not equal
      */
-    public void compare(MultiMeshPrimitive expected, MultiMeshPrimitive 
candidate) throws IllegalArgumentException {
+    public void compare(GeometryCollection<? extends MeshPrimitive> expected, 
GeometryCollection<? extends MeshPrimitive> candidate) throws 
IllegalArgumentException {
 
         if (compareCrs) {
             if (!CRS.equivalent(expected.getCoordinateReferenceSystem(), 
candidate.getCoordinateReferenceSystem())) {
@@ -168,8 +169,8 @@ public final class MeshPrimitiveComparator {
             }
         }
 
-        final List<? extends MeshPrimitive> expectedPrimitives = 
expected.getComponents();
-        final List<? extends MeshPrimitive> candidatePrimitives = 
candidate.getComponents();
+        final List<? extends MeshPrimitive> expectedPrimitives = 
expected.stream().toList();
+        final List<? extends MeshPrimitive> candidatePrimitives = 
candidate.stream().toList();
 
         int size = expectedPrimitives.size();
         if (expectedPrimitives.size() != candidatePrimitives.size()) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveVisitor.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveVisitor.java
index f1e66cbcc5..c2d3089aea 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveVisitor.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MeshPrimitiveVisitor.java
@@ -19,6 +19,7 @@ package org.apache.sis.geometries.mesh;
 import java.util.HashSet;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
 import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.Point;
 import org.apache.sis.geometries.curve.LineString;
@@ -39,7 +40,7 @@ import org.apache.sis.util.ArgumentChecks;
 public abstract class MeshPrimitiveVisitor {
 
     private final HashSet visited = new HashSet();
-    private MultiMeshPrimitive<?> geometry;
+    private GeometryCollection<? extends MeshPrimitive> geometry;
     private MeshPrimitive primitive;
 
 
@@ -51,11 +52,11 @@ public abstract class MeshPrimitiveVisitor {
      */
     public MeshPrimitiveVisitor(Geometry geometry) {
         if (geometry instanceof MeshPrimitive p) {
-            reset(new MultiMeshPrimitive(p));
-        } else if (geometry instanceof MultiMeshPrimitive mp) {
-            reset(mp);
+            reset(GeometryFactory.DEFAULT.createGeometryCollection(p));
+        } else if (geometry instanceof GeometryCollection<?> c && 
c.stream().allMatch(MeshPrimitive.class::isInstance)) {
+            reset((GeometryCollection<? extends MeshPrimitive>) c);
         } else {
-            throw new IllegalArgumentException("Geometry is not a primitive");
+            throw new IllegalArgumentException("Geometry is not a primitive or 
a collection of primitives");
         }
     }
 
@@ -64,7 +65,7 @@ public abstract class MeshPrimitiveVisitor {
      *
      * @param geometry not null
      */
-    public void reset(MultiMeshPrimitive geometry) {
+    public void reset(GeometryCollection<? extends MeshPrimitive> geometry) {
         ArgumentChecks.ensureNonNull("geometry", geometry);
         this.geometry = geometry;
         visited.clear();
@@ -74,7 +75,7 @@ public abstract class MeshPrimitiveVisitor {
      * Loop on all primitives in the geometry.
      */
     public void visit() {
-        for (MeshPrimitive p : geometry.getComponents()) {
+        for (MeshPrimitive p : geometry) {
             visit(p);
         }
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
deleted file mode 100644
index ac99004fa9..0000000000
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/mesh/MultiMeshPrimitive.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.geometries.mesh;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.geometries.Geometries;
-import org.apache.sis.geometries.GeometryType;
-import org.apache.sis.geometries.internal.shared.DefaultGeometryCollection;
-
-
-/**
- * MultiPrimitive is a group of primitive defining a more complex geometry.
- * A primitive mimics the natural structures used by GPUs.
- *
- * @author Johann Sorel (Geomatys)
- */
-public final class MultiMeshPrimitive<T extends MeshPrimitive> extends 
DefaultGeometryCollection<T> {
-
-    /**
-     * Create a geometry with right hand 3D CRS.
-     */
-    public MultiMeshPrimitive() {
-        this(Geometries.RIGHT_HAND_3D);
-    }
-
-    public MultiMeshPrimitive(CoordinateReferenceSystem crs) {
-        super(GeometryType.GEOMETRY, crs, null);
-    }
-
-    public MultiMeshPrimitive(MeshPrimitive ... primitives) {
-        this(primitives[0].getCoordinateReferenceSystem());
-        append(Arrays.asList(primitives));
-    }
-
-    public boolean isEmpty() {
-        for (MeshPrimitive p : geometries) {
-            if (!p.isEmpty()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public List<T> getComponents() {
-        return Collections.unmodifiableList(geometries);
-    }
-
-    /**
-     * Check geometry definition.
-     */
-    public void validate() {
-        for (MeshPrimitive p : geometries) {
-            p.validate();
-        }
-    }
-
-    /**
-     * Set each vertex normal by computing the normal of the triangle
-     * where it is used.
-     * This method should not be used if vertices are shared by multiple
-     * triangles or it will cause visual glitches.
-     */
-    public void computeFaceNormals() {
-        for (MeshPrimitive p : geometries) {
-            p.computeFaceNormals();
-        }
-    }
-
-    /**
-     * Set each vertex normal by computing the normal of the triangle
-     * where it is used.
-     */
-    public void computeSmoothNormals() {
-        for (MeshPrimitive p : geometries) {
-            p.computeSmoothNormals();
-        }
-    }
-
-}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ComputeAttribute.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ComputeAttribute.java
index 340e5eeb6b..a19bd20d82 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ComputeAttribute.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ComputeAttribute.java
@@ -16,16 +16,15 @@
  */
 package org.apache.sis.geometries.operation;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.function.Function;
 import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
+import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.Point;
 import org.apache.sis.geometries.internal.shared.ArrayDataPoints;
 import org.apache.sis.geometries.internal.shared.Vertex;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MeshPrimitiveVisitor;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.DataType;
 import org.apache.sis.maths.NDArrays;
@@ -69,21 +68,20 @@ public final class ComputeAttribute {
 
 
     /**
-     * Compute attribute on MultiPrimitive.
-     * Also works for ModelPrimitive.
+     * Compute attribute on each primitive of a collection.
+     *
+     * @throws UnsupportedOperationException if an element of the collection 
is not a mesh primitive.
      */
-    public static MultiMeshPrimitive compute(MultiMeshPrimitive base, String 
attributeName, SampleSystem attributeSystem, DataType attributeType, 
Function<Point,Vector> valueGenerator) {
-        final List<org.apache.sis.geometries.mesh.MeshPrimitive> news = new 
ArrayList<>();
-        for (int i = 0, n = base.getNumGeometries(); i < n; i++) {
-            Geometry p = base.getGeometryN(i);
-            p = compute((MeshPrimitive)p, attributeName, attributeSystem, 
attributeType, valueGenerator);
-            news.add((org.apache.sis.geometries.mesh.MeshPrimitive) p);
+    public static GeometryCollection<MeshPrimitive> 
compute(GeometryCollection<?> base, String attributeName, SampleSystem 
attributeSystem, DataType attributeType, Function<Point,Vector> valueGenerator) 
{
+        final MeshPrimitive[] news = new 
MeshPrimitive[base.getNumGeometries()];
+        for (int i = 0; i < news.length; i++) {
+            final Geometry p = base.getGeometryN(i);
+            if (!(p instanceof MeshPrimitive mp)) {
+                throw new UnsupportedOperationException("Collection element is 
not a mesh primitive : " + p.getGeometryType());
+            }
+            news[i] = compute(mp, attributeName, attributeSystem, 
attributeType, valueGenerator);
         }
-
-        final org.apache.sis.geometries.mesh.MultiMeshPrimitive copy3d = new 
org.apache.sis.geometries.mesh.MultiMeshPrimitive(base.getCoordinateReferenceSystem());
-        copy3d.append(news);
-
-        return copy3d;
+        return 
GeometryFactory.DEFAULT.createGeometryCollection(base.getCoordinateReferenceSystem(),
 news);
     }
 
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
index 387630f3ab..10cb6cdbda 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
@@ -47,7 +47,6 @@ import 
org.apache.sis.geometries.internal.shared.ArrayDataPoints;
 import org.apache.sis.geometries.internal.shared.Vertex;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MeshPrimitiveVisitor;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.solid.Polyhedron;
 import org.apache.sis.geometries.surface.Polygon;
 import org.apache.sis.geometries.surface.PolyhedralSurface;
@@ -506,8 +505,8 @@ public final class GeometryProcessor {
     public Geometry compute(Geometry geom, String attributeName, SampleSystem 
attributeSystem, DataType attributeType, Function<Point,Vector> valueGenerator) 
{
         if (geom instanceof MeshPrimitive mp) {
             return ComputeAttribute.compute(mp, attributeName, 
attributeSystem, attributeType, valueGenerator);
-        } else if (geom instanceof MultiMeshPrimitive<?> mp) {
-            return ComputeAttribute.compute(mp, attributeName, 
attributeSystem, attributeType, valueGenerator);
+        } else if (geom instanceof GeometryCollection<?> c) {
+            return ComputeAttribute.compute(c, attributeName, attributeSystem, 
attributeType, valueGenerator);
         }
         throw new UnsupportedOperationException();
     }
@@ -528,8 +527,9 @@ public final class GeometryProcessor {
      *  can be a Primitive or MultiPrimitive
      */
     public Geometry toPrimitive(Geometry geom, Quantity<?> spacing, 
Quantity<?> offset) {
-        if (geom instanceof MeshPrimitive || geom instanceof 
MultiMeshPrimitive) {
+        if (geom instanceof MeshPrimitive) {
             //Does nothing, geometry is already a Primitive.
+            //Collections of primitives are handled by the collection case 
below.
             return geom;
         } else if (geom instanceof Point cdt) {
             return ToPrimitive.toPrimitive(cdt);
@@ -578,10 +578,11 @@ public final class GeometryProcessor {
             return Transform.transform(cdt, crs, transform);
         } else if (geom instanceof Polygon cdt) {
             return Transform.transform(cdt, crs, transform);
-        } else if (geom instanceof MultiMeshPrimitive<?> cdt) {
-            return Transform.transform(cdt, crs, transform);
         } else if (geom instanceof MeshPrimitive cdt) {
             return Transform.transform(cdt, crs, transform);
+        } else if (geom instanceof GeometryCollection<?> cdt) {
+            // Checked after mesh primitives because the points and lines 
primitives are also collections.
+            return Transform.transform(cdt, crs, transform);
         }
 
         throw new UnsupportedOperationException();
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
index f27490fb05..47539c4f32 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
@@ -37,7 +37,6 @@ import 
org.apache.sis.geometries.internal.shared.DefaultNurbSurface;
 import org.apache.sis.geometries.internal.shared.Vertex;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MeshPrimitiveVisitor;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.surface.PreparedTIN;
 import org.apache.sis.geometries.surface.Triangle;
 import org.apache.sis.maths.Array;
@@ -71,8 +70,8 @@ public final class Intersection {
      * Inherit attributes from TIN.
      * The POSITION attribute is ignored.
      */
-    private static void copyAttributes(PreparedTIN tin, MultiMeshPrimitive<?> 
primitives) {
-        for (MeshPrimitive primitive : primitives.getComponents()) {
+    private static void copyAttributes(PreparedTIN tin, GeometryCollection<? 
extends MeshPrimitive> primitives) {
+        for (MeshPrimitive primitive : primitives) {
             copyAttributes(tin, primitive);
         }
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
index b1579b30d6..459f13393a 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
@@ -17,7 +17,6 @@
 package org.apache.sis.geometries.operation;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -27,13 +26,13 @@ import javax.measure.Quantity;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.GeometryCollection;
 import org.apache.sis.geometries.Point;
 import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.curve.LineString;
 import org.apache.sis.geometries.internal.shared.ArrayDataPoints;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.operation.triangulate.EarClipping;
 import org.apache.sis.geometries.solid.Polyhedron;
 import org.apache.sis.geometries.surface.Polygon;
@@ -223,16 +222,11 @@ public final class ToPrimitive {
             }
             return primitive;
         } else {
-            final MultiMeshPrimitive<MeshPrimitive> mp = new 
MultiMeshPrimitive(geometry.getCoordinateReferenceSystem());
+            final List<MeshPrimitive> primitives = new 
ArrayList<>(numGeometries);
             for (int i = 0; i < numGeometries; i++) {
-                final Geometry p = 
toPrimitive(geometry.getGeometryN(i).asLine(spacing, offset));
-                if (p instanceof 
org.apache.sis.geometries.mesh.MultiMeshPrimitive<?> subm) {
-                    mp.append(subm.getComponents());
-                } else if (p instanceof 
org.apache.sis.geometries.mesh.MeshPrimitive cd){
-                    mp.append(Collections.singletonList(cd));
-                }
+                addPrimitives(primitives, 
toPrimitive(geometry.getGeometryN(i).asLine(spacing, offset)));
             }
-            return mp;
+            return 
GeometryFactory.DEFAULT.createGeometryCollection(geometry.getCoordinateReferenceSystem(),
 primitives.toArray(MeshPrimitive[]::new));
         }
     }
 
@@ -242,19 +236,30 @@ public final class ToPrimitive {
      * @param spacing maximal distance along a curve between two points of its 
line strip, or zero or {@code null}.
      * @param offset  maximal distance between a curve and its line strip, or 
zero or {@code null}.
      */
-    public static MultiMeshPrimitive toPrimitive(GeometryCollection geometry, 
Quantity<?> spacing, Quantity<?> offset)
+    public static GeometryCollection<MeshPrimitive> 
toPrimitive(GeometryCollection<?> geometry, Quantity<?> spacing, Quantity<?> 
offset)
             throws OperationException
     {
-        final MultiMeshPrimitive<MeshPrimitive> mp = new 
MultiMeshPrimitive(geometry.getCoordinateReferenceSystem());
+        final GeometryProcessor processor = new GeometryProcessor();
+        final List<MeshPrimitive> primitives = new ArrayList<>();
         for (int i = 0, n = geometry.getNumGeometries(); i < n; i++) {
-            final Geometry p = new 
GeometryProcessor().toPrimitive(geometry.getGeometryN(i), spacing, offset);
-            if (p instanceof 
org.apache.sis.geometries.mesh.MultiMeshPrimitive<?> subm) {
-                mp.append(subm.getComponents());
-            } else if (p instanceof 
org.apache.sis.geometries.mesh.MeshPrimitive cd){
-                mp.append(Collections.singletonList(cd));
+            addPrimitives(primitives, 
processor.toPrimitive(geometry.getGeometryN(i), spacing, offset));
+        }
+        return 
GeometryFactory.DEFAULT.createGeometryCollection(geometry.getCoordinateReferenceSystem(),
 primitives.toArray(MeshPrimitive[]::new));
+    }
+
+    /**
+     * Adds the given primitive, or the primitives of the given collection, to 
the list.
+     * Collections of collections are flattened.
+     */
+    private static void addPrimitives(List<MeshPrimitive> primitives, Geometry 
geometry) {
+        // Checked first because the points and lines primitives are also 
collections.
+        if (geometry instanceof MeshPrimitive p) {
+            primitives.add(p);
+        } else if (geometry instanceof GeometryCollection<?> c) {
+            for (Geometry element : c) {
+                addPrimitives(primitives, element);
             }
         }
-        return mp;
     }
 
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Transform.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Transform.java
index 72f5ddf72f..bbfd21e2a3 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Transform.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Transform.java
@@ -24,12 +24,13 @@ import 
org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.geometries.DataPoints;
+import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
 import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.curve.LinearRing;
 import org.apache.sis.geometries.internal.shared.ArrayDataPoints;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.surface.Polygon;
 import org.apache.sis.geometries.surface.Triangle;
 import org.apache.sis.maths.Array;
@@ -103,14 +104,19 @@ public final class Transform {
         return GeometryFactory.DEFAULT.createPolygon(exterior, interiors);
     }
 
-    public static MultiMeshPrimitive<?> transform(MultiMeshPrimitive<?> mp, 
CoordinateReferenceSystem crs, MathTransform transform) throws 
OperationException {
-        final MultiMeshPrimitive<?> copy = new MultiMeshPrimitive<>(crs);
-        final List<MeshPrimitive> primitives = new ArrayList<>();
-        for (MeshPrimitive p : mp.getComponents()) {
-            primitives.add(transform(p, crs, transform));
+    /**
+     * Transform each element of the collection.
+     * The elements are transformed by {@link 
GeometryProcessor#transform(Geometry, CoordinateReferenceSystem, 
MathTransform)}.
+     *
+     * @return a new collection with the same element type.
+     */
+    public static GeometryCollection<Geometry> transform(GeometryCollection<?> 
collection, CoordinateReferenceSystem crs, MathTransform transform) throws 
OperationException {
+        final GeometryProcessor processor = new GeometryProcessor();
+        final Geometry[] elements = new 
Geometry[collection.getNumGeometries()];
+        for (int i = 0; i < elements.length; i++) {
+            elements[i] = processor.transform(collection.getGeometryN(i), crs, 
transform);
         }
-        copy.append(primitives);
-        return copy;
+        return 
GeometryFactory.DEFAULT.createGeometryCollection(collection.getElementType(), 
crs, elements);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/extrude/TerrainExtruder.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/extrude/TerrainExtruder.java
index 26a73a4ad1..9bfe4bfc8a 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/extrude/TerrainExtruder.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/extrude/TerrainExtruder.java
@@ -30,6 +30,7 @@ import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.index.quadtree.Quadtree;
 import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.GeometryCollection;
+import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.curve.LineString;
 import org.apache.sis.geometries.internal.shared.Planar;
 import org.apache.sis.geometries.internal.shared.Planar.DoubleList;
@@ -37,7 +38,6 @@ import 
org.apache.sis.geometries.internal.shared.Planar.IntList;
 import org.apache.sis.geometries.internal.shared.Planar.XY;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MeshPrimitiveIndexes;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.operation.Intersection;
 import org.apache.sis.geometries.operation.OperationException;
 import org.apache.sis.geometries.operation.ProcessorUtils;
@@ -268,7 +268,7 @@ public final class TerrainExtruder {
      *
      * @return new meshes, with positions only
      */
-    public MultiMeshPrimitive<MeshPrimitive.Triangles> getResult() {
+    public GeometryCollection<MeshPrimitive.Triangles> getResult() {
         final IntList terrainFaces = new IntList(), roofFaces = new IntList(), 
wallFaces = new IntList();
         for (int i = 0; i < nbTriangle; i++) {
             if (triangles[i*3] >= 0) {
@@ -302,19 +302,20 @@ public final class TerrainExtruder {
                 }
             }
         }
-        final MultiMeshPrimitive<MeshPrimitive.Triangles> result = new 
MultiMeshPrimitive<>(terrain.getCoordinateReferenceSystem());
+        final List<MeshPrimitive.Triangles> parts = new ArrayList<>(3);
         if (separateParts) {
             for (IntList faces : List.of(terrainFaces, roofFaces, wallFaces)) {
                 final MeshPrimitive.Triangles part = toPrimitive(faces);
-                if (part != null) result.append(List.of(part));
+                if (part != null) parts.add(part);
             }
         } else {
             terrainFaces.add(roofFaces.toArray());
             terrainFaces.add(wallFaces.toArray());
             final MeshPrimitive.Triangles part = toPrimitive(terrainFaces);
-            if (part != null) result.append(List.of(part));
+            if (part != null) parts.add(part);
         }
-        return result;
+        return 
GeometryFactory.DEFAULT.createGeometryCollection(terrain.getCoordinateReferenceSystem(),
+                parts.toArray(MeshPrimitive.Triangles[]::new));
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/summary.adoc
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/summary.adoc
index 387feed3b6..dfa764184d 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/summary.adoc
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/summary.adoc
@@ -5,18 +5,19 @@ Overview of the geometry types defined in the 
`org.apache.sis.geometry` module
 
 == Interface hierarchy
 
-The 56 interfaces of the `Geometry` hierarchy are shown below, split by theme.
-`Geometry` and most of its subtypes are `sealed`, so their permitted subtypes 
are fixed
-at compile time and a `switch` over them can be exhaustive.
+The 55 interfaces of the `Geometry` hierarchy are shown below, split by theme.
+All of them are `sealed`, so their permitted subtypes are fixed at compile time
+and a `switch` over them can be exhaustive.
 Implementation classes are omitted; see the `internal.shared` package.
+A few analytic shapes (`BBox`, `OBBox`, `Plane`, `HyperPlane`, `Frustrum`) are 
classes
+implementing `Geometry` directly, without an interface of their own.
 
-Colours: white is a `sealed` type; *yellow* is `non-sealed`, still open for 
extension
-because it has no implementation yet; *blue-grey* is a type detailed in 
another diagram
+Colours: white is a type of this diagram; *blue-grey* is a type detailed in 
another diagram
 here, repeated only to anchor it; *grey* is a type declared outside this 
module.
 
 === Core types
 
-The spine of the hierarchy. `Geometry` splits into a single `Primitive`, a 
`GeometryCollection` of geometries, an analytic `OrientedGeometry`, the `Empty` 
set, an extruded `Prism` and a GPU-oriented `MeshPrimitive` batch. Each leaf 
below is detailed in one of the following diagrams.
+The spine of the hierarchy. `Geometry` splits into a single `Primitive`, a 
`GeometryCollection` of geometries, the `Empty` set, an extruded `Prism` and a 
GPU-oriented `MeshPrimitive` batch. Each leaf below is detailed in one of the 
following diagrams.
 
 [plantuml,geometry-core,svg]
 ....
@@ -27,10 +28,6 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
@@ -46,17 +43,15 @@ interface Geometry
 interface "GeometryCollection<T extends Geometry>" as GeometryCollection
 interface MeshPrimitive
 interface Orientable
-interface OrientedGeometry
 interface Point
 interface Primitive
-interface Prism <<open>>
+interface Prism
 interface Solid
 interface Surface
 
 Geometry <|-- Empty
 Geometry <|-- GeometryCollection
 Geometry <|-- MeshPrimitive
-Geometry <|-- OrientedGeometry
 Geometry <|-- Primitive
 Geometry <|-- Prism
 Orientable <|-- Curve
@@ -69,7 +64,8 @@ Primitive <|-- Solid
 
 === Curves
 
-Subtypes of `Curve` that interpolate a stored sequence of points, plus the 
conic family.
+Subtypes of `Curve` that interpolate a stored sequence of points, plus the 
conic family
+and the curves defined by a construction (geodesic, rhumb line, ray, offset).
 
 [plantuml,geometry-curves,svg]
 ....
@@ -80,10 +76,6 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
@@ -96,17 +88,19 @@ skinparam interface<<external>> {
 interface Arc
 interface ArcByBulge
 interface ArcByCenterPoint
-interface Circle <<open>>
+interface Circle
 interface CircularString
-interface Clothoid <<open>>
+interface Clothoid
 interface CompoundCurve
 interface Conic
 interface Curve <<context>>
-interface EllipticArc <<open>>
+interface EllipticArc
 interface Geodesic
+interface Line
 interface LineString
 interface LinearRing
-interface OffsetCurve <<open>>
+interface OffsetCurve
+interface Ray
 interface Rhumb
 interface Spiral
 
@@ -119,8 +113,10 @@ Curve <|-- CircularString
 Curve <|-- CompoundCurve
 Curve <|-- Conic
 Curve <|-- Geodesic
+Curve <|-- Line
 Curve <|-- LineString
 Curve <|-- OffsetCurve
+Curve <|-- Ray
 Curve <|-- Rhumb
 Curve <|-- Spiral
 LineString <|-- LinearRing
@@ -141,10 +137,6 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
@@ -155,8 +147,8 @@ skinparam interface<<external>> {
 }
 
 interface BSplineCurve
-interface Bezier <<open>>
-interface CubicSpline <<open>>
+interface Bezier
+interface CubicSpline
 interface Curve <<context>>
 interface "FunctionCurve<T extends FunctionArc>" as FunctionCurve
 interface NurbCurve
@@ -178,7 +170,7 @@ SplineCurve <|-- PolynomialSpline
 
 === Surfaces
 
-Subtypes of `Surface`: polygonal, patch-based and parametric.
+Subtypes of `Surface`: polygonal, patch-based, parametric and implicit shapes.
 
 [plantuml,geometry-surfaces,svg]
 ....
@@ -189,10 +181,6 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
@@ -203,13 +191,17 @@ skinparam interface<<external>> {
 }
 
 interface BSplineSurface
-interface BilinearGrid <<open>>
+interface BilinearGrid
+interface Capsule
 interface CurvePolygon
+interface Cylinder
+interface Ellipsoid
 interface NurbSurface
 interface ParametricCurveSurface
 interface Polygon
 interface "PolyhedralSurface<T extends Polygon>" as PolyhedralSurface
 interface PreparedTIN
+interface Sphere
 interface Surface <<context>>
 interface TIN
 interface Triangle
@@ -220,10 +212,14 @@ interface ReferenceSystem <<external>>
 BSplineSurface <|-- NurbSurface
 ParametricCurveSurface <|-- BSplineSurface
 ParametricCurveSurface <|-- BilinearGrid
+ParametricCurveSurface <|-- Ellipsoid
+ParametricCurveSurface <|-- Sphere
 Polygon <|-- Triangle
 PolyhedralSurface <|-- TriangulatedSurface
 ReferenceSystem <|-- ParametricCurveSurface
+Surface <|-- Capsule
 Surface <|-- CurvePolygon
+Surface <|-- Cylinder
 Surface <|-- ParametricCurveSurface
 Surface <|-- Polygon
 Surface <|-- PolyhedralSurface
@@ -245,10 +241,6 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
@@ -258,12 +250,12 @@ skinparam interface<<external>> {
     BorderColor #999999
 }
 
-interface BSolidSpline <<open>>
+interface BSplineSolid
 interface ParametricCurveSolid
 interface Polyhedron
 interface Solid <<context>>
 
-ParametricCurveSolid <|-- BSolidSpline
+ParametricCurveSolid <|-- BSplineSolid
 Solid <|-- ParametricCurveSolid
 Solid <|-- Polyhedron
 @enduml
@@ -271,7 +263,12 @@ Solid <|-- Polyhedron
 
 === Collections
 
-Subtypes of `GeometryCollection`, each restricting its elements to one 
geometry kind. `ProductCurve` appears here because it is both a collection and 
a `Curve`.
+`GeometryCollection` has a single interface subtype, `ProductCurve`, which is 
both a collection and a `Curve`.
+Multi-geometries have no interface of their own: they are `GeometryCollection` 
instances whose
+`getElementType()` restricts the elements, created by the `GeometryFactory` 
methods
+`createMultiPoint`, `createMultiCurve`, `createMultiLineString`, 
`createMultiSurface`, `createMultiPolygon`
+and `createMultiPolyhedron`. Several mesh primitives are grouped in a 
`GeometryCollection<MeshPrimitive>`.
+The `Points` and `Lines` mesh primitives are themselves collections of points 
and of line segments.
 
 [plantuml,geometry-collections,svg]
 ....
@@ -282,39 +279,20 @@ skinparam interface {
     BackgroundColor #FDFDFD
     BorderColor #4A6785
 }
-skinparam interface<<open>> {
-    BackgroundColor #FFF6E0
-    BorderColor #C9A227
-}
 skinparam interface<<context>> {
     BackgroundColor #EEF3F8
     BorderColor #93A9C0
 }
-skinparam interface<<external>> {
-    BackgroundColor #F0F0F0
-    BorderColor #999999
-}
 
 interface Curve <<context>>
 interface "GeometryCollection<T extends Geometry>" as GeometryCollection 
<<context>>
-interface "MultiCurve<T extends Curve>" as MultiCurve
-interface MultiLineString
-interface "MultiPoint<T extends Point>" as MultiPoint
-interface MultiPolygon
-interface MultiPolyhedron
-interface "MultiSurface<T extends Surface>" as MultiSurface
-interface ProductCurve <<open>>
+interface ProductCurve
 
 Curve <|-- ProductCurve
-GeometryCollection <|-- MultiCurve
-GeometryCollection <|-- MultiPoint
-GeometryCollection <|-- MultiPolyhedron : Polyhedron
-GeometryCollection <|-- MultiSurface
 GeometryCollection <|-- ProductCurve : Curve
-MultiCurve <|-- MultiLineString : LineString
-MultiSurface <|-- MultiPolygon : Polygon
 @enduml
 ....
+
 == Format mapping
 
 [cols="2,3,2,2,2,2,2,6", options="header"]
@@ -415,6 +393,15 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |`LineString` that is both closed and simple.
 
+|Line
+|`curve`
+|—
+|—
+|—
+|—
+|—
+|Curve with linear interpolation between consecutive points, each pair 
defining a line segment.
+
 |CompoundCurve
 |`curve`
 |—
@@ -432,6 +419,7 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |—
 |Curve derived from a base curve by applying a lateral offset distance.
+The offset of a `LineString` is computed by `DefaultOffsetLineString`.
 
 |ProductCurve
 |`curve`
@@ -443,7 +431,7 @@ MultiSurface <|-- MultiPolygon : Polygon
 |Collection of curves treated as one continuous curve.
 
 |Ray
-|—
+|`curve`
 |—
 |—
 |—
@@ -461,7 +449,7 @@ MultiSurface <|-- MultiPolygon : Polygon
 |Curve of constant bearing (loxodrome) crossing all meridians at the same 
angle.
 
 |Bearing
-|—
+|`cs`
 |—
 |—
 |—
@@ -592,14 +580,14 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |Solid defined by a parametric function.
 
-|BSolidSpline
+|BSplineSolid
 |`solid`
 |—
 |—
 |—
 |—
 |—
-|`ParametricCurveSolid` defined by a B-spline volumetric representation.
+|`ParametricCurveSolid` defined by a B-spline volumetric representation (ISO 
19107 `BSolidSpline`).
 
 |ConstructiveSolidGeometry
 |`csg`
@@ -610,29 +598,20 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |Placeholder type for boolean-combined solids (union, intersection, 
difference).
 
-// --- Oriented / analytic shapes ------------------------------------------
-8+| *Oriented / analytic shapes*
-
-|OrientedGeometry
-|—
-|—
-|—
-|—
-|—
-|—
-|Base type for named analytic shapes carrying their own rotation and 
translation.
+// --- Analytic shapes ----------------------------------------------------
+8+| *Analytic shapes*
 
 |Cylinder
-|`solid`
+|`surface`
 |—
 |—
 |—
 |—
 |cylinder, cone
-|Oriented geometry describing a tube shape with a radius and a height.
+|Surface along the axis joining two positions, with a radius at each end. A 
cone when one radius is zero.
 
 |Sphere
-|`solid`
+|`surface`
 |—
 |—
 |—
@@ -640,6 +619,15 @@ MultiSurface <|-- MultiPolygon : Polygon
 |sphere
 |Parametric surface made of the set of points equidistant from a center.
 
+|Ellipsoid
+|`surface`
+|—
+|—
+|—
+|—
+|—
+|Parametric surface defined by a center and one semi-axis length per axis.
+
 |Frustrum
 |`solid`
 |—
@@ -647,16 +635,16 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |—
 |—
-|Oriented geometry describing a cone or pyramid with its top cut off.
+|Space delimited by one or more planes, used for example to clip what lies 
outside a camera view.
 
 |Capsule
-|`solid`
+|`surface`
 |—
 |—
 |—
 |—
 |—
-|Oriented geometry describing a cylinder capped with two hemispherical ends.
+|Surface of a cylinder capped with two hemispherical ends, defined by the two 
capping spheres.
 
 |Plane
 |—
@@ -665,7 +653,7 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |—
 |quad
-|Oriented geometry describing a flat surface.
+|Plane centered on a position and perpendicular to a normal, optionally with 
finite extents.
 
 |HyperPlane
 |—
@@ -674,7 +662,7 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |—
 |—
-|Oriented geometry dividing the coordinate space in two half-spaces.
+|Plane dividing the coordinate space in two half-spaces, defined by a position 
and a normal.
 
 |BBox
 |—
@@ -706,59 +694,59 @@ MultiSurface <|-- MultiPolygon : Polygon
 |—
 |Collection of an arbitrary number of geometric objects sharing the same 
spatial reference system.
 
-|MultiPoint
-|`point`
+|GeometryCollection<Point>
+|—
 |MultiPoint
 |MultiPoint
 |MultiPoint
 |—
 |—
-|0-dimensional `GeometryCollection` whose elements are restricted to `Point`.
+|MultiPoint: collection of element type `POINT`, created by 
`GeometryFactory.createMultiPoint`. `DefaultMultiPoint` stores all the points 
in a single `DataPoints`.
 
-|MultiCurve
-|`curve`
+|GeometryCollection<Curve>
+|—
 |MultiCurve
 |—
 |MultiCurve
 |—
 |—
-|1-dimensional `GeometryCollection` whose elements are `Curve`.
+|MultiCurve: collection of element type `CURVE`, created by 
`GeometryFactory.createMultiCurve`.
 
-|MultiLineString
-|`curve`
+|GeometryCollection<LineString>
+|—
 |MultiLineString
 |MultiLineString
 |MultiLineString
 |—
 |—
-|`MultiCurve` whose elements are `LineString`.
+|MultiLineString: collection of element type `LINESTRING`, created by 
`GeometryFactory.createMultiLineString`.
 
-|MultiSurface
-|`surface`
+|GeometryCollection<Surface>
+|—
 |MultiSurface
 |—
 |MultiSurface
 |—
 |—
-|2-dimensional `GeometryCollection` whose elements are `Surface`.
+|MultiSurface: collection of element type `SURFACE`, created by 
`GeometryFactory.createMultiSurface`.
 
-|MultiPolygon
-|`surface`
+|GeometryCollection<Polygon>
+|—
 |MultiPolygon
 |MultiPolygon
 |MultiPolygon
 |—
 |—
-|`MultiSurface` whose elements are `Polygon`.
+|MultiPolygon: collection of element type `POLYGON`, created by 
`GeometryFactory.createMultiPolygon`.
 
-|MultiPolyhedron
-|`solid`
+|GeometryCollection<Polyhedron>
+|—
 |—
 |—
 |MultiPolyhedron
 |—
 |—
-|Arbitrary aggregation of `Polyhedron` solids.
+|MultiPolyhedron: collection of element type `POLYHEDRON`, created by 
`GeometryFactory.createMultiPolyhedron`.
 
 // --- Mesh -----------------------------------------------------------------
 8+| *Mesh*
@@ -772,14 +760,14 @@ MultiSurface <|-- MultiPolygon : Polygon
 |triangle
 |GPU-like batch of simple geometries (points, lines or triangles), inspired by 
glTF 2.0.
 
-|MultiMeshPrimitive
-|`mesh`
+|GeometryCollection<MeshPrimitive>
+|—
 |—
 |—
 |—
 |Mesh
 |—
-|`GeometryCollection` of `MeshPrimitive` batches.
+|Several `MeshPrimitive` batches, like the primitives of a glTF mesh.
 
 // --- Regular polyhedra ------------------------------------------------
 8+| *Regular polyhedra*
@@ -1109,3 +1097,126 @@ MultiSurface <|-- MultiPolygon : Polygon
 |Implicit surface extracted from a scalar field at given iso-values 
(marching-cubes style)
 
 |===
+
+== JTS backend
+
+The methods below still delegate their computation to the 
https://locationtech.github.io/jts/[JTS Topology Suite]
+(packages `org.locationtech.jts`). The geometry is viewed as a JTS geometry by 
`adapter.JTSAdapter`,
+which supports only points, line strings, linear rings, polygons and 
collections of them:
+other geometry types make those methods throw `UnsupportedOperationException` 
or `IllegalArgumentException`.
+A geometry computed by JTS is converted back with the coordinate reference 
system of the operand,
+but its points keep no attribute other than the position.
+
+The conversions between both models (`adapter.JTSAdapter`, `Geometries.asJTS`, 
`Geometries.fromJTS`,
+`adapter.Wrapper.asJTS`) are not listed: converting is their purpose.
+
+[cols="3,3,3,5", options="header"]
+|===
+|Method |Implemented in |Computed without JTS |Notes
+
+4+| *Spatial operations*, declared by `Geometry` and implemented by 
`operation.GeometryProcessor`
+
+|`buffer`
+|`GeometryProcessor.buffer`
+|Empty geometry.
+|Attributes of the result are lost.
+
+|`convexHull`
+|`GeometryProcessor.convexHull`
+|Empty geometry.
+|Attributes of the result are lost.
+
+|`difference`, `symDifference`, `union`
+|`GeometryProcessor`
+|Empty operands.
+|Attributes of the result are lost.
+
+|`intersection`
+|`GeometryProcessor.intersection`
+|Empty operands; mesh `Triangles` with mesh `Points` or `Lines` 
(`operation.Intersection`).
+|Attributes of the result are lost, except in the mesh cases.
+
+|`distance`
+|`GeometryProcessor.distance`
+|Empty operands; point to point (`operation.Distance`).
+|
+
+|`contains`
+|`GeometryProcessor.contains`
+|Empty operands; polygon containing a point (`operation.Contains`).
+|
+
+|`crosses`, `disjoint`, `equal`, `intersects`, `overlaps`, `touches`, 
`within`, `withinDistance`
+|`GeometryProcessor`
+|Empty operands.
+|
+
+|`relate`
+|`GeometryProcessor.relate`
+|Empty operands.
+|The DE-9IM matrix of non-empty operands is computed by JTS.
+
+4+| *Measures and properties*, implemented by the classes of `internal.shared`
+
+|`getLength`
+|`DefaultLineString`, also inherited by `DefaultLinearRing`
+|—
+|Other curves compute their length natively.
+
+|`getPerimeter`
+|`DefaultPolygon`, also inherited by `DefaultTriangle`
+|—
+|
+
+|`getBoundary`
+|`DefaultPolygon`, `DefaultGeometryCollection`, `DefaultMultiPoint`
+|Empty geometry.
+|`DefaultTriangle` inherits the JTS version of `DefaultPolygon` instead of the 
`Triangle` default, which returns the exterior ring.
+
+|`isSimple`, `isValid`
+|`DefaultLineString`, `DefaultPolygon`, `DefaultGeometryCollection`, 
`DefaultMultiPoint`
+|—
+|Also inherited by `DefaultLinearRing` and `DefaultTriangle`.
+
+|`getLength`, `getCentroid`, `isSimple`, `isValid`
+|`DefaultLine`
+|Empty geometry (`getCentroid`).
+|Always unsupported: `JTSAdapter` cannot convert a `Line`, which is not a 
`LineString`.
+The `Curve` default of `getCentroid` would compute it natively.
+
+4+| *SQL/MM functions*, implemented by `adapter.Wrapper`
+
+|`ST_Simplify`
+|`Wrapper.operationSameCRS`
+|—
+|JTS `DouglasPeuckerSimplifier`.
+
+|`ST_SimplifyPreserveTopology`
+|`Wrapper.operationSameCRS`
+|—
+|JTS `TopologyPreservingSimplifier`.
+
+4+| The other SQL/MM functions call the `Geometry` methods above.
+
+4+| *JTS classes used internally*
+
+|`ISOBand.create`
+|`operation.spatialanalysis2d.ISOBand`
+|Band polygons of each triangle.
+|Returns JTS polygons. The bands are merged with JTS `union()`, retried after 
`buffer(0)` on topology errors.
+
+|`ISOLine.createMesh`
+|`operation.spatialanalysis2d.ISOLine`
+|Everything.
+|JTS `Coordinate` is used only to hold interpolated positions.
+
+|`PreparedTIN.getPatches`
+|`surface.PreparedTIN`
+|Everything.
+|The triangles are indexed in a JTS `Quadtree`, queried with JTS `Envelope`.
+
+|`TerrainExtruder`
+|`operation.extrude.TerrainExtruder`
+|Everything.
+|The terrain triangles are indexed in a JTS `Quadtree`, queried with JTS 
`Envelope`.
+|===
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/GeometryCollectionTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/GeometryCollectionTest.java
index 6f2f2c2fe6..3af5e652f6 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/GeometryCollectionTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/GeometryCollectionTest.java
@@ -16,8 +16,16 @@
  */
 package org.apache.sis.geometries;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
 // Test dependencies
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -58,6 +66,32 @@ public class GeometryCollectionTest extends GeometryTest {
     public void testGetNumGeometries() {
     }
 
+    /**
+     * Tests that the iterator and the stream return each element once, in 
order.
+     */
+    @Test
+    public void testIterator() {
+        final Geometry point = GeometryFactory.DEFAULT.createPoint(CRS_2D, 1, 
2);
+        final Geometry line  = GeometryFactory.DEFAULT.createLineString(
+                GeometryFactory.DEFAULT.createDataPoints(CRS_2D, 0,0, 10,0));
+        final GeometryCollection<Geometry> collection = 
GeometryFactory.DEFAULT.createGeometryCollection(CRS_2D, point, line);
+        final List<Geometry> iterated = new ArrayList<>();
+        for (Geometry g : collection) {
+            iterated.add(g);
+        }
+        assertEquals(List.of(point, line), iterated);
+
+        final List<Geometry> remaining = new ArrayList<>();
+        final Iterator<Geometry> it = collection.iterator();
+        assertSame(point, it.next());
+        it.forEachRemaining(remaining::add);
+        assertEquals(List.of(line), remaining);
+        assertFalse(it.hasNext());
+        assertThrows(NoSuchElementException.class, it::next);
+
+        assertEquals(List.of(point, line), collection.stream().toList());
+    }
+
     @Test
     @Disabled("Not implemented yet.")
     public void testGetGeometryN() {
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/JTSAdapterTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/JTSAdapterTest.java
new file mode 100644
index 0000000000..f901bbaabd
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/adapter/JTSAdapterTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.adapter;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.apache.sis.geometries.DataPointsType;
+import org.apache.sis.geometries.Geometries;
+import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.curve.LineString;
+import org.apache.sis.referencing.CommonCRS;
+
+// Test dependencies
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
+
+
+/**
+ * Tests {@link JTSAdapter}.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class JTSAdapterTest {
+
+    private static final GeometryFactory GF = new GeometryFactory();
+
+    /**
+     * Tests that the CRS of a view over a JTS geometry can be relabeled,
+     * without modifying the coordinates nor the JTS geometry.
+     */
+    @Test
+    public void testRelabelView() {
+        final org.locationtech.jts.geom.LineString jts = 
GF.createLineString(new Coordinate[] {
+                new Coordinate(1, 2), new Coordinate(3, 4)});
+        final Geometry view = JTSAdapter.fromJTS(jts, false);
+        assertInstanceOf(LineString.class, view);
+        
assertTrue(Geometries.isUndefined(view.getCoordinateReferenceSystem()));
+
+        
view.setCoordinateReferenceSystem(CommonCRS.WGS84.normalizedGeographic());
+        assertEquals(CommonCRS.WGS84.normalizedGeographic(), 
view.getCoordinateReferenceSystem());
+        assertEquals(CommonCRS.WGS84.normalizedGeographic(),
+                
view.getDataPoints().getType().getAttributeSystem(DataPointsType.ATT_POSITION).getCoordinateReferenceSystem());
+        assertArrayEquals(new double[] {1, 2, 3, 4},
+                
view.getDataPoints().getAttributeArray(DataPointsType.ATT_POSITION).toArrayDouble());
+        assertNull(jts.getUserData());
+
+        // The view still reflects the JTS coordinates.
+        jts.getCoordinateSequence().setOrdinate(0, 0, 10);
+        assertEquals(10, view.getDataPoints().getPosition(0).get(0));
+
+        assertThrows(IllegalArgumentException.class,
+                () -> 
view.setCoordinateReferenceSystem(CommonCRS.WGS84.geographic3D()));
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
index 71d17b02b0..9f83e4597e 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
@@ -35,7 +35,6 @@ import org.apache.sis.geometries.solid.polyhedron.Hexahedron;
 import org.apache.sis.geometries.solid.polyhedron.Icosahedron;
 import org.apache.sis.geometries.solid.polyhedron.Tetrahedron;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.maths.Array;
 import org.apache.sis.measure.NumberRange;
@@ -105,8 +104,8 @@ public class ToPrimitiveTest {
         final var arc2 = f.createCircularString(f.createDataPoints(CRS2D, 
20,0, 25,5, 30,0));
         final Geometry result = new 
GeometryProcessor().toPrimitive(f.createMultiCurve(CRS2D, arc1, arc2),
                 Quantities.create(1, Units.DEGREE), null);
-        assertTrue(result instanceof MultiMeshPrimitive);
-        for (final Object component : ((MultiMeshPrimitive<?>) 
result).getComponents()) {
+        assertTrue(result instanceof GeometryCollection);
+        for (final Object component : (GeometryCollection<?>) result) {
             final Array positions = ((MeshPrimitive) component).getPositions();
             // A half circle of radius 5 is 15.7 long, so a spacing of 1 needs 
at least 16 segments.
             assertTrue(positions.getLength() >= 17, "Spacing not applied: " + 
positions.getLength() + " points.");
@@ -124,8 +123,8 @@ public class ToPrimitiveTest {
                 f.createLineString(f.createDataPoints(CRS2D, 0,5, 10,5)));
         assertEquals(MeshPrimitive.Type.LINES, ((MeshPrimitive) new 
GeometryProcessor().toPrimitive(lines, null, null)).getType());
         final Geometry result = new GeometryProcessor().toPrimitive(lines, 
Quantities.create(2.5, Units.DEGREE), null);
-        assertTrue(result instanceof MultiMeshPrimitive);
-        for (final Object component : ((MultiMeshPrimitive<?>) 
result).getComponents()) {
+        assertTrue(result instanceof GeometryCollection);
+        for (final Object component : (GeometryCollection<?>) result) {
             assertEquals(5, ((MeshPrimitive) 
component).getPositions().getLength());
         }
     }
@@ -499,8 +498,8 @@ public class ToPrimitiveTest {
             final GeometryCollection<LineString> mlines = 
GeometryFactory.DEFAULT.createMultiLineString(line1, line2);
 
             final Geometry result = new 
GeometryProcessor().toPrimitive(mlines, null, null);
-            assertTrue(result instanceof MultiMeshPrimitive);
-            final MultiMeshPrimitive mp = (MultiMeshPrimitive) result;
+            assertTrue(result instanceof GeometryCollection);
+            final GeometryCollection<?> mp = (GeometryCollection<?>) result;
             assertEquals(2, mp.getNumGeometries());
             assertTrue(mp.getGeometryN(0) instanceof MeshPrimitive);
             assertTrue(mp.getGeometryN(1) instanceof MeshPrimitive);
@@ -594,7 +593,7 @@ public class ToPrimitiveTest {
         result = processor.toPrimitive(f.createMultiLineString(CRS2D,
                 f.createLineString(attributedPoints(0,0, 1,1, 2,0)),
                 f.createLineString(attributedPoints(3,0, 4,1, 5,0))), null, 
null);
-        final MultiMeshPrimitive<?> multi = 
assertInstanceOf(MultiMeshPrimitive.class, result);
+        final GeometryCollection<?> multi = 
assertInstanceOf(GeometryCollection.class, result);
         assertEquals(2, multi.getNumGeometries());
         for (int i = 0; i < 2; i++) {
             assertAttributes((MeshPrimitive) multi.getGeometryN(i), 3);
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/TransformTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/TransformTest.java
index b1655ccc17..f9a27ae8fc 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/TransformTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/TransformTest.java
@@ -21,8 +21,9 @@ import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
 import org.apache.sis.geometries.Geometry;
+import org.apache.sis.geometries.GeometryCollection;
+import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.maths.NDArrays;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
@@ -122,8 +123,8 @@ public class TransformTest extends AbstractD9IMTest {
 
     @Test
     public void MultiPrimitive() throws FactoryException, TransformException {
-        final MultiMeshPrimitive mp = new 
MultiMeshPrimitive(createPrimitive());
-        final MultiMeshPrimitive result = (MultiMeshPrimitive) new 
GeometryProcessor().transform(mp, CRS_TARGET, TRANSFORM);
+        final GeometryCollection<MeshPrimitive> mp = 
GeometryFactory.DEFAULT.createGeometryCollection(createPrimitive());
+        final GeometryCollection<?> result = (GeometryCollection<?>) new 
GeometryProcessor().transform(mp, CRS_TARGET, TRANSFORM);
         assertEquals(1, result.getNumGeometries());
         testPrimitive((MeshPrimitive) result.getGeometryN(0));
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/extrude/TerrainExtruderTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/extrude/TerrainExtruderTest.java
index e6dfc22cc0..770096fc59 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/extrude/TerrainExtruderTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/extrude/TerrainExtruderTest.java
@@ -25,10 +25,10 @@ import java.util.Set;
 import java.util.function.DoubleBinaryOperator;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.Geometries;
+import org.apache.sis.geometries.GeometryCollection;
 import org.apache.sis.geometries.GeometryFactory;
 import org.apache.sis.geometries.curve.LinearRing;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
-import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import 
org.apache.sis.geometries.operation.extrude.TerrainExtruder.HeightReference;
 import org.apache.sis.geometries.surface.Polygon;
 import org.apache.sis.maths.Maths;
@@ -118,7 +118,7 @@ public class TerrainExtruderTest {
         final TerrainExtruder extruder = new TerrainExtruder(terrain());
         extruder.setSeparateParts(true);
         extruder.appendFlatConstraint(polygon(FOOTPRINT), 20, 23, 
HeightReference.ABSOLUTE, true);
-        final MultiMeshPrimitive<MeshPrimitive.Triangles> result = 
extruder.getResult();
+        final GeometryCollection<MeshPrimitive.Triangles> result = 
extruder.getResult();
         assertEquals(3, result.getNumGeometries());
         final Mesh ground = new Mesh(result.getGeometryN(0));
         final Mesh roof   = new Mesh(result.getGeometryN(1));
@@ -322,7 +322,7 @@ public class TerrainExtruderTest {
         final double[] positions;
         final int[] index;
 
-        Mesh(final MultiMeshPrimitive<MeshPrimitive.Triangles> result) {
+        Mesh(final GeometryCollection<MeshPrimitive.Triangles> result) {
             this(single(result));
         }
 
@@ -331,7 +331,7 @@ public class TerrainExtruderTest {
             index = mesh.getIndex().toArrayInt();
         }
 
-        private static MeshPrimitive.Triangles single(final 
MultiMeshPrimitive<MeshPrimitive.Triangles> result) {
+        private static MeshPrimitive.Triangles single(final 
GeometryCollection<MeshPrimitive.Triangles> result) {
             assertEquals(1, result.getNumGeometries(), "Parts are not 
separated by default.");
             return result.getGeometryN(0);
         }

Reply via email to