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