You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ma...@apache.org on 2020/12/18 13:01:23 UTC

[commons-geometry] branch master updated: GEOMETRY-108: adding BoundaryList classes

This is an automated email from the ASF dual-hosted git repository.

mattjuntunen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git


The following commit(s) were added to refs/heads/master by this push:
     new 29b20e9  GEOMETRY-108: adding BoundaryList classes
29b20e9 is described below

commit 29b20e9cb6e0577c2c5754dc7089b1cafe578200
Author: Matt Juntunen <ma...@apache.org>
AuthorDate: Mon Dec 14 21:20:28 2020 -0500

    GEOMETRY-108: adding BoundaryList classes
---
 .../AbstractConvexHyperplaneBoundedRegion.java     |  3 +-
 .../geometry/core/partitioning/BoundaryList.java   | 82 ++++++++++++++++++++
 .../AbstractConvexHyperplaneBoundedRegionTest.java |  3 +-
 .../core/partitioning/BoundaryListTest.java        | 87 ++++++++++++++++++++++
 .../geometry/euclidean/threed/BoundaryList3D.java  | 47 ++++++++++++
 .../euclidean/threed/BoundarySource3D.java         | 11 +++
 .../geometry/euclidean/twod/BoundaryList2D.java    | 47 ++++++++++++
 .../geometry/euclidean/twod/BoundarySource2D.java  | 11 +++
 .../euclidean/threed/BoundaryList3DTest.java       | 50 +++++++------
 .../euclidean/threed/BoundarySource3DTest.java     | 27 +++++++
 .../euclidean/threed/ConvexVolumeTest.java         | 33 ++++++++
 .../euclidean/threed/RegionBSPTree3DTest.java      | 21 ++++++
 .../euclidean/twod/BoundaryList2DTest.java         | 50 +++++++------
 .../euclidean/twod/BoundarySource2DTest.java       | 27 +++++++
 .../geometry/euclidean/twod/ConvexAreaTest.java    | 27 +++++++
 .../euclidean/twod/RegionBSPTree2DTest.java        | 20 +++++
 .../geometry/spherical/twod/BoundaryList2S.java    | 47 ++++++++++++
 .../geometry/spherical/twod/BoundarySource2S.java  | 32 ++++++++
 ...rySource2STest.java => BoundaryList2STest.java} | 48 +++++++-----
 .../spherical/twod/BoundarySource2STest.java       | 36 ++++++++-
 .../geometry/spherical/twod/ConvexArea2STest.java  | 29 ++++++++
 .../spherical/twod/RegionBSPTree2STest.java        | 21 ++++++
 22 files changed, 690 insertions(+), 69 deletions(-)

diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegion.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegion.java
index 20e7f7b..e449955 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegion.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegion.java
@@ -144,7 +144,8 @@ public abstract class AbstractConvexHyperplaneBoundedRegion<P extends Point<P>,
         final StringBuilder sb = new StringBuilder();
         sb.append(this.getClass().getSimpleName())
             .append("[boundaries= ")
-            .append(boundaries);
+            .append(boundaries)
+            .append(']');
 
         return sb.toString();
     }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryList.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryList.java
new file mode 100644
index 0000000..edf839f
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryList.java
@@ -0,0 +1,82 @@
+/*
+ * 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.commons.geometry.core.partitioning;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.commons.geometry.core.Point;
+
+/** Simple implementation of {@link BoundarySource} containing boundaries stored in a list.
+ * Lists given during construction are used directly; no copies are made. Thread safety and
+ * immutability therefore depend on the underlying list and its usage outside of this class.
+ * The boundary list cannot be modified through this class.
+ * @param <P> Point implementation type
+ * @param <S> Hyperplane convex subset implementation type
+ */
+public class BoundaryList<P extends Point<P>, S extends HyperplaneConvexSubset<P>>
+    implements BoundarySource<S> {
+
+    /** List of boundaries. */
+    private final List<S> boundaries;
+
+    /** Construct a new instance containing the given boundaries. The input list is
+     * used directly; no copy is made.
+     * @param boundaries boundary list
+     */
+    public BoundaryList(final List<? extends S> boundaries) {
+        this.boundaries = Collections.unmodifiableList(boundaries);
+    }
+
+    /** Get the boundaries for the instance. The returned list cannot be modified.
+     * @return boundaries for the instance
+     */
+    public List<S> getBoundaries() {
+        return boundaries;
+    }
+
+    /** Get the number of boundaries in the instance. This is exactly
+     * equivalent to {@code boundaryList.getBoundaries().size()} but the
+     * word "size" is avoided here to prevent confusion with geometric
+     * size.
+     * @return number of boundaries in the instance
+     */
+    public int count() {
+        return boundaries.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Stream<S> boundaryStream() {
+        return boundaries.stream();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(this.getClass().getSimpleName())
+            // only display the count and not the actual boundaries
+            // since the list could be huge
+            .append("[count= ")
+            .append(count())
+            .append(']');
+
+        return sb.toString();
+    }
+}
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegionTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegionTest.java
index b018d21..73961d6 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegionTest.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/AbstractConvexHyperplaneBoundedRegionTest.java
@@ -646,8 +646,7 @@ public class AbstractConvexHyperplaneBoundedRegionTest {
         final String str = region.toString();
 
         // assert
-        Assertions.assertTrue(str.contains("StubRegion"));
-        Assertions.assertTrue(str.contains("boundaries= "));
+        Assertions.assertEquals("StubRegion[boundaries= []]", str);
     }
 
     private static void checkClassify(final Region<TestPoint2D> region, final RegionLocation loc, final TestPoint2D... pts) {
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/BoundaryListTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/BoundaryListTest.java
new file mode 100644
index 0000000..62827a5
--- /dev/null
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/BoundaryListTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.commons.geometry.core.partitioning;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.geometry.core.partitioning.test.TestLineSegment;
+import org.apache.commons.geometry.core.partitioning.test.TestPoint2D;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class BoundaryListTest {
+
+    @Test
+    public void testBoundaries() {
+        // arrange
+        final List<TestLineSegment> boundaries = new ArrayList<>();
+        boundaries.add(new TestLineSegment(0, 0, 1, 1));
+        boundaries.add(new TestLineSegment(1, 1, 0, 2));
+
+        // act
+        final BoundaryList<TestPoint2D, TestLineSegment> list = new BoundaryList<>(boundaries);
+
+        // assert
+        Assertions.assertNotSame(boundaries, list.getBoundaries());
+        Assertions.assertEquals(boundaries, list.getBoundaries());
+        Assertions.assertEquals(boundaries, list.boundaryStream().collect(Collectors.toList()));
+    }
+
+    @Test
+    public void testGetBoundaries_listCannotBeModified() {
+        // arrange
+        final List<TestLineSegment> boundaries = new ArrayList<>();
+        boundaries.add(new TestLineSegment(0, 0, 1, 1));
+
+        final BoundaryList<TestPoint2D, TestLineSegment> list = new BoundaryList<>(boundaries);
+
+        // act/assert
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
+            list.getBoundaries().add(new TestLineSegment(1, 1, 0, 2));
+        });
+    }
+
+    @Test
+    public void testCount() {
+        // act/assert
+        Assertions.assertEquals(0, new BoundaryList<>(Collections.emptyList()).count());
+        Assertions.assertEquals(1, new BoundaryList<>(Arrays.asList(
+                    new TestLineSegment(0, 0, 1, 1)
+                )).count());
+        Assertions.assertEquals(2, new BoundaryList<>(Arrays.asList(
+                new TestLineSegment(0, 0, 1, 1),
+                new TestLineSegment(1, 1, 0, 2)
+            )).count());
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        final BoundaryList<TestPoint2D, TestLineSegment> empty = new BoundaryList<>(Collections.emptyList());
+        final BoundaryList<TestPoint2D, TestLineSegment> single = new BoundaryList<>(Arrays.asList(
+                    new TestLineSegment(0, 0, 1, 1)
+                ));
+
+        // act
+        Assertions.assertEquals("BoundaryList[count= 0]", empty.toString());
+        Assertions.assertEquals("BoundaryList[count= 1]", single.toString());
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3D.java
new file mode 100644
index 0000000..645643c
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3D.java
@@ -0,0 +1,47 @@
+/*
+ * 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.commons.geometry.euclidean.threed;
+
+import java.util.List;
+
+import org.apache.commons.geometry.core.partitioning.BoundaryList;
+
+/** {@link BoundarySource3D} implementation that uses boundaries stored in
+ * a list. Lists given during construction are used directly; no copies are made.
+ * Thread safety and immutability therefore depend on the underlying list and its
+ * usage outside of this class. The boundary list cannot be modified through this
+ * class.
+ */
+public class BoundaryList3D extends BoundaryList<Vector3D, PlaneConvexSubset>
+    implements BoundarySource3D {
+
+    /** Construct a new instance with the given list of boundaries. The
+     * argument is used directly; no copy is made.
+     * @param boundaries list of boundaries for the instance
+     */
+    public BoundaryList3D(final List<? extends PlaneConvexSubset> boundaries) {
+        super(boundaries);
+    }
+
+    /** Return this instance.
+     * @return this instance
+     */
+    @Override
+    public BoundaryList3D toList() {
+        return this;
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3D.java
index f7e7cf0..7188f2d 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3D.java
@@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.threed;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.commons.geometry.core.partitioning.BoundarySource;
@@ -33,6 +34,16 @@ import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
  */
 public interface BoundarySource3D extends BoundarySource<PlaneConvexSubset>, Linecastable3D {
 
+    /** Return a {@link BoundaryList3D} containing the boundaries in this instance.
+     * @return a {@link BoundaryList3D} containing the boundaries in this instance
+     */
+    default BoundaryList3D toList() {
+        final List<PlaneConvexSubset> boundaries = boundaryStream()
+                .collect(Collectors.toList());
+
+        return new BoundaryList3D(boundaries);
+    }
+
     /** Return a BSP tree constructed from the boundaries contained in this instance. This is
      * a convenience method for quickly constructing BSP trees and may produce unbalanced trees
      * with unacceptable performance characteristics when used with large numbers of boundaries.
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2D.java
new file mode 100644
index 0000000..42062ea
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2D.java
@@ -0,0 +1,47 @@
+/*
+ * 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.commons.geometry.euclidean.twod;
+
+import java.util.List;
+
+import org.apache.commons.geometry.core.partitioning.BoundaryList;
+
+/** {@link BoundarySource2D} implementation that uses boundaries stored in
+ * a list. Lists given during construction are used directly; no copies are made.
+ * Thread safety and immutability therefore depend on the underlying list and its
+ * usage outside of this class. The boundary list cannot be modified through this
+ * class.
+ */
+public class BoundaryList2D extends BoundaryList<Vector2D, LineConvexSubset>
+    implements BoundarySource2D {
+
+    /** Construct a new instance with the given list of boundaries. The
+     * argument is used directly; no copy is made.
+     * @param boundaries list of boundaries for the instance
+     */
+    public BoundaryList2D(final List<? extends LineConvexSubset> boundaries) {
+        super(boundaries);
+    }
+
+    /** Return this instance.
+     * @return this instance
+     */
+    @Override
+    public BoundaryList2D toList() {
+        return this;
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2D.java
index 12cd0de..cd7f6fc 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2D.java
@@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.twod;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import org.apache.commons.geometry.core.partitioning.BoundarySource;
 
@@ -26,6 +27,16 @@ import org.apache.commons.geometry.core.partitioning.BoundarySource;
  */
 public interface BoundarySource2D extends BoundarySource<LineConvexSubset>, Linecastable2D {
 
+    /** Return a {@link BoundaryList2D} containing the boundaries in this instance.
+     * @return a {@link BoundaryList2D} containing the boundaries in this instance
+     */
+    default BoundaryList2D toList() {
+        final List<LineConvexSubset> boundaries = boundaryStream()
+                .collect(Collectors.toList());
+
+        return new BoundaryList2D(boundaries);
+    }
+
     /** Return a BSP tree constructed from the boundaries contained in this instance. This is
      * a convenience method for quickly constructing BSP trees and may produce unbalanced trees
      * with unacceptable performance characteristics when used with large numbers of boundaries.
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3DTest.java
similarity index 52%
copy from commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
copy to commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3DTest.java
index 03a741a..723dae6 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundaryList3DTest.java
@@ -14,47 +14,55 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.spherical.twod;
+package org.apache.commons.geometry.euclidean.threed;
 
-import java.util.stream.Stream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-public class BoundarySource2STest {
+public class BoundaryList3DTest {
 
     private static final double TEST_EPS = 1e-10;
 
-    private static final DoublePrecisionContext TEST_PRECISION = new EpsilonDoublePrecisionContext(TEST_EPS);
+    private static final DoublePrecisionContext TEST_PRECISION =
+            new EpsilonDoublePrecisionContext(TEST_EPS);
 
     @Test
-    public void testToTree() {
-        // act
-        final BoundarySource2S src = () -> Stream.of(
-                GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION));
+    public void testCtor() {
+        // arrange
+        final List<PlaneConvexSubset> boundaries = Arrays.asList(
+                    Planes.fromNormal(Vector3D.Unit.PLUS_X, TEST_PRECISION).span()
+                );
 
         // act
-        final RegionBSPTree2S tree = src.toTree();
+        final BoundaryList3D list = new BoundaryList3D(boundaries);
 
         // assert
-        Assertions.assertEquals(3, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertFalse(tree.isEmpty());
+        Assertions.assertNotSame(boundaries, list.getBoundaries());
+        Assertions.assertEquals(boundaries, list.getBoundaries());
+        Assertions.assertEquals(1, list.count());
     }
 
     @Test
-    public void testToTree_noBoundaries() {
-        // act
-        final BoundarySource2S src = Stream::empty;
+    public void testToList() {
+        // arrange
+        final BoundaryList3D list = new BoundaryList3D(Collections.emptyList());
 
-        // act
-        final RegionBSPTree2S tree = src.toTree();
+        // act/assert
+        Assertions.assertSame(list, list.toList());
+    }
 
-        // assert
-        Assertions.assertEquals(1, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertTrue(tree.isEmpty());
+    @Test
+    public void testToString() {
+        // arrange
+        final BoundaryList3D list = new BoundaryList3D(Collections.emptyList());
+
+        // act
+        Assertions.assertEquals("BoundaryList3D[count= 0]", list.toString());
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3DTest.java
index 16bd1d9..f2767a8 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/BoundarySource3DTest.java
@@ -34,6 +34,33 @@ public class BoundarySource3DTest {
             new EpsilonDoublePrecisionContext(TEST_EPS);
 
     @Test
+    public void testToList() {
+        // act
+        final BoundarySource3D src = BoundarySource3D.from(
+            Planes.convexPolygonFromVertices(
+                    Arrays.asList(Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y), TEST_PRECISION)
+        );
+
+        // act
+        final BoundaryList3D list = src.toList();
+
+        // assert
+        Assertions.assertEquals(1, list.count());
+    }
+
+    @Test
+    public void testToList_noBoundaries() {
+        // act
+        final BoundarySource3D src = BoundarySource3D.from();
+
+        // act
+        final BoundaryList3D list = src.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
+
+    @Test
     public void testToTree() {
         // act
         final PlaneConvexSubset a = Planes.convexPolygonFromVertices(
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/ConvexVolumeTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/ConvexVolumeTest.java
index c81e19c..80f6e8c 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/ConvexVolumeTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/ConvexVolumeTest.java
@@ -179,6 +179,39 @@ public class ConvexVolumeTest {
     }
 
     @Test
+    public void testToList_full() {
+        // arrange
+        final ConvexVolume volume = ConvexVolume.full();
+
+        // act
+        final BoundaryList3D list = volume.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
+
+    @Test
+    public void testToList() {
+        // arrange
+        final ConvexVolume volume = ConvexVolume.fromBounds(
+                    Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.MINUS_X, TEST_PRECISION),
+                    Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.MINUS_Y, TEST_PRECISION),
+                    Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.MINUS_Z, TEST_PRECISION),
+
+                    Planes.fromPointAndNormal(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_X, TEST_PRECISION),
+                    Planes.fromPointAndNormal(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Y, TEST_PRECISION),
+                    Planes.fromPointAndNormal(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Z, TEST_PRECISION)
+                );
+
+        // act
+        final BoundaryList3D list = volume.toList();
+
+        // assert
+        Assertions.assertEquals(6, list.count());
+        Assertions.assertEquals(1, list.toTree().getSize(), TEST_EPS);
+    }
+
+    @Test
     public void testToTree_full() {
         // arrange
         final ConvexVolume volume = ConvexVolume.full();
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RegionBSPTree3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RegionBSPTree3DTest.java
index 12519b6..1c9ccb6 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RegionBSPTree3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RegionBSPTree3DTest.java
@@ -460,6 +460,27 @@ public class RegionBSPTree3DTest {
     }
 
     @Test
+    public void testToList() {
+        // arrange
+        final RegionBSPTree3D tree = Parallelepiped.axisAligned(
+                Vector3D.ZERO, Vector3D.of(1, 3, 3), TEST_PRECISION).toTree();
+
+        // act
+        final BoundaryList3D list = tree.toList();
+
+        // assert
+        Assertions.assertEquals(6, list.count());
+        Assertions.assertEquals(9, list.toTree().getSize());
+    }
+
+    @Test
+    public void testToList_fullAndEmpty() {
+        // act/assert
+        Assertions.assertEquals(0, RegionBSPTree3D.full().toList().count());
+        Assertions.assertEquals(0, RegionBSPTree3D.empty().toList().count());
+    }
+
+    @Test
     public void testToTree_returnsSameInstance() {
         // arrange
         final RegionBSPTree3D tree = createRect(Vector3D.ZERO, Vector3D.of(1, 2, 1));
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2DTest.java
similarity index 52%
copy from commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
copy to commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2DTest.java
index 03a741a..816af97 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundaryList2DTest.java
@@ -14,47 +14,55 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.spherical.twod;
+package org.apache.commons.geometry.euclidean.twod;
 
-import java.util.stream.Stream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-public class BoundarySource2STest {
+public class BoundaryList2DTest {
 
     private static final double TEST_EPS = 1e-10;
 
-    private static final DoublePrecisionContext TEST_PRECISION = new EpsilonDoublePrecisionContext(TEST_EPS);
+    private static final DoublePrecisionContext TEST_PRECISION =
+            new EpsilonDoublePrecisionContext(TEST_EPS);
 
     @Test
-    public void testToTree() {
-        // act
-        final BoundarySource2S src = () -> Stream.of(
-                GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION));
+    public void testCtor() {
+        // arrange
+        final List<LineConvexSubset> boundaries = Arrays.asList(
+                    Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION)
+                );
 
         // act
-        final RegionBSPTree2S tree = src.toTree();
+        final BoundaryList2D list = new BoundaryList2D(boundaries);
 
         // assert
-        Assertions.assertEquals(3, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertFalse(tree.isEmpty());
+        Assertions.assertNotSame(boundaries, list.getBoundaries());
+        Assertions.assertEquals(boundaries, list.getBoundaries());
+        Assertions.assertEquals(1, list.count());
     }
 
     @Test
-    public void testToTree_noBoundaries() {
-        // act
-        final BoundarySource2S src = Stream::empty;
+    public void testToList() {
+        // arrange
+        final BoundaryList2D list = new BoundaryList2D(Collections.emptyList());
 
-        // act
-        final RegionBSPTree2S tree = src.toTree();
+        // act/assert
+        Assertions.assertSame(list, list.toList());
+    }
 
-        // assert
-        Assertions.assertEquals(1, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertTrue(tree.isEmpty());
+    @Test
+    public void testToString() {
+        // arrange
+        final BoundaryList2D list = new BoundaryList2D(Collections.emptyList());
+
+        // act
+        Assertions.assertEquals("BoundaryList2D[count= 0]", list.toString());
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2DTest.java
index 1236add..ae736cb 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/BoundarySource2DTest.java
@@ -33,6 +33,33 @@ public class BoundarySource2DTest {
             new EpsilonDoublePrecisionContext(TEST_EPS);
 
     @Test
+    public void testToList() {
+        // act
+        final BoundarySource2D src = BoundarySource2D.from(
+            Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION),
+            Lines.segmentFromPoints(Vector2D.of(1, 0), Vector2D.of(1, 1), TEST_PRECISION)
+        );
+
+        // act
+        final BoundaryList2D list = src.toList();
+
+        // assert
+        Assertions.assertEquals(2, list.count());
+    }
+
+    @Test
+    public void testToList_noBoundaries() {
+        // act
+        final BoundarySource2D src = BoundarySource2D.from();
+
+        // act
+        final BoundaryList2D list = src.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
+
+    @Test
     public void testToTree() {
         // act
         final BoundarySource2D src = BoundarySource2D.from(
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/ConvexAreaTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/ConvexAreaTest.java
index 113795c..8c85262 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/ConvexAreaTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/ConvexAreaTest.java
@@ -87,6 +87,33 @@ public class ConvexAreaTest {
     }
 
     @Test
+    public void testToList() {
+        // arrange
+        final ConvexArea area = ConvexArea.convexPolygonFromVertices(Arrays.asList(
+                    Vector2D.ZERO, Vector2D.of(1, 0), Vector2D.of(0, 1)
+                ), TEST_PRECISION);
+
+        // act
+        final BoundaryList2D list = area.toList();
+
+        // assert
+        Assertions.assertEquals(3, list.count());
+        Assertions.assertEquals(area.getBoundaries(), list.getBoundaries());
+    }
+
+    @Test
+    public void testToList_full() {
+        // arrange
+        final ConvexArea area = ConvexArea.full();
+
+        // act
+        final BoundaryList2D list = area.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
+
+    @Test
     public void testToTree() {
         // arrange
         final ConvexArea area = ConvexArea.fromBounds(
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/RegionBSPTree2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/RegionBSPTree2DTest.java
index 93a11be..908dab8 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/RegionBSPTree2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/RegionBSPTree2DTest.java
@@ -1081,6 +1081,26 @@ public class RegionBSPTree2DTest {
     }
 
     @Test
+    public void testToList() {
+        // arrange
+        final RegionBSPTree2D tree = Parallelogram.axisAligned(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION).toTree();
+
+        // act
+        final BoundaryList2D list = tree.toList();
+
+        // assert
+        Assertions.assertEquals(4, list.toList().count());
+        Assertions.assertEquals(1, list.toTree().getSize(), TEST_EPS);
+    }
+
+    @Test
+    public void testToList_fullAndEmpty() {
+        // act/assert
+        Assertions.assertEquals(0, RegionBSPTree2D.full().toList().count());
+        Assertions.assertEquals(0, RegionBSPTree2D.empty().toList().count());
+    }
+
+    @Test
     public void testToTree_returnsSameInstance() {
         // arrange
         final RegionBSPTree2D tree = Parallelogram.axisAligned(Vector2D.ZERO, Vector2D.of(1, 2), TEST_PRECISION).toTree();
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundaryList2S.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundaryList2S.java
new file mode 100644
index 0000000..d0b12ae
--- /dev/null
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundaryList2S.java
@@ -0,0 +1,47 @@
+/*
+ * 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.commons.geometry.spherical.twod;
+
+import java.util.List;
+
+import org.apache.commons.geometry.core.partitioning.BoundaryList;
+
+/** {@link BoundarySource2S} implementation that uses boundaries stored in
+ * a list. Lists given during construction are used directly; no copies are made.
+ * Thread safety and immutability therefore depend on the underlying list and its
+ * usage outside of this class. The boundary list cannot be modified through this
+ * class.
+ */
+public class BoundaryList2S extends BoundaryList<Point2S, GreatArc>
+    implements BoundarySource2S {
+
+    /** Construct a new instance with the given list of boundaries. The
+     * argument is used directly; no copy is made.
+     * @param boundaries list of boundaries for the instance
+     */
+    public BoundaryList2S(final List<? extends GreatArc> boundaries) {
+        super(boundaries);
+    }
+
+    /** Return this instance.
+     * @return this instance
+     */
+    @Override
+    public BoundaryList2S toList() {
+        return this;
+    }
+}
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundarySource2S.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundarySource2S.java
index 190e53b..749904d 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundarySource2S.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/BoundarySource2S.java
@@ -16,6 +16,11 @@
  */
 package org.apache.commons.geometry.spherical.twod;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
 import org.apache.commons.geometry.core.partitioning.BoundarySource;
 
 /** Extension of the {@link BoundarySource} interface for spherical 2D
@@ -23,6 +28,16 @@ import org.apache.commons.geometry.core.partitioning.BoundarySource;
  */
 public interface BoundarySource2S extends BoundarySource<GreatArc> {
 
+    /** Return a {@link BoundaryList2S} containing the boundaries in this instance.
+     * @return a {@link BoundaryList2S} containing the boundaries in this instance
+     */
+    default BoundaryList2S toList() {
+        final List<GreatArc> boundaries = boundaryStream()
+                .collect(Collectors.toList());
+
+        return new BoundaryList2S(boundaries);
+    }
+
     /** Return a BSP tree constructed from the boundaries contained in this
      * instance. The default implementation creates a new, empty tree
      * and inserts the boundaries from this instance.
@@ -34,4 +49,21 @@ public interface BoundarySource2S extends BoundarySource<GreatArc> {
 
         return tree;
     }
+
+    /** Return a {@link BoundarySource2S} instance containing the given boundaries.
+     * @param boundaries boundaries to include in the boundary source
+     * @return a boundary source containing the given boundaries
+     */
+    static BoundarySource2S from(final GreatArc... boundaries) {
+        return from(Arrays.asList(boundaries));
+    }
+
+    /** Return a {@link BoundarySource2S} instance containing the given boundaries. The given
+     * collection is used directly as the source of the line subsets; no copy is made.
+     * @param boundaries boundaries to include in the boundary source
+     * @return a boundary source containing the given boundaries
+     */
+    static BoundarySource2S from(final Collection<GreatArc> boundaries) {
+        return boundaries::stream;
+    }
 }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundaryList2STest.java
similarity index 54%
copy from commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
copy to commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundaryList2STest.java
index 03a741a..c36c850 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundaryList2STest.java
@@ -16,45 +16,53 @@
  */
 package org.apache.commons.geometry.spherical.twod;
 
-import java.util.stream.Stream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-public class BoundarySource2STest {
+public class BoundaryList2STest {
 
     private static final double TEST_EPS = 1e-10;
 
-    private static final DoublePrecisionContext TEST_PRECISION = new EpsilonDoublePrecisionContext(TEST_EPS);
+    private static final DoublePrecisionContext TEST_PRECISION =
+            new EpsilonDoublePrecisionContext(TEST_EPS);
 
     @Test
-    public void testToTree() {
-        // act
-        final BoundarySource2S src = () -> Stream.of(
-                GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION));
+    public void testCtor() {
+        // arrange
+        final List<GreatArc> boundaries = Arrays.asList(
+                    GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION)
+                );
 
         // act
-        final RegionBSPTree2S tree = src.toTree();
+        final BoundaryList2S list = new BoundaryList2S(boundaries);
 
         // assert
-        Assertions.assertEquals(3, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertFalse(tree.isEmpty());
+        Assertions.assertNotSame(boundaries, list.getBoundaries());
+        Assertions.assertEquals(boundaries, list.getBoundaries());
+        Assertions.assertEquals(1, list.count());
     }
 
     @Test
-    public void testToTree_noBoundaries() {
-        // act
-        final BoundarySource2S src = Stream::empty;
+    public void testToList() {
+        // arrange
+        final BoundaryList2S list = new BoundaryList2S(Collections.emptyList());
 
-        // act
-        final RegionBSPTree2S tree = src.toTree();
+        // act/assert
+        Assertions.assertSame(list, list.toList());
+    }
 
-        // assert
-        Assertions.assertEquals(1, tree.count());
-        Assertions.assertFalse(tree.isFull());
-        Assertions.assertTrue(tree.isEmpty());
+    @Test
+    public void testToString() {
+        // arrange
+        final BoundaryList2S list = new BoundaryList2S(Collections.emptyList());
+
+        // act
+        Assertions.assertEquals("BoundaryList2S[count= 0]", list.toString());
     }
 }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
index 03a741a..0ed9362 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/BoundarySource2STest.java
@@ -16,7 +16,7 @@
  */
 package org.apache.commons.geometry.spherical.twod;
 
-import java.util.stream.Stream;
+import java.util.Collections;
 
 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
@@ -27,12 +27,40 @@ public class BoundarySource2STest {
 
     private static final double TEST_EPS = 1e-10;
 
-    private static final DoublePrecisionContext TEST_PRECISION = new EpsilonDoublePrecisionContext(TEST_EPS);
+    private static final DoublePrecisionContext TEST_PRECISION =
+            new EpsilonDoublePrecisionContext(TEST_EPS);
+
+    @Test
+    public void testToList() {
+        // act
+        final BoundarySource2S src = BoundarySource2S.from(
+            GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
+            GreatCircles.arcFromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
+        );
+
+        // act
+        final BoundaryList2S list = src.toList();
+
+        // assert
+        Assertions.assertEquals(2, list.count());
+    }
+
+    @Test
+    public void testToList_noBoundaries() {
+        // act
+        final BoundarySource2S src = BoundarySource2S.from();
+
+        // act
+        final BoundaryList2S list = src.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
 
     @Test
     public void testToTree() {
         // act
-        final BoundarySource2S src = () -> Stream.of(
+        final BoundarySource2S src = BoundarySource2S.from(
                 GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION));
 
         // act
@@ -47,7 +75,7 @@ public class BoundarySource2STest {
     @Test
     public void testToTree_noBoundaries() {
         // act
-        final BoundarySource2S src = Stream::empty;
+        final BoundarySource2S src = BoundarySource2S.from(Collections.emptyList());
 
         // act
         final RegionBSPTree2S tree = src.toTree();
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/ConvexArea2STest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/ConvexArea2STest.java
index 78c38a1..d2bbc81 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/ConvexArea2STest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/ConvexArea2STest.java
@@ -791,6 +791,35 @@ public class ConvexArea2STest {
     }
 
     @Test
+    public void testToList_full() {
+        // arrange
+        final ConvexArea2S area = ConvexArea2S.full();
+
+        // act
+        final BoundaryList2S list = area.toList();
+
+        // assert
+        Assertions.assertEquals(0, list.count());
+    }
+
+    @Test
+    public void testToList() {
+        // arrange
+        final ConvexArea2S area = ConvexArea2S.fromVertexLoop(Arrays.asList(
+                    Point2S.of(0.1, 0.1), Point2S.of(-0.4, 1),
+                    Point2S.of(0.15, 1.5), Point2S.of(0.3, 1.2),
+                    Point2S.of(0.1, 0.1)
+                ), TEST_PRECISION);
+
+        // act
+        final BoundaryList2S list = area.toList();
+
+        // assert
+        Assertions.assertEquals(4, list.count());
+        Assertions.assertEquals(area.getSize(), list.toTree().getSize(), TEST_EPS);
+    }
+
+    @Test
     public void testToTree_full() {
         // arrange
         final ConvexArea2S area = ConvexArea2S.full();
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/RegionBSPTree2STest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/RegionBSPTree2STest.java
index 867d4d8..16259ad 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/RegionBSPTree2STest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/RegionBSPTree2STest.java
@@ -226,6 +226,27 @@ public class RegionBSPTree2STest {
     }
 
     @Test
+    public void testToList_fullAndEmpty() {
+        // act/assert
+        Assertions.assertEquals(0, RegionBSPTree2S.full().toList().count());
+        Assertions.assertEquals(0, RegionBSPTree2S.empty().toList().count());
+    }
+
+    @Test
+    public void testToList() {
+        // arrange
+        final RegionBSPTree2S tree = RegionBSPTree2S.empty();
+        insertPositiveQuadrant(tree);
+
+        // act
+        final BoundaryList2S list = tree.toList();
+
+        // assert
+        Assertions.assertEquals(3, list.count());
+        Assertions.assertEquals(0.5 * Math.PI, list.toTree().getSize(), TEST_EPS);
+    }
+
+    @Test
     public void testToTree_returnsSameInstance() {
         // arrange
         final RegionBSPTree2S tree = RegionBSPTree2S.empty();