001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.io.euclidean.threed; 018 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.List; 022import java.util.NoSuchElementException; 023import java.util.Spliterator; 024import java.util.Spliterators; 025import java.util.stream.Stream; 026import java.util.stream.StreamSupport; 027 028import org.apache.commons.geometry.euclidean.threed.BoundaryList3D; 029import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; 030import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset; 031import org.apache.commons.geometry.euclidean.threed.Triangle3D; 032import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh; 033import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh; 034import org.apache.commons.geometry.io.core.input.GeometryInput; 035import org.apache.commons.geometry.io.core.internal.GeometryIOUtils; 036import org.apache.commons.numbers.core.Precision; 037 038/** Abstract base class for {@link BoundaryReadHandler3D} implementations. 039 */ 040public abstract class AbstractBoundaryReadHandler3D implements BoundaryReadHandler3D { 041 042 /** {@inheritDoc} */ 043 @Override 044 public BoundarySource3D read(final GeometryInput in, final Precision.DoubleEquivalence precision) { 045 // read the input as a simple list of boundaries 046 final List<PlaneConvexSubset> list = new ArrayList<>(); 047 048 try (FacetDefinitionReader reader = facetDefinitionReader(in)) { 049 FacetDefinition facet; 050 while ((facet = reader.readFacet()) != null) { 051 list.add(FacetDefinitions.toPolygon(facet, precision)); 052 } 053 } 054 055 return new BoundaryList3D(list); 056 } 057 058 /** {@inheritDoc} */ 059 @Override 060 public TriangleMesh readTriangleMesh(final GeometryInput in, final Precision.DoubleEquivalence precision) { 061 final SimpleTriangleMesh.Builder meshBuilder = SimpleTriangleMesh.builder(precision); 062 063 try (FacetDefinitionReader reader = facetDefinitionReader(in)) { 064 FacetDefinition facet; 065 while ((facet = reader.readFacet()) != null) { 066 for (final Triangle3D tri : FacetDefinitions.toPolygon(facet, precision).toTriangles()) { 067 meshBuilder.addFaceUsingVertices( 068 tri.getPoint1(), 069 tri.getPoint2(), 070 tri.getPoint3() 071 ); 072 } 073 } 074 } 075 076 return meshBuilder.build(); 077 } 078 079 /** {@inheritDoc} */ 080 @Override 081 public Stream<PlaneConvexSubset> boundaries(final GeometryInput in, final Precision.DoubleEquivalence precision) { 082 return facets(in) 083 .map(f -> FacetDefinitions.toPolygon(f, precision)); 084 } 085 086 /** {@inheritDoc} */ 087 @Override 088 public Stream<FacetDefinition> facets(final GeometryInput in) { 089 return GeometryIOUtils.createCloseableStream(inputStream -> { 090 final FacetDefinitionReader fdReader = facetDefinitionReader(in); 091 final FacetDefinitionReaderIterator it = new FacetDefinitionReaderIterator(fdReader); 092 093 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED), false); 094 }, in::getInputStream); 095 } 096 097 /** Class exposing a {@link FacetDefinitionReader} as an iterator. 098 */ 099 static final class FacetDefinitionReaderIterator implements Iterator<FacetDefinition> { 100 101 /** Reader supplying the facets for iteration. */ 102 private final FacetDefinitionReader reader; 103 104 /** Number of facets read from the reader. */ 105 private int loadCount = 0; 106 107 /** Next facet to return from the instance; may be null. */ 108 private FacetDefinition next; 109 110 /** Construct a new iterator instance that iterates through the facets available from the 111 * argument. 112 * @param reader read supplying facets for iteration 113 */ 114 FacetDefinitionReaderIterator(final FacetDefinitionReader reader) { 115 this.reader = reader; 116 } 117 118 /** {@inheritDoc} */ 119 @Override 120 public boolean hasNext() { 121 ensureLoaded(); 122 return next != null; 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public FacetDefinition next() { 128 if (!hasNext()) { 129 throw new NoSuchElementException(); 130 } 131 132 final FacetDefinition result = next; 133 loadNext(); 134 135 return result; 136 } 137 138 /** Ensure that the instance has attempted to load at least one facet from 139 * the underlying reader. 140 */ 141 private void ensureLoaded() { 142 if (loadCount < 1) { 143 loadNext(); 144 } 145 } 146 147 /** Load the next facet from the underlying reader. 148 */ 149 private void loadNext() { 150 ++loadCount; 151 next = reader.readFacet(); 152 } 153 } 154}