Removed too tight link between edges and BSP tree.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1557693 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0f13370f35
commit
cfee716182
|
@ -19,7 +19,10 @@ package org.apache.commons.math3.geometry.spherical.twod;
|
|||
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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
|
||||
// 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
/** 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
// 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
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);
|
||||
// this is an edge belonging to a new loop, store it
|
||||
Edge edge = edges.get(0);
|
||||
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);
|
||||
// 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
/** 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
|
||||
/** {@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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
} 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 extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
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) {
|
||||
|
||||
if (following == null) {
|
||||
// this should never happen
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// we should never reach this point
|
||||
throw new MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
|
||||
return following;
|
||||
|
||||
}
|
||||
|
||||
|
@ -851,11 +839,11 @@ public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
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());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue