View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.geometry.io.euclidean.threed;
    18
    19  import java.nio.file.Paths;
    20  import java.util.Arrays;
    21  import java.util.Collection;
    22  import java.util.Collections;
    23  import java.util.stream.Collectors;
    24  import java.util.stream.Stream;
    25
    26  import org.apache.commons.geometry.core.GeometryTestUtils;
    27  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
    28  import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
    29  import org.apache.commons.geometry.euclidean.threed.Planes;
    30  import org.apache.commons.geometry.euclidean.threed.Triangle3D;
    31  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    32  import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
    33  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
    34  import org.apache.commons.geometry.io.core.GeometryFormat;
    35  import org.apache.commons.geometry.io.core.input.FileGeometryInput;
    36  import org.apache.commons.geometry.io.core.input.GeometryInput;
    37  import org.apache.commons.geometry.io.core.output.FileGeometryOutput;
    38  import org.apache.commons.geometry.io.core.output.GeometryOutput;
    39  import org.apache.commons.geometry.io.core.test.StubGeometryFormat;
    40  import org.apache.commons.numbers.core.Precision;
    41  import org.junit.jupiter.api.Assertions;
    42  import org.junit.jupiter.api.Test;
    43
    44  class BoundaryIOManager3DTest {
    45
    46      private static final double TEST_EPS = 1e-10;
    47
    48      private static final Precision.DoubleEquivalence TEST_PRECISION = Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    49
    50      private static final GeometryFormat TEST_FMT = new StubGeometryFormat("test");
    51
    52      private static final FacetDefinitionReader FACET_DEF_READER = new FacetDefinitionReader() {
    53
    54          @Override
    55          public FacetDefinition readFacet() {
    56              throw new UnsupportedOperationException();
    57          }
    58
    59          @Override
    60          public void close() {
    61              // do nothing
    62          }
    63      };
    64
    65      private static final FacetDefinition FACET = new SimpleFacetDefinition(Arrays.asList(
    66              Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(1, 1, 0), Vector3D.of(0, 1, 0)));
    67
    68      private static final Triangle3D TRI = Planes.triangleFromVertices(
    69              Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    70
    71      private static final TriangleMesh TRI_MESH = SimpleTriangleMesh.builder(TEST_PRECISION).build();
    72
    73      private final BoundaryIOManager3D manager = new BoundaryIOManager3D();
    74
    75      @Test
    76      void testRegisterDefaultHandlers() {
    77          // act
    78          manager.registerDefaultHandlers();
    79
    80          // assert
    81          // ensure that we have default read/write handlers for every defined format
    82          final GeometryFormat3D[] fmts = GeometryFormat3D.values();
    83
    84          Assertions.assertEquals(fmts.length, manager.getReadHandlers().size());
    85          Assertions.assertEquals(fmts.length, manager.getWriteHandlers().size());
    86
    87          for (final GeometryFormat3D fmt : fmts) {
    88              Assertions.assertNotNull(manager.getReadHandlerForFormat(fmt));
    89              Assertions.assertNotNull(manager.getWriteHandlerForFormat(fmt));
    90          }
    91      }
    92
    93      @Test
    94      void testFacetDefinitionReader_formatGiven() {
    95          // arrange
    96          final StubReadHandler3D readHandler = new StubReadHandler3D();
    97          manager.registerReadHandler(readHandler);
    98
    99          final GeometryInput in = new FileGeometryInput(Paths.get("myfile"));
    100
    101         // act
    102         final FacetDefinitionReader result = manager.facetDefinitionReader(in, TEST_FMT);
    103
    104         // assert
    105         Assertions.assertSame(FACET_DEF_READER, result);
    106         Assertions.assertSame(in, readHandler.inArg);
    107     }
    108
    109     @Test
    110     void testFacetDefinitionReader_nullFormat() {
    111         // arrange
    112         final StubReadHandler3D readHandler = new StubReadHandler3D();
    113         manager.registerReadHandler(readHandler);
    114
    115         final GeometryInput in = new FileGeometryInput(Paths.get("myfile.test"));
    116
    117         // act
    118         final FacetDefinitionReader result = manager.facetDefinitionReader(in, null);
    119
    120         // assert
    121         Assertions.assertSame(FACET_DEF_READER, result);
    122         Assertions.assertSame(in, readHandler.inArg);
    123     }
    124
    125     @Test
    126     void testFacetDefinitionReader_unknownHandler() {
    127         // act/assert
    128         checkUnknownReadHandler(manager::facetDefinitionReader);
    129     }
    130
    131     @Test
    132     void testFacets_formatGiven() {
    133         // arrange
    134         final StubReadHandler3D readHandler = new StubReadHandler3D();
    135         manager.registerReadHandler(readHandler);
    136
    137         final GeometryInput in = new FileGeometryInput(Paths.get("myfile"));
    138
    139         // act
    140         final Stream<FacetDefinition> result = manager.facets(in, TEST_FMT);
    141
    142         // assert
    143         Assertions.assertEquals(Collections.singletonList(FACET), result.collect(Collectors.toList()));
    144         Assertions.assertSame(in, readHandler.inArg);
    145     }
    146
    147     @Test
    148     void testFacets_nullFormat() {
    149         // arrange
    150         final StubReadHandler3D readHandler = new StubReadHandler3D();
    151         manager.registerReadHandler(readHandler);
    152
    153         final GeometryInput in = new FileGeometryInput(Paths.get("myfile.test"));
    154
    155         // act
    156         final Stream<FacetDefinition> result = manager.facets(in, null);
    157
    158         // assert
    159         Assertions.assertEquals(Collections.singletonList(FACET), result.collect(Collectors.toList()));
    160         Assertions.assertSame(in, readHandler.inArg);
    161     }
    162
    163     @Test
    164     void testFacets_unknownHandler() {
    165         // act/assert
    166         checkUnknownReadHandler(manager::facets);
    167     }
    168
    169     @Test
    170     void testTriangles_formatGiven() {
    171         // arrange
    172         final StubReadHandler3D readHandler = new StubReadHandler3D();
    173         manager.registerReadHandler(readHandler);
    174
    175         final GeometryInput in = new FileGeometryInput(Paths.get("myfile"));
    176
    177         // act
    178         final Stream<Triangle3D> result = manager.triangles(in, TEST_FMT, TEST_PRECISION);
    179
    180         // assert
    181         Assertions.assertEquals(Collections.singletonList(TRI), result.collect(Collectors.toList()));
    182         Assertions.assertSame(in, readHandler.inArg);
    183         Assertions.assertSame(TEST_PRECISION, readHandler.precisionArg);
    184     }
    185
    186     @Test
    187     void testTriangles_nullFormat() {
    188         // arrange
    189         final StubReadHandler3D readHandler = new StubReadHandler3D();
    190         manager.registerReadHandler(readHandler);
    191
    192         final GeometryInput in = new FileGeometryInput(Paths.get("myfile.test"));
    193
    194         // act
    195         final Stream<Triangle3D> result = manager.triangles(in, null, TEST_PRECISION);
    196
    197         // assert
    198         Assertions.assertEquals(Collections.singletonList(TRI), result.collect(Collectors.toList()));
    199         Assertions.assertSame(in, readHandler.inArg);
    200         Assertions.assertSame(TEST_PRECISION, readHandler.precisionArg);
    201     }
    202
    203     @Test
    204     void testTriangles_unknownHandler() {
    205         // act/assert
    206         checkUnknownReadHandler((in, fmt) -> manager.triangles(in, fmt, TEST_PRECISION));
    207     }
    208
    209     @Test
    210     void testReadTriangleMesh_formatGiven() {
    211         // arrange
    212         final StubReadHandler3D readHandler = new StubReadHandler3D();
    213         manager.registerReadHandler(readHandler);
    214
    215         final GeometryInput in = new FileGeometryInput(Paths.get("myfile"));
    216
    217         // act
    218         final TriangleMesh result = manager.readTriangleMesh(in, TEST_FMT, TEST_PRECISION);
    219
    220         // assert
    221         Assertions.assertEquals(TRI_MESH, result);
    222         Assertions.assertSame(in, readHandler.inArg);
    223         Assertions.assertSame(TEST_PRECISION, readHandler.precisionArg);
    224     }
    225
    226     @Test
    227     void testReadTriangleMesh_nullFormat() {
    228         // arrange
    229         final StubReadHandler3D readHandler = new StubReadHandler3D();
    230         manager.registerReadHandler(readHandler);
    231
    232         final GeometryInput in = new FileGeometryInput(Paths.get("myfile.test"));
    233
    234         // act
    235         final TriangleMesh result = manager.readTriangleMesh(in, null, TEST_PRECISION);
    236
    237         // assert
    238         Assertions.assertEquals(TRI_MESH, result);
    239         Assertions.assertSame(in, readHandler.inArg);
    240         Assertions.assertSame(TEST_PRECISION, readHandler.precisionArg);
    241     }
    242
    243     @Test
    244     void testReadTriangleMesh_unknownHandler() {
    245         // act/assert
    246         checkUnknownReadHandler((in, fmt) -> manager.readTriangleMesh(in, fmt, TEST_PRECISION));
    247     }
    248
    249     @Test
    250     void testWrite_stream_formatGiven() {
    251         // arrange
    252         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    253         manager.registerWriteHandler(writeHandler);
    254
    255         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile"));
    256
    257         // act
    258         manager.write(Stream.of(TRI), out, TEST_FMT);
    259
    260         // assert
    261         Assertions.assertEquals(Collections.singletonList(TRI), writeHandler.boundariesArg);
    262         Assertions.assertSame(out, writeHandler.outArg);
    263     }
    264
    265     @Test
    266     void testWrite_stream_nullFormat() {
    267         // arrange
    268         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    269         manager.registerWriteHandler(writeHandler);
    270
    271         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile.TEST"));
    272
    273         // act
    274         manager.write(Stream.of(TRI), out, null);
    275
    276         // assert
    277         Assertions.assertEquals(Collections.singletonList(TRI), writeHandler.boundariesArg);
    278         Assertions.assertSame(out, writeHandler.outArg);
    279     }
    280
    281     @Test
    282     void testWrite_stream_unknownHandler() {
    283         // act/assert
    284         checkUnknownWriteHandler((out, fmt) -> manager.write(Stream.of(TRI), out, fmt));
    285     }
    286
    287     @Test
    288     void testWriteFacets_stream_formatGiven() {
    289         // arrange
    290         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    291         manager.registerWriteHandler(writeHandler);
    292
    293         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile"));
    294
    295         // act
    296         manager.writeFacets(Stream.of(FACET), out, TEST_FMT);
    297
    298         // assert
    299         Assertions.assertEquals(Collections.singletonList(FACET), writeHandler.facetsArg);
    300         Assertions.assertSame(out, writeHandler.outArg);
    301     }
    302
    303     @Test
    304     void testWriteFacets_stream_nullFormat() {
    305         // arrange
    306         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    307         manager.registerWriteHandler(writeHandler);
    308
    309         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile.TEST"));
    310
    311         // act
    312         manager.writeFacets(Stream.of(FACET), out, null);
    313
    314         // assert
    315         Assertions.assertEquals(Collections.singletonList(FACET), writeHandler.facetsArg);
    316         Assertions.assertSame(out, writeHandler.outArg);
    317     }
    318
    319     @Test
    320     void testWriteFacets_stream_unknownHandler() {
    321         // act/assert
    322         checkUnknownWriteHandler((out, fmt) -> manager.writeFacets(Stream.of(FACET), out, fmt));
    323     }
    324
    325     @Test
    326     void testWriteFacets_collection_formatGiven() {
    327         // arrange
    328         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    329         manager.registerWriteHandler(writeHandler);
    330
    331         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile"));
    332
    333         // act
    334         manager.writeFacets(Collections.singletonList(FACET), out, TEST_FMT);
    335
    336         // assert
    337         Assertions.assertEquals(Collections.singletonList(FACET), writeHandler.facetsArg);
    338         Assertions.assertSame(out, writeHandler.outArg);
    339     }
    340
    341     @Test
    342     void testWriteFacets_collection_nullFormat() {
    343         // arrange
    344         final StubWriteHandler3D writeHandler = new StubWriteHandler3D();
    345         manager.registerWriteHandler(writeHandler);
    346
    347         final GeometryOutput out = new FileGeometryOutput(Paths.get("myfile.TEST"));
    348
    349         // act
    350         manager.writeFacets(Collections.singletonList(FACET), out, null);
    351
    352         // assert
    353         Assertions.assertEquals(Collections.singletonList(FACET), writeHandler.facetsArg);
    354         Assertions.assertSame(out, writeHandler.outArg);
    355     }
    356
    357     @Test
    358     void testWriteFacets_collection_unknownHandler() {
    359         // act/assert
    360         checkUnknownWriteHandler((out, fmt) -> manager.writeFacets(Collections.singletonList(FACET), out, fmt));
    361     }
    362
    363     private static void checkUnknownReadHandler(final ThrowingBiConsumer<GeometryInput, GeometryFormat> fn) {
    364         // arrange
    365         final GeometryInput withFileExt = new FileGeometryInput(Paths.get("myfile.test"));
    366         final GeometryInput noFileExt = new FileGeometryInput(Paths.get("myfile"));
    367
    368         // act/assert
    369         GeometryTestUtils.assertThrowsWithMessage(
    370                 () -> fn.accept(withFileExt, TEST_FMT),
    371                 IllegalArgumentException.class, "Failed to find handler for format \"test\"");
    372
    373         GeometryTestUtils.assertThrowsWithMessage(
    374                 () -> fn.accept(withFileExt, null),
    375                 IllegalArgumentException.class, "Failed to find handler for file extension \"test\"");
    376
    377         GeometryTestUtils.assertThrowsWithMessage(
    378                 () -> fn.accept(noFileExt, null),
    379                 IllegalArgumentException.class, "Failed to find handler: no format specified and no file extension available");
    380     }
    381
    382     private static void checkUnknownWriteHandler(final ThrowingBiConsumer<GeometryOutput, GeometryFormat> fn) {
    383         // arrange
    384         final GeometryOutput withFileExt = new FileGeometryOutput(Paths.get("myfile.test"));
    385         final GeometryOutput noFileExt = new FileGeometryOutput(Paths.get("myfile"));
    386
    387         // act/assert
    388         GeometryTestUtils.assertThrowsWithMessage(
    389                 () -> fn.accept(withFileExt, TEST_FMT),
    390                 IllegalArgumentException.class, "Failed to find handler for format \"test\"");
    391
    392         GeometryTestUtils.assertThrowsWithMessage(
    393                 () -> fn.accept(withFileExt, null),
    394                 IllegalArgumentException.class, "Failed to find handler for file extension \"test\"");
    395
    396         GeometryTestUtils.assertThrowsWithMessage(
    397                 () -> fn.accept(noFileExt, null),
    398                 IllegalArgumentException.class, "Failed to find handler: no format specified and no file extension available");
    399     }
    400
    401     @FunctionalInterface
    402     private interface ThrowingBiConsumer<T, V> {
    403         void accept(T t, V v) throws Exception;
    404     }
    405
    406     private static final class StubReadHandler3D implements BoundaryReadHandler3D {
    407
    408         private GeometryInput inArg;
    409
    410         private Precision.DoubleEquivalence precisionArg;
    411
    412         /** {@inheritDoc} */
    413         @Override
    414         public GeometryFormat getFormat() {
    415             return TEST_FMT;
    416         }
    417
    418         /** {@inheritDoc} */
    419         @Override
    420         public BoundarySource3D read(final GeometryInput in, final Precision.DoubleEquivalence precision) {
    421             throw new UnsupportedOperationException();
    422         }
    423
    424         /** {@inheritDoc} */
    425         @Override
    426         public Stream<PlaneConvexSubset> boundaries(final GeometryInput in,
    427                 final Precision.DoubleEquivalence precision) {
    428             this.inArg = in;
    429             this.precisionArg = precision;
    430
    431             return Stream.of(TRI);
    432         }
    433
    434         /** {@inheritDoc} */
    435         @Override
    436         public FacetDefinitionReader facetDefinitionReader(final GeometryInput in) {
    437             this.inArg = in;
    438
    439             return FACET_DEF_READER;
    440         }
    441
    442         /** {@inheritDoc} */
    443         @Override
    444         public Stream<FacetDefinition> facets(final GeometryInput in) {
    445             this.inArg = in;
    446
    447             return Stream.of(FACET);
    448         }
    449
    450         /** {@inheritDoc} */
    451         @Override
    452         public TriangleMesh readTriangleMesh(final GeometryInput in, final Precision.DoubleEquivalence precision) {
    453             this.inArg = in;
    454             this.precisionArg = precision;
    455
    456             return TRI_MESH;
    457         }
    458     }
    459
    460     private static final class StubWriteHandler3D implements BoundaryWriteHandler3D {
    461
    462         private Collection<? extends PlaneConvexSubset> boundariesArg;
    463
    464         private Collection<? extends FacetDefinition> facetsArg;
    465
    466         private GeometryOutput outArg;
    467
    468         /** {@inheritDoc} */
    469         @Override
    470         public GeometryFormat getFormat() {
    471             return TEST_FMT;
    472         }
    473
    474         /** {@inheritDoc} */
    475         @Override
    476         public void write(final Stream<? extends PlaneConvexSubset> boundaries, final GeometryOutput out) {
    477             this.boundariesArg = boundaries.collect(Collectors.toList());
    478             this.outArg = out;
    479         }
    480
    481         /** {@inheritDoc} */
    482         @Override
    483         public void write(final BoundarySource3D src, final GeometryOutput out) {
    484             throw new UnsupportedOperationException();
    485         }
    486
    487         /** {@inheritDoc} */
    488         @Override
    489         public void writeFacets(final Stream<? extends FacetDefinition> facets, final GeometryOutput out) {
    490             this.facetsArg = facets.collect(Collectors.toList());
    491             this.outArg = out;
    492         }
    493
    494         /** {@inheritDoc} */
    495         @Override
    496         public void writeFacets(final Collection<? extends FacetDefinition> facets, final GeometryOutput out) {
    497             this.facetsArg = facets;
    498             this.outArg = out;
    499         }
    500     }
    501 }