commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From l..@apache.org
Subject [1/3] [math] Added dump/parse utilities for BSP trees.
Date Thu, 09 Apr 2015 20:30:36 GMT
Repository: commons-math
Updated Branches:
  refs/heads/MATH_3_X b4d2c75d3 -> 5c07e5487


Added dump/parse utilities for BSP trees.

These utilities are for test and debug purposes only.

Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/9744a812
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/9744a812
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/9744a812

Branch: refs/heads/MATH_3_X
Commit: 9744a812bdca34cdfc3663e4fbcd40df73def460
Parents: b4d2c75
Author: Luc Maisonobe <luc@apache.org>
Authored: Thu Apr 9 22:13:45 2015 +0200
Committer: Luc Maisonobe <luc@apache.org>
Committed: Thu Apr 9 22:19:06 2015 +0200

----------------------------------------------------------------------
 .../euclidean/threed/PolyhedronsSetTest.java    |  57 ++++
 .../geometry/partitioning/RegionDumper.java     | 244 +++++++++++++++
 .../geometry/partitioning/RegionParser.java     | 303 +++++++++++++++++++
 3 files changed, 604 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/9744a812/src/test/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSetTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSetTest.java
b/src/test/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSetTest.java
index b23fc5d..ce7c49d 100644
--- a/src/test/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSetTest.java
+++ b/src/test/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSetTest.java
@@ -16,7 +16,11 @@
  */
 package org.apache.commons.math3.geometry.euclidean.threed;
 
+import java.io.IOException;
+import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.commons.math3.exception.MathArithmeticException;
 import org.apache.commons.math3.exception.MathIllegalArgumentException;
@@ -29,7 +33,9 @@ import org.apache.commons.math3.geometry.partitioning.BSPTree;
 import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
 import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
 import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.RegionDumper;
 import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.RegionParser;
 import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
 import org.apache.commons.math3.util.FastMath;
 import org.junit.Assert;
@@ -307,6 +313,57 @@ public class PolyhedronsSetTest {
         }
     }
 
+    @Test
+    public void testDumpParse() throws IOException, ParseException {
+        double tol=1e-8;
+        
+            Vector3D[] verts=new Vector3D[8];
+            double xmin=-1,xmax=1;
+            double ymin=-1,ymax=1;
+            double zmin=-1,zmax=1;
+            verts[0]=new Vector3D(xmin,ymin,zmin);
+            verts[1]=new Vector3D(xmax,ymin,zmin);
+            verts[2]=new Vector3D(xmax,ymax,zmin);
+            verts[3]=new Vector3D(xmin,ymax,zmin);
+            verts[4]=new Vector3D(xmin,ymin,zmax);
+            verts[5]=new Vector3D(xmax,ymin,zmax);
+            verts[6]=new Vector3D(xmax,ymax,zmax);
+            verts[7]=new Vector3D(xmin,ymax,zmax);
+            //
+            int[][] faces=new int[12][];
+            faces[0]=new int[]{3,1,0};  // bottom (-z)
+            faces[1]=new int[]{1,3,2};  // bottom (-z)
+            faces[2]=new int[]{5,7,4};  // top (+z)
+            faces[3]=new int[]{7,5,6};  // top (+z)
+            faces[4]=new int[]{2,5,1};  // right (+x)
+            faces[5]=new int[]{5,2,6};  // right (+x)
+            faces[6]=new int[]{4,3,0};  // left (-x)
+            faces[7]=new int[]{3,4,7};  // left (-x)
+            faces[8]=new int[]{4,1,5};  // front (-y)
+            faces[9]=new int[]{1,4,0};  // front (-y)
+            faces[10]=new int[]{3,6,2}; // back (+y)
+            faces[11]=new int[]{6,3,7}; // back (+y)
+            //
+            Set<SubHyperplane<Euclidean3D>> pset=new HashSet<>();
+            for (int f=0; f<faces.length; f++) {
+                int[] vidx=faces[f];
+                Plane p=new Plane(verts[vidx[0]],verts[vidx[1]],verts[vidx[2]],tol);
+                Vector2D p0=p.toSubSpace(verts[vidx[0]]);
+                Vector2D p1=p.toSubSpace(verts[vidx[1]]);
+                Vector2D p2=p.toSubSpace(verts[vidx[2]]);
+                PolygonsSet lset=new PolygonsSet(tol,p0,p1,p2);
+                pset.add(new SubPlane(p,lset));
+            }
+            PolyhedronsSet polyset=new PolyhedronsSet(pset,tol);
+            Assert.assertEquals(8.0, polyset.getSize(), 1.0e-10);
+            Assert.assertEquals(24.0, polyset.getBoundarySize(), 1.0e-10);
+            String dump = RegionDumper.dump(polyset);
+            PolyhedronsSet parsed = RegionParser.parsePolyhedronsSet(dump);
+            Assert.assertEquals(8.0, parsed.getSize(), 1.0e-10);
+            Assert.assertEquals(24.0, parsed.getBoundarySize(), 1.0e-10);
+            Assert.assertTrue(new RegionFactory<Euclidean3D>().difference(polyset,
parsed).isEmpty());
+    }
+
     private void checkPoints(Region.Location expected, PolyhedronsSet tree, Vector3D[] points)
{
         for (int i = 0; i < points.length; ++i) {
             Assert.assertEquals(expected, tree.checkPoint(points[i]));

http://git-wip-us.apache.org/repos/asf/commons-math/blob/9744a812/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionDumper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionDumper.java
b/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionDumper.java
new file mode 100644
index 0000000..33be9d7
--- /dev/null
+++ b/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionDumper.java
@@ -0,0 +1,244 @@
+/*
+ * 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.math3.geometry.partitioning;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.euclidean.threed.Euclidean3D;
+import org.apache.commons.math3.geometry.euclidean.threed.Plane;
+import org.apache.commons.math3.geometry.euclidean.threed.PolyhedronsSet;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.Line;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.LimitAngle;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+import org.apache.commons.math3.geometry.spherical.twod.Circle;
+import org.apache.commons.math3.geometry.spherical.twod.Sphere2D;
+import org.apache.commons.math3.geometry.spherical.twod.SphericalPolygonsSet;
+
+/** Class dumping a string representation of an {@link AbstractRegion}.
+ * <p>
+ * This class is intended for tests and debug purposes only.
+ * </p>
+ * @see RegionParser
+ * @since 3.5
+ */
+public class RegionDumper {
+
+    /** Private constructor for a utility class
+     */
+    private RegionDumper() {    
+    }
+
+    /** Get a string representation of an {@link ArcsSet}.
+     * @param arcsSet region to dump
+     * @return string representation of the region
+     */
+    public static String dump(final ArcsSet arcsSet) {
+        final TreeDumper<Sphere1D> visitor = new TreeDumper<Sphere1D>("ArcsSet",
arcsSet.getTolerance()) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected void formatHyperplane(final Hyperplane<Sphere1D> hyperplane)
{
+                final LimitAngle h = (LimitAngle) hyperplane;
+                getFormatter().format("%22.15e %b %22.15e",
+                                      h.getLocation().getAlpha(), h.isDirect(), h.getTolerance());
+            }
+
+        };
+        arcsSet.getTree(false).visit(visitor);
+        return visitor.getDump();
+    }
+
+    /** Get a string representation of a {@link SphericalPolygonsSet}.
+     * @param sphericalPolygonsSet region to dump
+     * @return string representation of the region
+     */
+    public static String dump(final SphericalPolygonsSet sphericalPolygonsSet) {
+        final TreeDumper<Sphere2D> visitor = new TreeDumper<Sphere2D>("SphericalPolygonsSet",
sphericalPolygonsSet.getTolerance()) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected void formatHyperplane(final Hyperplane<Sphere2D> hyperplane)
{
+                final Circle h = (Circle) hyperplane;
+                getFormatter().format("%22.15e %22.15e %22.15e %22.15e",
+                                      h.getPole().getX(), h.getPole().getY(), h.getPole().getZ(),
+                                      h.getTolerance());
+            }
+
+        };
+        sphericalPolygonsSet.getTree(false).visit(visitor);
+        return visitor.getDump();
+    }
+
+    /** Get a string representation of an {@link IntervalsSet}.
+     * @param intervalsSet region to dump
+     * @return string representation of the region
+     */
+    public static String dump(final IntervalsSet intervalsSet) {
+        final TreeDumper<Euclidean1D> visitor = new TreeDumper<Euclidean1D>("IntervalsSet",
intervalsSet.getTolerance()) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected void formatHyperplane(final Hyperplane<Euclidean1D> hyperplane)
{
+                final OrientedPoint h = (OrientedPoint) hyperplane;
+                getFormatter().format("%22.15e %b %22.15e",
+                                      h.getLocation().getX(), h.isDirect(), h.getTolerance());
+            }
+
+        };
+        intervalsSet.getTree(false).visit(visitor);
+        return visitor.getDump();
+    }
+
+    /** Get a string representation of a {@link PolygonsSet}.
+     * @param polygonsSet region to dump
+     * @return string representation of the region
+     */
+    public static String dump(final PolygonsSet polygonsSet) {
+        final TreeDumper<Euclidean2D> visitor = new TreeDumper<Euclidean2D>("PolygonsSet",
polygonsSet.getTolerance()) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected void formatHyperplane(final Hyperplane<Euclidean2D> hyperplane)
{
+                final Line h = (Line) hyperplane;
+                final Vector2D p = h.toSpace(Vector1D.ZERO);
+                getFormatter().format("%22.15e %22.15e %22.15e %22.15e",
+                                      p.getX(), p.getY(), h.getAngle(), h.getTolerance());
+            }
+
+        };
+        polygonsSet.getTree(false).visit(visitor);
+        return visitor.getDump();
+    }
+
+    /** Get a string representation of a {@link PolyhedronsSet}.
+     * @param polyhedronsSet region to dump
+     * @return string representation of the region
+     */
+    public static String dump(final PolyhedronsSet polyhedronsSet) {
+        final TreeDumper<Euclidean3D> visitor = new TreeDumper<Euclidean3D>("PolyhedronsSet",
polyhedronsSet.getTolerance()) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected void formatHyperplane(final Hyperplane<Euclidean3D> hyperplane)
{
+                final Plane h = (Plane) hyperplane;
+                final Vector3D p = h.toSpace(Vector2D.ZERO);
+                getFormatter().format("%22.15e %22.15e %22.15e %22.15e %22.15e %22.15e %22.15e",
+                                      p.getX(), p.getY(), p.getZ(),
+                                      h.getNormal().getX(), h.getNormal().getY(), h.getNormal().getZ(),
+                                      h.getTolerance());
+            }
+
+        };
+        polyhedronsSet.getTree(false).visit(visitor);
+        return visitor.getDump();
+    }
+
+    /** Dumping visitor.
+     * @param <S> Type of the space.
+     */
+    private abstract static class TreeDumper<S extends Space> implements BSPTreeVisitor<S>
{
+
+        /** Builder for the string representation of the dumped tree. */
+        private final StringBuilder dump;
+
+        /** Formatter for strings. */
+        private final Formatter formatter;
+
+        /** Current indentation prefix. */
+        private String prefix;
+
+        /** Simple constructor.
+         * @param type type of the region to dump
+         * @param tolerance tolerance of the region
+         */
+        public TreeDumper(final String type, final double tolerance) {
+            this.dump      = new StringBuilder();
+            this.formatter = new Formatter(dump, Locale.US);
+            this.prefix    = "";
+            formatter.format("%s%n", type);
+            formatter.format("tolerance %22.15e%n", tolerance);
+        }
+
+        /** Get the string representation of the tree.
+         * @return string representation of the tree.
+         */
+        public String getDump() {
+            return dump.toString();
+        }
+
+        /** Get the formatter to use.
+         * @return formatter to use
+         */
+        protected Formatter getFormatter() {
+            return formatter;
+        }
+
+        /** Format a string representation of the hyperplane underlying a cut sub-hyperplane.
+         * @param hyperplane hyperplane to format
+         */
+        protected abstract void formatHyperplane(Hyperplane<S> hyperplane);
+
+        /** {@inheritDoc} */
+        @Override
+        public Order visitOrder(final BSPTree<S> node) {
+            return Order.SUB_MINUS_PLUS;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitInternalNode(final BSPTree<S> node) {
+            formatter.format("%s %s internal ", prefix, type(node));
+            formatHyperplane(node.getCut().getHyperplane());
+            formatter.format("%n");
+            prefix = prefix + "  ";
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitLeafNode(final BSPTree<S> node) {
+            formatter.format("%s %s leaf %s%n",
+                             prefix, type(node), node.getAttribute());
+            for (BSPTree<S> n = node;
+                 n.getParent() != null && n == n.getParent().getPlus();
+                 n = n.getParent()) {
+                prefix = prefix.substring(0, prefix.length() - 2);
+            }
+        }
+
+        /** Get the type of the node.
+         * @param node node to check
+         * @return "plus " or "minus" depending on the node being the plus or minus
+         * child of its parent ("plus " is arbitrarily returned for the root node)
+         */
+        private String type(final BSPTree<S> node) {
+            return (node.getParent() != null && node == node.getParent().getMinus())
? "minus" : "plus ";
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/9744a812/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionParser.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionParser.java
b/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionParser.java
new file mode 100644
index 0000000..8fc1e6e
--- /dev/null
+++ b/src/test/java/org/apache/commons/math3/geometry/partitioning/RegionParser.java
@@ -0,0 +1,303 @@
+/*
+ * 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.math3.geometry.partitioning;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.StringTokenizer;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.euclidean.threed.Euclidean3D;
+import org.apache.commons.math3.geometry.euclidean.threed.Plane;
+import org.apache.commons.math3.geometry.euclidean.threed.PolyhedronsSet;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.Line;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.LimitAngle;
+import org.apache.commons.math3.geometry.spherical.oned.S1Point;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+import org.apache.commons.math3.geometry.spherical.twod.Circle;
+import org.apache.commons.math3.geometry.spherical.twod.Sphere2D;
+import org.apache.commons.math3.geometry.spherical.twod.SphericalPolygonsSet;
+
+/** Class parsing a string representation of an {@link AbstractRegion}.
+ * <p>
+ * This class is intended for tests and debug purposes only.
+ * </p>
+ * @see RegionDumper
+ * @since 3.5
+ */
+public class RegionParser {
+
+    /** Private constructor for a utility class
+     */
+    private RegionParser() {    
+    }
+
+    /** Parse a string representation of an {@link ArcsSet}.
+     * @param s string to parse
+     * @return parsed region
+     * @exception IOException if the string cannot be read
+     * @exception ParseException if the string cannot be parsed
+     */
+    public static ArcsSet parseArcsSet(final String s)
+        throws IOException, ParseException {
+        final TreeBuilder<Sphere1D> builder = new TreeBuilder<Sphere1D>("ArcsSet",
s) {
+
+            /** {@inheritDoc} */
+            @Override
+            protected LimitAngle parseHyperplane()
+                throws IOException, ParseException {
+                return new LimitAngle(new S1Point(getNumber()), getBoolean(), getNumber());
+            }
+
+        };
+        return new ArcsSet(builder.getTree(), builder.getTolerance());
+    }
+
+    /** Parse a string representation of a {@link SphericalPolygonsSet}.
+     * @param s string to parse
+     * @return parsed region
+     * @exception IOException if the string cannot be read
+     * @exception ParseException if the string cannot be parsed
+     */
+    public static SphericalPolygonsSet parseSphericalPolygonsSet(final String s)
+        throws IOException, ParseException {
+        final TreeBuilder<Sphere2D> builder = new TreeBuilder<Sphere2D>("SphericalPolygonsSet",
s) {
+
+            /** {@inheritDoc} */
+            @Override
+            public Circle parseHyperplane()
+                throws IOException, ParseException {
+                return new Circle(new Vector3D(getNumber(), getNumber(), getNumber()), getNumber());
+            }
+
+        };
+        return new SphericalPolygonsSet(builder.getTree(), builder.getTolerance());
+    }
+
+    /** Parse a string representation of an {@link IntervalsSet}.
+     * @param s string to parse
+     * @return parsed region
+     * @exception IOException if the string cannot be read
+     * @exception ParseException if the string cannot be parsed
+     */
+    public static IntervalsSet parseIntervalsSet(final String s)
+        throws IOException, ParseException {
+        final TreeBuilder<Euclidean1D> builder = new TreeBuilder<Euclidean1D>("IntervalsSet",
s) {
+
+            /** {@inheritDoc} */
+            @Override
+            public OrientedPoint parseHyperplane()
+                throws IOException, ParseException {
+                return new OrientedPoint(new Vector1D(getNumber()), getBoolean(), getNumber());
+            }
+
+        };
+        return new IntervalsSet(builder.getTree(), builder.getTolerance());
+    }
+
+    /** Parse a string representation of a {@link PolygonsSet}.
+     * @param s string to parse
+     * @return parsed region
+     * @exception IOException if the string cannot be read
+     * @exception ParseException if the string cannot be parsed
+     */
+    public static PolygonsSet parsePolygonsSet(final String s)
+        throws IOException, ParseException {
+        final TreeBuilder<Euclidean2D> builder = new TreeBuilder<Euclidean2D>("PolygonsSet",
s) {
+
+            /** {@inheritDoc} */
+            @Override
+            public Line parseHyperplane()
+                throws IOException, ParseException {
+                return new Line(new Vector2D(getNumber(), getNumber()), getNumber(), getNumber());
+            }
+
+        };
+        return new PolygonsSet(builder.getTree(), builder.getTolerance());
+    }
+
+    /** Parse a string representation of a {@link PolyhedronsSet}.
+     * @param s string to parse
+     * @return parsed region
+     * @exception IOException if the string cannot be read
+     * @exception ParseException if the string cannot be parsed
+     */
+    public static PolyhedronsSet parsePolyhedronsSet(final String s)
+        throws IOException, ParseException {
+        final TreeBuilder<Euclidean3D> builder = new TreeBuilder<Euclidean3D>("PolyhedronsSet",
s) {
+
+            /** {@inheritDoc} */
+            @Override
+            public Plane parseHyperplane()
+                throws IOException, ParseException {
+                return new Plane(new Vector3D(getNumber(), getNumber(), getNumber()),
+                                 new Vector3D(getNumber(), getNumber(), getNumber()),
+                                 getNumber());
+            }
+
+        };
+        return new PolyhedronsSet(builder.getTree(), builder.getTolerance());
+    }
+
+    /** Local class for building an {@link AbstractRegion} tree.
+     * @param <S> Type of the space.
+     */
+    private abstract static class TreeBuilder<S extends Space> {
+
+        /** Keyword for tolerance. */
+        private static final String TOLERANCE = "tolerance";
+
+        /** Keyword for internal nodes. */
+        private static final String INTERNAL  = "internal";
+
+        /** Keyword for leaf nodes. */
+        private static final String LEAF      = "leaf";
+
+        /** Keyword for plus children trees. */
+        private static final String PLUS      = "plus";
+
+        /** Keyword for minus children trees. */
+        private static final String MINUS     = "minus";
+
+        /** Keyword for true flags. */
+        private static final String TRUE      = "true";
+
+        /** Keyword for false flags. */
+        private static final String FALSE     = "false";
+
+        /** Tree root. */
+        private BSPTree<S> root;
+
+        /** Tolerance. */
+        private final double tolerance;
+
+        /** Tokenizer parsing string representation. */
+        private final StringTokenizer tokenizer;
+
+        /** Simple constructor.
+         * @param type type of the expected representation
+         * @param reader reader for the string representation
+         * @exception IOException if the string cannot be read
+         * @exception ParseException if the string cannot be parsed
+         */
+        public TreeBuilder(final String type, final String s)
+            throws IOException, ParseException {
+            root = null;
+            tokenizer = new StringTokenizer(s);
+            getWord(type);
+            getWord(TOLERANCE);
+            tolerance = getNumber();
+            getWord(PLUS);
+            root = new BSPTree<S>();
+            parseTree(root);
+            if (tokenizer.hasMoreTokens()) {
+                throw new ParseException("unexpected " + tokenizer.nextToken(), 0);
+            }
+        }
+
+        /** Parse a tree.
+         * @param node start node
+         * @exception IOException if the string cannot be read
+         * @exception ParseException if the string cannot be parsed
+         */
+        private void parseTree(final BSPTree<S> node)
+            throws IOException, ParseException {
+            if (INTERNAL.equals(getWord(INTERNAL, LEAF))) {
+                // this is an internal node, it has a cut sub-hyperplane (stored as a whole
hyperplane)
+                // then a minus tree, then a plus tree
+                node.insertCut(parseHyperplane());
+                getWord(MINUS);
+                parseTree(node.getMinus());
+                getWord(PLUS);
+                parseTree(node.getPlus());
+            } else {
+                // this is a leaf node, it has only an inside/outside flag
+                node.setAttribute(getBoolean());
+            }
+        }
+
+        /** Get next word.
+         * @param allowed allowed values
+         * @return parsed word
+         * @exception IOException if the string cannot be read
+         * @exception ParseException if the string cannot be parsed
+         */
+        protected String getWord(final String ... allowed)
+            throws IOException, ParseException {
+            final String token = tokenizer.nextToken();
+            for (final String a : allowed) {
+                if (a.equals(token)) {
+                    return token;
+                }
+            }
+            throw new ParseException(token + " != " + allowed[0], 0);
+        }
+
+        /** Get next number.
+         * @return parsed number
+         * @exception IOException if the string cannot be read
+         * @exception NumberFormatException if the string cannot be parsed
+         */
+        protected double getNumber()
+            throws IOException, NumberFormatException {
+            return Double.parseDouble(tokenizer.nextToken());
+        }
+
+        /** Get next boolean.
+         * @return parsed boolean
+         * @exception IOException if the string cannot be read
+         * @exception ParseException if the string cannot be parsed
+         */
+        protected boolean getBoolean()
+            throws IOException, ParseException {
+            return getWord(TRUE, FALSE).equals(TRUE);
+        }
+
+        /** Get the built tree.
+         * @return built tree
+         */
+        public BSPTree<S> getTree() {
+            return root;
+        }
+
+        /** Get the tolerance.
+         * @return tolerance
+         */
+        public double getTolerance() {
+            return tolerance;
+        }
+
+        /** Parse an hyperplane.
+         * @return next hyperplane from the stream
+         * @exception IOException if the string cannot be read
+         * @exception ParseException if the string cannot be parsed
+         */
+        protected abstract Hyperplane<S> parseHyperplane()
+            throws IOException, ParseException;
+
+    }
+
+}


Mime
View raw message