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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.geometry.partitioning;
23  
24  import java.io.IOException;
25  import java.text.ParseException;
26  import java.util.StringTokenizer;
27  
28  import org.hipparchus.geometry.Point;
29  import org.hipparchus.geometry.Space;
30  import org.hipparchus.geometry.euclidean.oned.Euclidean1D;
31  import org.hipparchus.geometry.euclidean.oned.IntervalsSet;
32  import org.hipparchus.geometry.euclidean.oned.OrientedPoint;
33  import org.hipparchus.geometry.euclidean.oned.SubOrientedPoint;
34  import org.hipparchus.geometry.euclidean.oned.Vector1D;
35  import org.hipparchus.geometry.euclidean.threed.Euclidean3D;
36  import org.hipparchus.geometry.euclidean.threed.Plane;
37  import org.hipparchus.geometry.euclidean.threed.PolyhedronsSet;
38  import org.hipparchus.geometry.euclidean.threed.SubPlane;
39  import org.hipparchus.geometry.euclidean.threed.Vector3D;
40  import org.hipparchus.geometry.euclidean.twod.Euclidean2D;
41  import org.hipparchus.geometry.euclidean.twod.Line;
42  import org.hipparchus.geometry.euclidean.twod.PolygonsSet;
43  import org.hipparchus.geometry.euclidean.twod.SubLine;
44  import org.hipparchus.geometry.euclidean.twod.Vector2D;
45  import org.hipparchus.geometry.spherical.oned.ArcsSet;
46  import org.hipparchus.geometry.spherical.oned.LimitAngle;
47  import org.hipparchus.geometry.spherical.oned.S1Point;
48  import org.hipparchus.geometry.spherical.oned.Sphere1D;
49  import org.hipparchus.geometry.spherical.oned.SubLimitAngle;
50  import org.hipparchus.geometry.spherical.twod.Circle;
51  import org.hipparchus.geometry.spherical.twod.S2Point;
52  import org.hipparchus.geometry.spherical.twod.Sphere2D;
53  import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
54  import org.hipparchus.geometry.spherical.twod.SubCircle;
55  
56  /** Class parsing a string representation of an {@link AbstractRegion}.
57   * <p>
58   * This class is intended for tests and debug purposes only.
59   * </p>
60   * @see RegionDumper
61   */
62  public class RegionParser {
63  
64      /** Private constructor for a utility class
65       */
66      private RegionParser() {
67      }
68  
69      /** Parse a string representation of an {@link ArcsSet}.
70       * @param s string to parse
71       * @return parsed region
72       * @exception IOException if the string cannot be read
73       * @exception ParseException if the string cannot be parsed
74       */
75      public static ArcsSet parseArcsSet(final String s)
76          throws IOException, ParseException {
77          final TreeBuilder<Sphere1D, S1Point, LimitAngle, SubLimitAngle> builder =
78                  new TreeBuilder<Sphere1D, S1Point, LimitAngle, SubLimitAngle>("ArcsSet", s) {
79  
80              /** {@inheritDoc} */
81              @Override
82              protected LimitAngle parseHyperplane() throws ParseException {
83                  return new LimitAngle(new S1Point(getNumber()), getBoolean(), getNumber());
84              }
85  
86          };
87          return new ArcsSet(builder.getTree(), builder.getTolerance());
88      }
89  
90      /** Parse a string representation of a {@link SphericalPolygonsSet}.
91       * @param s string to parse
92       * @return parsed region
93       * @exception IOException if the string cannot be read
94       * @exception ParseException if the string cannot be parsed
95       */
96      public static SphericalPolygonsSet parseSphericalPolygonsSet(final String s)
97          throws IOException, ParseException {
98          final TreeBuilder<Sphere2D, S2Point, Circle, SubCircle> builder =
99                  new TreeBuilder<Sphere2D, S2Point, Circle, SubCircle>("SphericalPolygonsSet", s) {
100 
101             /** {@inheritDoc} */
102             @Override
103             public Circle parseHyperplane() {
104                 return new Circle(new Vector3D(getNumber(), getNumber(), getNumber()), getNumber());
105             }
106 
107         };
108         return new SphericalPolygonsSet(builder.getTree(), builder.getTolerance());
109     }
110 
111     /** Parse a string representation of an {@link IntervalsSet}.
112      * @param s string to parse
113      * @return parsed region
114      * @exception IOException if the string cannot be read
115      * @exception ParseException if the string cannot be parsed
116      */
117     public static IntervalsSet parseIntervalsSet(final String s)
118         throws IOException, ParseException {
119         final TreeBuilder<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> builder =
120                 new TreeBuilder<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>("IntervalsSet", s) {
121 
122             /** {@inheritDoc} */
123             @Override
124             public OrientedPoint parseHyperplane() throws ParseException {
125                 return new OrientedPoint(new Vector1D(getNumber()), getBoolean(), getNumber());
126             }
127 
128         };
129         return new IntervalsSet(builder.getTree(), builder.getTolerance());
130     }
131 
132     /** Parse a string representation of a {@link PolygonsSet}.
133      * @param s string to parse
134      * @return parsed region
135      * @exception IOException if the string cannot be read
136      * @exception ParseException if the string cannot be parsed
137      */
138     public static PolygonsSet parsePolygonsSet(final String s)
139         throws IOException, ParseException {
140         final TreeBuilder<Euclidean2D, Vector2D, Line, SubLine> builder =
141                 new TreeBuilder<Euclidean2D, Vector2D, Line, SubLine>("PolygonsSet", s) {
142 
143             /** {@inheritDoc} */
144             @Override
145             public Line parseHyperplane() {
146                 return new Line(new Vector2D(getNumber(), getNumber()), getNumber(), getNumber());
147             }
148 
149         };
150         return new PolygonsSet(builder.getTree(), builder.getTolerance());
151     }
152 
153     /** Parse a string representation of a {@link PolyhedronsSet}.
154      * @param s string to parse
155      * @return parsed region
156      * @exception IOException if the string cannot be read
157      * @exception ParseException if the string cannot be parsed
158      */
159     public static PolyhedronsSet parsePolyhedronsSet(final String s)
160         throws IOException, ParseException {
161         final TreeBuilder<Euclidean3D, Vector3D, Plane, SubPlane> builder =
162                 new TreeBuilder<Euclidean3D, Vector3D, Plane, SubPlane>("PolyhedronsSet", s) {
163 
164             /** {@inheritDoc} */
165             @Override
166             public Plane parseHyperplane() {
167                 return new Plane(new Vector3D(getNumber(), getNumber(), getNumber()),
168                                  new Vector3D(getNumber(), getNumber(), getNumber()),
169                                  getNumber());
170             }
171 
172         };
173         return new PolyhedronsSet(builder.getTree(), builder.getTolerance());
174     }
175 
176     /** Local class for building an {@link AbstractRegion} tree.
177      * @param <S> Type of the space.
178      * @param <P> Type of the points in space.
179      * @param <H> Type of the hyperplane.
180      * @param <I> Type of the sub-hyperplane.
181      */
182     private abstract static class TreeBuilder<S extends Space,
183                                               P extends Point<S, P>,
184                                               H extends Hyperplane<S, P, H, I>,
185                                               I extends SubHyperplane<S, P, H, I>> {
186 
187         /** Keyword for tolerance. */
188         private static final String TOLERANCE = "tolerance";
189 
190         /** Keyword for internal nodes. */
191         private static final String INTERNAL  = "internal";
192 
193         /** Keyword for leaf nodes. */
194         private static final String LEAF      = "leaf";
195 
196         /** Keyword for plus children trees. */
197         private static final String PLUS      = "plus";
198 
199         /** Keyword for minus children trees. */
200         private static final String MINUS     = "minus";
201 
202         /** Keyword for true flags. */
203         private static final String TRUE      = "true";
204 
205         /** Keyword for false flags. */
206         private static final String FALSE     = "false";
207 
208         /** Tree root. */
209         private BSPTree<S, P, H, I> root;
210 
211         /** Tolerance. */
212         private final double tolerance;
213 
214         /** Tokenizer parsing string representation. */
215         private final StringTokenizer tokenizer;
216 
217         /** Simple constructor.
218          * @param type type of the expected representation
219          * @param s string representation
220          * @exception IOException if the string cannot be read
221          * @exception ParseException if the string cannot be parsed
222          */
223         public TreeBuilder(final String type, final String s)
224             throws IOException, ParseException {
225             root = null;
226             tokenizer = new StringTokenizer(s);
227             getWord(type);
228             getWord(TOLERANCE);
229             tolerance = getNumber();
230             getWord(PLUS);
231             root = new BSPTree<>();
232             parseTree(root);
233             if (tokenizer.hasMoreTokens()) {
234                 throw new ParseException("unexpected " + tokenizer.nextToken(), 0);
235             }
236         }
237 
238         /** Parse a tree.
239          * @param node start node
240          * @exception IOException if the string cannot be read
241          * @exception ParseException if the string cannot be parsed
242          */
243         private void parseTree(final BSPTree<S, P, H, I> node)
244             throws IOException, ParseException {
245             if (INTERNAL.equals(getWord(INTERNAL, LEAF))) {
246                 // this is an internal node, it has a cut sub-hyperplane (stored as a whole hyperplane)
247                 // then a minus tree, then a plus tree
248                 node.insertCut(parseHyperplane());
249                 getWord(MINUS);
250                 parseTree(node.getMinus());
251                 getWord(PLUS);
252                 parseTree(node.getPlus());
253             } else {
254                 // this is a leaf node, it has only an inside/outside flag
255                 node.setAttribute(getBoolean());
256             }
257         }
258 
259         /** Get next word.
260          * @param allowed allowed values
261          * @return parsed word
262          * @exception ParseException if the string cannot be parsed
263          */
264         protected String getWord(final String ... allowed) throws ParseException {
265             final String token = tokenizer.nextToken();
266             for (final String a : allowed) {
267                 if (a.equals(token)) {
268                     return token;
269                 }
270             }
271             throw new ParseException(token + " != " + allowed[0], 0);
272         }
273 
274         /** Get next number.
275          * @return parsed number
276          * @exception NumberFormatException if the string cannot be parsed
277          */
278         protected double getNumber() throws NumberFormatException {
279             return Double.parseDouble(tokenizer.nextToken());
280         }
281 
282         /** Get next boolean.
283          * @return parsed boolean
284          * @exception ParseException if the string cannot be parsed
285          */
286         protected boolean getBoolean() throws ParseException {
287             return getWord(TRUE, FALSE).equals(TRUE);
288         }
289 
290         /** Get the built tree.
291          * @return built tree
292          */
293         public BSPTree<S, P, H, I> getTree() {
294             return root;
295         }
296 
297         /** Get the tolerance.
298          * @return tolerance
299          */
300         public double getTolerance() {
301             return tolerance;
302         }
303 
304         /** Parse an hyperplane.
305          * @return next hyperplane from the stream
306          * @exception IOException if the string cannot be read
307          * @exception ParseException if the string cannot be parsed
308          */
309         protected abstract H parseHyperplane()
310             throws IOException, ParseException;
311 
312     }
313 
314 }