Author: luc
Date: Mon Jan 13 12:42:40 2014
New Revision: 1557693
URL: http://svn.apache.org/r1557693
Log:
Removed too tight link between edges and BSP tree.
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java?rev=1557693&r1=1557692&r2=1557693&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
Mon Jan 13 12:42:40 2014
@@ -19,7 +19,10 @@ package org.apache.commons.math3.geometr
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import org.apache.commons.math3.exception.MathIllegalStateException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
@@ -225,16 +228,10 @@ public class SphericalPolygonsSet extend
// find an edge with an hyperplane that can be inserted in the node
int index = 0;
- Edge inserted =null;
+ Edge inserted = null;
while (inserted == null && index < edges.size()) {
inserted = edges.get(index++);
- if (inserted.getNode() == null) {
- if (node.insertCut(inserted.getCircle())) {
- inserted.setNode(node);
- } else {
- inserted = null;
- }
- } else {
+ if (!node.insertCut(inserted.getCircle())) {
inserted = null;
}
}
@@ -394,9 +391,6 @@ public class SphericalPolygonsSet extend
/** Circle supporting the edge. */
private final Circle circle;
- /** Node whose cut hyperplane contains this edge. */
- private BSPTree<Sphere2D> node;
-
/** Build an edge not contained in any node yet.
* @param start start vertex
* @param end end vertex
@@ -409,7 +403,6 @@ public class SphericalPolygonsSet extend
this.end = end;
this.length = length;
this.circle = circle;
- this.node = null;
// connect the vertices back to the edge
start.setOutgoing(this);
@@ -459,20 +452,6 @@ public class SphericalPolygonsSet extend
return circle.getPointAt(alpha +
circle.getPhase(start.getLocation().getVector()));
}
- /** Set the node whose cut hyperplane contains this edge.
- * @param node node whose cut hyperplane contains this edge
- */
- private void setNode(final BSPTree<Sphere2D> node) {
- this.node = node;
- }
-
- /** Get the node whose cut hyperplane contains this edge.
- * @return node whose cut hyperplane contains this edge
- */
- private BSPTree<Sphere2D> getNode() {
- return node;
- }
-
/** Connect the instance with a following edge.
* @param next edge following the instance
*/
@@ -584,7 +563,6 @@ public class SphericalPolygonsSet extend
// really add the edge
subEnd.bindWith(splitCircle);
final Edge edge = new Edge(subStart, subEnd, subLength, circle);
- edge.setNode(node);
list.add(edge);
return subEnd;
@@ -708,24 +686,32 @@ public class SphericalPolygonsSet extend
root.visit(visitor);
final List<Edge> edges = visitor.getEdges();
+
// convert the list of all edges into a list of start vertices
loops = new ArrayList<Vertex>();
- for (final Edge edge : edges) {
- if (edge.getNode() != null) {
+ while (!edges.isEmpty()) {
- // this is an edge belonging to a new loop, store it
- final Vertex startVertex = edge.getStart();
- loops.add(startVertex);
-
- // disable all remaining edges in the same loop
- for (Vertex vertex = edge.getEnd();
- vertex != startVertex;
- vertex = vertex.getOutgoing().getEnd()) {
- vertex.getIncoming().setNode(null);
+ // this is an edge belonging to a new loop, store it
+ Edge edge = edges.get(0);
+ final Vertex startVertex = edge.getStart();
+ loops.add(startVertex);
+
+ // remove all remaining edges in the same loop
+ do {
+
+ // remove one edge
+ for (final Iterator<Edge> iterator = edges.iterator();
iterator.hasNext();) {
+ if (iterator.next() == edge) {
+ iterator.remove();
+ break;
+ }
}
- startVertex.getIncoming().setNode(null);
- }
+ // go to next edge following the boundary loop
+ edge = edge.getEnd().getOutgoing();
+
+ } while (edge.getStart() != startVertex);
+
}
}
@@ -744,17 +730,21 @@ public class SphericalPolygonsSet extend
/** Tolerance below which points are consider to be identical. */
private final double tolerance;
- /** Boundary edges. */
- private final List<Edge> edges;
+ /** Built edges and their associated nodes. */
+ private final Map<Edge, BSPTree<Sphere2D>> edgeToNode;
+
+ /** Reversed map. */
+ private final Map<BSPTree<Sphere2D>, List<Edge>> nodeToEdgesList;
/** Simple constructor.
* @param root tree root
* @param tolerance below which points are consider to be identical
*/
public EdgesBuilder(final BSPTree<Sphere2D> root, final double
tolerance) {
- this.root = root;
- this.tolerance = tolerance;
- this.edges = new ArrayList<Edge>();
+ this.root = root;
+ this.tolerance = tolerance;
+ this.edgeToNode = new IdentityHashMap<Edge,
BSPTree<Sphere2D>>();
+ this.nodeToEdgesList = new IdentityHashMap<BSPTree<Sphere2D>,
List<Edge>>();
}
/** {@inheritDoc} */
@@ -764,6 +754,7 @@ public class SphericalPolygonsSet extend
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree<Sphere2D> node) {
+ nodeToEdgesList.put(node, new ArrayList<Edge>());
@SuppressWarnings("unchecked")
final BoundaryAttribute<Sphere2D> attribute =
(BoundaryAttribute<Sphere2D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
@@ -798,14 +789,14 @@ public class SphericalPolygonsSet extend
} else {
edge = new Edge(start, end, a.getSize(), circle);
}
- edge.setNode(node);
- edges.add(edge);
+ edgeToNode.put(edge, node);
+ nodeToEdgesList.get(node).add(edge);
}
}
/** Get the edge that should naturally follow another one.
* @param previous edge to be continued
- * @return other edge, starting where the other one ends (they
+ * @return other edge, starting where the previous one ends (they
* have not been connected yet)
* @exception MathIllegalStateException if there is not a single other
edge
*/
@@ -816,31 +807,28 @@ public class SphericalPolygonsSet extend
final S2Point point = previous.getEnd().getLocation();
final List<BSPTree<Sphere2D>> candidates =
root.getCloseCuts(point, tolerance);
- // filter the single other node we are interested in
- BSPTree<Sphere2D> selected = null;
+ // the following edge we are looking for must start from one of
the candidates nodes
+ double closest = tolerance;
+ Edge following = null;
for (final BSPTree<Sphere2D> node : candidates) {
- if (node != previous.getNode()) {
- if (selected == null) {
- selected = node;
- } else {
- throw new
MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
+ for (final Edge edge : nodeToEdgesList.get(node)) {
+ if (edge != previous && edge.getStart().getIncoming() ==
null) {
+ final Vector3D edgeStart =
edge.getStart().getLocation().getVector();
+ final double gap =
Vector3D.angle(point.getVector(), edgeStart);
+ if (gap <= closest) {
+ closest = gap;
+ following = edge;
+ }
}
}
}
- if (selected == null) {
- throw new
MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
- }
- // find the edge associated with the selected node
- for (final Edge edge : edges) {
- if (edge.getNode() == selected &&
- Vector3D.angle(point.getVector(),
edge.getStart().getLocation().getVector()) <= tolerance) {
- return edge;
- }
+ if (following == null) {
+ // this should never happen
+ throw new
MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
}
- // we should never reach this point
- throw new
MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
+ return following;
}
@@ -851,11 +839,11 @@ public class SphericalPolygonsSet extend
public List<Edge> getEdges() throws MathIllegalStateException {
// connect the edges
- for (final Edge previous : edges) {
+ for (final Edge previous : edgeToNode.keySet()) {
previous.setNextEdge(getFollowingEdge(previous));
}
- return edges;
+ return new ArrayList<Edge>(edgeToNode.keySet());
}
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java?rev=1557693&r1=1557692&r2=1557693&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
Mon Jan 13 12:42:40 2014
@@ -16,14 +16,20 @@
*/
package org.apache.commons.math3.geometry.spherical.twod;
+import java.util.ArrayList;
import java.util.List;
+import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.geometry.partitioning.Region.Location;
import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
import org.apache.commons.math3.random.Well1024a;
import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -205,4 +211,74 @@ public class SphericalPolygonsSetTest {
}
+ @Test
+ public void testModeratlyComplexShape() {
+ double tol = 0.01;
+ List<SubHyperplane<Sphere2D>> boundary = new
ArrayList<SubHyperplane<Sphere2D>>();
+ boundary.add(create(Vector3D.MINUS_J, Vector3D.PLUS_I,
Vector3D.PLUS_K, tol, 0.0, 0.5 * FastMath.PI));
+ boundary.add(create(Vector3D.MINUS_I, Vector3D.PLUS_K,
Vector3D.PLUS_J, tol, 0.0, 0.5 * FastMath.PI));
+ boundary.add(create(Vector3D.PLUS_K, Vector3D.PLUS_J,
Vector3D.MINUS_I, tol, 0.0, 0.5 * FastMath.PI));
+ boundary.add(create(Vector3D.MINUS_J, Vector3D.MINUS_I,
Vector3D.MINUS_K, tol, 0.0, 0.5 * FastMath.PI));
+ boundary.add(create(Vector3D.MINUS_I, Vector3D.MINUS_K,
Vector3D.MINUS_J, tol, 0.0, 0.5 * FastMath.PI));
+ boundary.add(create(Vector3D.PLUS_K, Vector3D.MINUS_J,
Vector3D.PLUS_I, tol, 0.0, 0.5 * FastMath.PI));
+ SphericalPolygonsSet polygon = new SphericalPolygonsSet(boundary, tol);
+
+ Assert.assertEquals(Location.OUTSIDE, polygon.checkPoint(new
S2Point(new Vector3D( 1, 1, 1).normalize())));
+ Assert.assertEquals(Location.INSIDE, polygon.checkPoint(new
S2Point(new Vector3D(-1, 1, 1).normalize())));
+ Assert.assertEquals(Location.INSIDE, polygon.checkPoint(new
S2Point(new Vector3D(-1, -1, 1).normalize())));
+ Assert.assertEquals(Location.INSIDE, polygon.checkPoint(new
S2Point(new Vector3D( 1, -1, 1).normalize())));
+ Assert.assertEquals(Location.OUTSIDE, polygon.checkPoint(new
S2Point(new Vector3D( 1, 1, -1).normalize())));
+ Assert.assertEquals(Location.OUTSIDE, polygon.checkPoint(new
S2Point(new Vector3D(-1, 1, -1).normalize())));
+ Assert.assertEquals(Location.INSIDE, polygon.checkPoint(new
S2Point(new Vector3D(-1, -1, -1).normalize())));
+ Assert.assertEquals(Location.OUTSIDE, polygon.checkPoint(new
S2Point(new Vector3D( 1, -1, -1).normalize())));
+
+ Assert.assertEquals(MathUtils.TWO_PI, polygon.getSize(), 1.0e-10);
+ Assert.assertEquals(3 * FastMath.PI, polygon.getBoundarySize(),
1.0e-10);
+
+ List<SphericalPolygonsSet.Vertex> loops = polygon.getBoundaryLoops();
+ Assert.assertEquals(1, loops.size());
+ boolean pXFound = false;
+ boolean mXFound = false;
+ boolean pYFound = false;
+ boolean mYFound = false;
+ boolean pZFound = false;
+ boolean mZFound = false;
+ SphericalPolygonsSet.Vertex first = loops.get(0);
+ int count = 0;
+ for (SphericalPolygonsSet.Vertex v = first; count == 0 || v != first;
v = v.getOutgoing().getEnd()) {
+ ++count;
+ SphericalPolygonsSet.Edge e = v.getIncoming();
+ Assert.assertTrue(v == e.getStart().getOutgoing().getEnd());
+ pXFound = pXFound ||
v.getLocation().getVector().distance(Vector3D.PLUS_I) < 1.0e-10;
+ mXFound = mXFound ||
v.getLocation().getVector().distance(Vector3D.MINUS_I) < 1.0e-10;
+ pYFound = pYFound ||
v.getLocation().getVector().distance(Vector3D.PLUS_J) < 1.0e-10;
+ mYFound = mYFound ||
v.getLocation().getVector().distance(Vector3D.MINUS_J) < 1.0e-10;
+ pZFound = pZFound ||
v.getLocation().getVector().distance(Vector3D.PLUS_K) < 1.0e-10;
+ mZFound = mZFound ||
v.getLocation().getVector().distance(Vector3D.MINUS_K) < 1.0e-10;
+ Assert.assertEquals(0.5 * FastMath.PI, e.getLength(), 1.0e-10);
+ }
+ Assert.assertTrue(pXFound);
+ Assert.assertTrue(mXFound);
+ Assert.assertTrue(pYFound);
+ Assert.assertTrue(mYFound);
+ Assert.assertTrue(pZFound);
+ Assert.assertTrue(mZFound);
+ Assert.assertEquals(6, count);
+
+ }
+
+ private SubCircle create(Vector3D pole, Vector3D x, Vector3D y,
+ double tolerance, double ... limits) {
+ RegionFactory<Sphere1D> factory = new RegionFactory<Sphere1D>();
+ Circle circle = new Circle(pole, tolerance);
+ Circle phased =
+ (Circle) Circle.getTransform(new Rotation(circle.getXAxis(),
circle.getYAxis(), x, y)).apply(circle);
+ ArcsSet set = (ArcsSet) factory.getComplement(new ArcsSet(tolerance));
+ for (int i = 0; i < limits.length; i += 2) {
+ set = (ArcsSet) factory.union(set, new ArcsSet(limits[i], limits[i
+ 1], tolerance));
+ }
+ return new SubCircle(phased, set);
+ }
+
+
}