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:
parent
d83cdb9133
commit
169a316003
|
@ -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} */
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue