Improved navigation within sub-arcs.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1554655 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2014-01-01 17:30:34 +00:00
parent d83cdb9133
commit 169a316003
2 changed files with 128 additions and 88 deletions

View File

@ -176,6 +176,19 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
* or null if there are no internal nodes (i.e. the set is either empty or covers the full circle) * or null if there are no internal nodes (i.e. the set is either empty or covers the full circle)
*/ */
public LimitAngle getSmallestLimit() { public LimitAngle getSmallestLimit() {
final BSPTree<Sphere1D> first = getFirstArcStart();
if (first == null) {
return null;
} else {
return (LimitAngle) first.getCut().getHyperplane();
}
}
/** Get the node corresponding to the first arc start.
* @return smallest internal node (i.e. first after 0.0 radians, in trigonometric direction),
* or null if there are no internal nodes (i.e. the set is either empty or covers the full circle)
*/
private BSPTree<Sphere1D> getFirstArcStart() {
// start search at the tree root // start search at the tree root
BSPTree<Sphere1D> node = getTree(false); BSPTree<Sphere1D> node = getTree(false);
@ -183,35 +196,73 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
return null; return null;
} }
// walk tree until we find the smallest internal node
BSPTree<Sphere1D> previous = previousNode(node); BSPTree<Sphere1D> previous = previousNode(node);
while (previous != null) { while (previous != null) {
node = previous; node = previous;
previous = previousNode(node); previous = previousNode(node);
} }
return (LimitAngle) node.getCut().getHyperplane(); // walk tree until we find an arc start
while (node != null && !isArcStart(node)) {
node = nextNode(node);
}
return node;
} }
/** Get the largest limit angle in the set. /** Check if a node corresponds to the start angle of an arc.
* @return largest limit angle (i.e. last before or at \(2 \pi) radians, in trigonometric direction), * @param node node to check
* or null if there are no limits (i.e. the set is either empty or covers the full circle) * @return true if the node corresponds to the start angle of an arc
*/ */
public LimitAngle getLargestLimit() { private boolean isArcStart(final BSPTree<Sphere1D> node) {
// start search at the tree root
BSPTree<Sphere1D> node = getTree(false);
if (node.getCut() == null) { if (node.getCut() == null) {
return null; // it's not even a limit angle, it cannot start an arc!
return false;
} }
BSPTree<Sphere1D> next = nextNode(node); if ((Boolean) leafBefore(node).getAttribute()) {
while (next != null) { // it has an inside cell before it, it may end an arc but not start it
node = next; return false;
next = nextNode(node);
} }
return (LimitAngle) node.getCut().getHyperplane(); if (!(Boolean) leafAfter(node).getAttribute()) {
// it has an outside cell after it, it is a dummy cut away from real arcs
return false;
}
// the cell defines a limit angle, with an outside before and an inside after
// it is the start of an arc
return true;
}
/** Check if a node corresponds to the end angle of an arc.
* @param node node to check
* @return true if the node corresponds to the end angle of an arc
*/
private boolean isArcEnd(final BSPTree<Sphere1D> node) {
if (node.getCut() == null) {
// it's not even a limit angle, it cannot start an arc!
return false;
}
if (!(Boolean) leafBefore(node).getAttribute()) {
// it has an outside cell before it, it may start an arc but not end it
return false;
}
if ((Boolean) leafAfter(node).getAttribute()) {
// it has an inside cell after it, it is a dummy cut in the middle of an arc
return false;
}
// the cell defines a limit angle, with an inside before and an outside after
// it is the end of an arc
return true;
} }
@ -222,13 +273,9 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
*/ */
private BSPTree<Sphere1D> nextNode(BSPTree<Sphere1D> node) { private BSPTree<Sphere1D> nextNode(BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> nextDeeper = if (childAfter(node).getCut() != null) {
((LimitAngle) node.getCut().getHyperplane()).isDirect() ?
node.getPlus() : node.getMinus();
if (nextDeeper.getCut() != null) {
// the next node is in the sub-tree // the next node is in the sub-tree
return findSmallest(nextDeeper); return leafAfter(node).getParent();
} }
// there is nothing left deeper in the tree, we backtrack // there is nothing left deeper in the tree, we backtrack
@ -246,13 +293,9 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
*/ */
private BSPTree<Sphere1D> previousNode(BSPTree<Sphere1D> node) { private BSPTree<Sphere1D> previousNode(BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> nextDeeper = if (childBefore(node).getCut() != null) {
((LimitAngle) node.getCut().getHyperplane()).isDirect() ?
node.getMinus() : node.getPlus();
if (nextDeeper.getCut() != null) {
// the next node is in the sub-tree // the next node is in the sub-tree
return findLargest(nextDeeper); return leafBefore(node).getParent();
} }
// there is nothing left deeper in the tree, we backtrack // there is nothing left deeper in the tree, we backtrack
@ -263,6 +306,42 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
} }
/** Find the leaf node just before an internal node.
* @param node node at which the sub-tree starts
* @return leaf node just before the internal node
*/
private BSPTree<Sphere1D> leafBefore(BSPTree<Sphere1D> node) {
if (node.getCut() != null) {
node = childBefore(node);
}
while (node.getCut() != null) {
node = childAfter(node);
}
return node;
}
/** Find the leaf node just after an internal node.
* @param node node at which the sub-tree starts
* @return leaf node just after the internal node
*/
private BSPTree<Sphere1D> leafAfter(BSPTree<Sphere1D> node) {
if (node.getCut() != null) {
node = childAfter(node);
}
while (node.getCut() != null) {
node = childBefore(node);
}
return node;
}
/** Check if a node is the child before its parent in trigonometric order. /** Check if a node is the child before its parent in trigonometric order.
* @param node child node considered * @param node child node considered
* @return true is the node has a parent end is before it in trigonometric order * @return true is the node has a parent end is before it in trigonometric order
@ -271,13 +350,8 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
final BSPTree<Sphere1D> parent = node.getParent(); final BSPTree<Sphere1D> parent = node.getParent();
if (parent == null) { if (parent == null) {
return false; return false;
}
if (((LimitAngle) parent.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
return node == parent.getMinus();
} else { } else {
// smaller angles are on plus side, larger angles are on minus side return node == childBefore(parent);
return node == parent.getPlus();
} }
} }
@ -289,62 +363,37 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
final BSPTree<Sphere1D> parent = node.getParent(); final BSPTree<Sphere1D> parent = node.getParent();
if (parent == null) { if (parent == null) {
return false; return false;
} else {
return node == childAfter(parent);
} }
if (((LimitAngle) parent.getCut().getHyperplane()).isDirect()) { }
/** Find the child node just before an internal node.
* @param node node at which the sub-tree starts
* @return child node just before the internal node
*/
private BSPTree<Sphere1D> childBefore(BSPTree<Sphere1D> node) {
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side // smaller angles are on minus side, larger angles are on plus side
return node == parent.getPlus(); return node.getMinus();
} else { } else {
// smaller angles are on plus side, larger angles are on minus side // smaller angles are on plus side, larger angles are on minus side
return node == parent.getMinus(); return node.getPlus();
} }
} }
/** Find the smallest internal node in a sub-tree. /** Find the child node just after an internal node.
* @param node node at which the sub-tree starts * @param node node at which the sub-tree starts
* @return smallest internal node (in trigonometric order), may be the * @return child node just after the internal node
* provided node if no smaller internal node exist
*/ */
private BSPTree<Sphere1D> findSmallest(BSPTree<Sphere1D> node) { private BSPTree<Sphere1D> childAfter(BSPTree<Sphere1D> node) {
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) {
BSPTree<Sphere1D> internal = null; // smaller angles are on minus side, larger angles are on plus side
return node.getPlus();
while (node.getCut() != null) { } else {
internal = node; // smaller angles are on plus side, larger angles are on minus side
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) { return node.getMinus();
// smaller angles are on minus side, larger angles are on plus side
node = node.getMinus();
} else {
// smaller angles are on plus side, larger angles are on minus side
node = node.getPlus();
}
} }
return internal;
}
/** Find the largest internal node in a sub-tree.
* @param node node at which the sub-tree starts
* @return largest internal node (in trigonometric order), may be the
* provided node if no larger internal node exist
*/
private BSPTree<Sphere1D> findLargest(BSPTree<Sphere1D> node) {
BSPTree<Sphere1D> internal = null;
while (node.getCut() != null) {
internal = node;
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
node = node.getPlus();
} else {
// smaller angles are on plus side, larger angles are on minus side
node = node.getMinus();
}
}
return internal;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

View File

@ -48,8 +48,6 @@ public class ArcsSetTest {
Assert.assertEquals(5.7, set.asList().get(0).getSup(), 1.0e-10); Assert.assertEquals(5.7, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10); Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertFalse(set.getSmallestLimit().isDirect()); Assert.assertFalse(set.getSmallestLimit().isDirect());
Assert.assertEquals(5.7, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertTrue(set.getLargestLimit().isDirect());
} }
@Test @Test
@ -66,10 +64,8 @@ public class ArcsSetTest {
Assert.assertEquals(1, set.asList().size()); Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(5.7, set.asList().get(0).getInf(), 1.0e-10); Assert.assertEquals(5.7, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(2.3 + MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10); Assert.assertEquals(2.3 + MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10); Assert.assertEquals(5.7, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertTrue(set.getSmallestLimit().isDirect()); Assert.assertFalse(set.getSmallestLimit().isDirect());
Assert.assertEquals(5.7, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertFalse(set.getLargestLimit().isDirect());
} }
@Test(expected=NumberIsTooLargeException.class) @Test(expected=NumberIsTooLargeException.class)
@ -82,7 +78,6 @@ public class ArcsSetTest {
ArcsSet set = new ArcsSet(1.0, 1.0, 1.0e-10); ArcsSet set = new ArcsSet(1.0, 1.0, 1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20); Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertNull(set.getSmallestLimit()); Assert.assertNull(set.getSmallestLimit());
Assert.assertNull(set.getLargestLimit());
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0)));
for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) { for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) {
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha)));
@ -98,7 +93,6 @@ public class ArcsSetTest {
ArcsSet set = new ArcsSet(1.0e-10); ArcsSet set = new ArcsSet(1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20); Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertNull(set.getSmallestLimit()); Assert.assertNull(set.getSmallestLimit());
Assert.assertNull(set.getLargestLimit());
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0)));
for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) { for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) {
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha)));
@ -116,7 +110,6 @@ public class ArcsSetTest {
Assert.assertEquals(0.0, empty.getSize(), 1.0e-10); Assert.assertEquals(0.0, empty.getSize(), 1.0e-10);
Assert.assertTrue(empty.asList().isEmpty()); Assert.assertTrue(empty.asList().isEmpty());
Assert.assertNull(empty.getSmallestLimit()); Assert.assertNull(empty.getSmallestLimit());
Assert.assertNull(empty.getLargestLimit());
} }
@Test @Test
@ -128,7 +121,6 @@ public class ArcsSetTest {
Assert.assertEquals(0.0, tiny.asList().get(0).getInf(), 1.0e-10); Assert.assertEquals(0.0, tiny.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.asList().get(0).getSup(), 1.0e-10); Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(0.0, tiny.getSmallestLimit().getLocation().getAlpha(), 1.0e-10); Assert.assertEquals(0.0, tiny.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
} }
@Test @Test
@ -144,7 +136,6 @@ public class ArcsSetTest {
Assert.assertEquals(MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10); Assert.assertEquals(MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(0.0, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10); Assert.assertEquals(0.0, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertFalse(set.getSmallestLimit().isDirect()); Assert.assertFalse(set.getSmallestLimit().isDirect());
Assert.assertEquals(0.0, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
} }
@Test @Test