IntervalsSet.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.euclidean.oned;

  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.NoSuchElementException;

  27. import org.hipparchus.geometry.partitioning.AbstractRegion;
  28. import org.hipparchus.geometry.partitioning.BSPTree;
  29. import org.hipparchus.geometry.partitioning.BoundaryProjection;
  30. import org.hipparchus.util.Precision;

  31. /** This class represents a 1D region: a set of intervals.
  32.  */
  33. public class IntervalsSet
  34.     extends AbstractRegion<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint,
  35.                            Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  36.     implements Iterable<double[]> {

  37.     /** Build an intervals set representing the whole real line.
  38.      * @param tolerance tolerance below which points are considered identical.
  39.      */
  40.     public IntervalsSet(final double tolerance) {
  41.         super(tolerance);
  42.     }

  43.     /** Build an intervals set corresponding to a single interval.
  44.      * @param lower lower bound of the interval, must be lesser or equal
  45.      * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY})
  46.      * @param upper upper bound of the interval, must be greater or equal
  47.      * to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
  48.      * @param tolerance tolerance below which points are considered identical.
  49.      */
  50.     public IntervalsSet(final double lower, final double upper, final double tolerance) {
  51.         super(buildTree(lower, upper, tolerance), tolerance);
  52.     }

  53.     /** Build an intervals set from an inside/outside BSP tree.
  54.      * <p>The leaf nodes of the BSP tree <em>must</em> have a
  55.      * {@code Boolean} attribute representing the inside status of
  56.      * the corresponding cell (true for inside cells, false for outside
  57.      * cells). In order to avoid building too many small objects, it is
  58.      * recommended to use the predefined constants
  59.      * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
  60.      * @param tree inside/outside BSP tree representing the intervals set
  61.      * @param tolerance tolerance below which points are considered identical.
  62.      */
  63.     public IntervalsSet(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> tree,
  64.                         final double tolerance) {
  65.         super(tree, tolerance);
  66.     }

  67.     /** Build an intervals set from a Boundary REPresentation (B-rep).
  68.      * <p>The boundary is provided as a collection of {@link
  69.      * org.hipparchus.geometry.partitioning.SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
  70.      * interior part of the region on its minus side and the exterior on
  71.      * its plus side.</p>
  72.      * <p>The boundary elements can be in any order, and can form
  73.      * several non-connected sets (like for example polygons with holes
  74.      * or a set of disjoints polyhedrons considered as a whole). In
  75.      * fact, the elements do not even need to be connected together
  76.      * (their topological connections are not used here). However, if the
  77.      * boundary does not really separate an inside open from an outside
  78.      * open (open having here its topological meaning), then subsequent
  79.      * calls to the {@link
  80.      * org.hipparchus.geometry.partitioning.Region#checkPoint(org.hipparchus.geometry.Point)
  81.      * checkPoint} method will not be meaningful anymore.</p>
  82.      * <p>If the boundary is empty, the region will represent the whole
  83.      * space.</p>
  84.      * @param boundary collection of boundary elements
  85.      * @param tolerance tolerance below which points are considered identical.
  86.      */
  87.     public IntervalsSet(final Collection<SubOrientedPoint> boundary, final double tolerance) {
  88.         super(boundary, tolerance);
  89.     }

  90.     /** Build an inside/outside tree representing a single interval.
  91.      * @param lower lower bound of the interval, must be lesser or equal
  92.      * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY})
  93.      * @param upper upper bound of the interval, must be greater or equal
  94.      * to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
  95.      * @param tolerance tolerance below which points are considered identical.
  96.      * @return the built tree
  97.      */
  98.     private static BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  99.         buildTree(final double lower, final double upper, final double tolerance) {
  100.         if (Double.isInfinite(lower) && (lower < 0)) {
  101.             if (Double.isInfinite(upper) && (upper > 0)) {
  102.                 // the tree must cover the whole real line
  103.                 return new BSPTree<>(Boolean.TRUE);
  104.             }
  105.             // the tree must be open on the negative infinity side
  106.             final SubOrientedPoint upperCut = new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
  107.             return new BSPTree<>(upperCut, new BSPTree<>(Boolean.FALSE), new BSPTree<>(Boolean.TRUE), null);
  108.         }
  109.         final SubOrientedPoint lowerCut = new OrientedPoint(new Vector1D(lower), false, tolerance).wholeHyperplane();
  110.         if (Double.isInfinite(upper) && (upper > 0)) {
  111.             // the tree must be open on the positive infinity side
  112.             return new BSPTree<>(lowerCut, new BSPTree<>(Boolean.FALSE), new BSPTree<>(Boolean.TRUE), null);
  113.         }

  114.         // the tree must be bounded on the two sides
  115.         final SubOrientedPoint upperCut = new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
  116.         return new BSPTree<>(lowerCut,
  117.                              new BSPTree<>(Boolean.FALSE),
  118.                              new BSPTree<>(upperCut, new BSPTree<>(Boolean.FALSE), new BSPTree<>(Boolean.TRUE), null),
  119.                              null);

  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public IntervalsSet buildNew(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> tree) {
  124.         return new IntervalsSet(tree, getTolerance());
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public Vector1D getInteriorPoint() {

  129.         // look for the midpoint of the longest interval
  130.         // or some finite point if interval extends to ±∞
  131.         double selectedPoint  = Double.NaN;
  132.         double selectedLength = 0;
  133.         for (final double[] a : this) {
  134.             final double length = a[1] - a[0];
  135.             if (length > selectedLength) {
  136.                 // this interval is longer than the selected one, change selection
  137.                 if (Double.isInfinite(a[0])) {
  138.                     // interval starts at -∞, it may extend to +∞ as well
  139.                     selectedPoint = Double.isInfinite(a[1]) ? 0 : a[1] - 1.0e9 * getTolerance();
  140.                 } else if (Double.isInfinite(a[1])) {
  141.                     // interval ends at +∞
  142.                     selectedPoint = a[0] + 1.0e9 * getTolerance();
  143.                 } else {
  144.                     selectedPoint  = 0.5 * (a[0] + a[1]);
  145.                 }
  146.                 selectedLength = length;
  147.             }
  148.         }

  149.         return Double.isNaN(selectedPoint) ? null : new Vector1D(selectedPoint);

  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     protected void computeGeometricalProperties() {
  154.         if (getTree(false).getCut() == null) {
  155.             setBarycenter(Vector1D.NaN);
  156.             setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0);
  157.         } else {
  158.             double size = 0.0;
  159.             double sum = 0.0;
  160.             for (final Interval interval : asList()) {
  161.                 size += interval.getSize();
  162.                 sum  += interval.getSize() * interval.getBarycenter();
  163.             }
  164.             setSize(size);
  165.             if (Double.isInfinite(size)) {
  166.                 setBarycenter(Vector1D.NaN);
  167.             } else if (size >= Precision.SAFE_MIN) {
  168.                 setBarycenter(new Vector1D(sum / size));
  169.             } else {
  170.                 setBarycenter(getTree(false).getCut().getHyperplane().getLocation());
  171.             }
  172.         }
  173.     }

  174.     /** Get the lowest value belonging to the instance.
  175.      * @return lowest value belonging to the instance
  176.      * ({@code Double.NEGATIVE_INFINITY} if the instance doesn't
  177.      * have any low bound, {@code Double.POSITIVE_INFINITY} if the
  178.      * instance is empty)
  179.      */
  180.     public double getInf() {
  181.         BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node = getTree(false);
  182.         double  inf  = Double.POSITIVE_INFINITY;
  183.         while (node.getCut() != null) {
  184.             final OrientedPoint op = node.getCut().getHyperplane();
  185.             inf  = op.getLocation().getX();
  186.             node = op.isDirect() ? node.getMinus() : node.getPlus();
  187.         }
  188.         return ((Boolean) node.getAttribute()) ? Double.NEGATIVE_INFINITY : inf;
  189.     }

  190.     /** Get the highest value belonging to the instance.
  191.      * @return highest value belonging to the instance
  192.      * ({@code Double.POSITIVE_INFINITY} if the instance doesn't
  193.      * have any high bound, {@code Double.NEGATIVE_INFINITY} if the
  194.      * instance is empty)
  195.      */
  196.     public double getSup() {
  197.         BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node = getTree(false);
  198.         double  sup  = Double.NEGATIVE_INFINITY;
  199.         while (node.getCut() != null) {
  200.             final OrientedPoint op = node.getCut().getHyperplane();
  201.             sup  = op.getLocation().getX();
  202.             node = op.isDirect() ? node.getPlus() : node.getMinus();
  203.         }
  204.         return ((Boolean) node.getAttribute()) ? Double.POSITIVE_INFINITY : sup;
  205.     }

  206.     /** {@inheritDoc}
  207.      */
  208.     @Override
  209.     public BoundaryProjection<Euclidean1D, Vector1D> projectToBoundary(final Vector1D point) {

  210.         // get position of test point
  211.         final double x = point.getX();

  212.         double previous = Double.NEGATIVE_INFINITY;
  213.         for (final double[] a : this) {
  214.             if (x < a[0]) {
  215.                 // the test point lies between the previous and the current intervals
  216.                 // offset will be positive
  217.                 final double previousOffset = x - previous;
  218.                 final double currentOffset  = a[0] - x;
  219.                 if (previousOffset < currentOffset) {
  220.                     return new BoundaryProjection<>(point, finiteOrNullPoint(previous), previousOffset);
  221.                 } else {
  222.                     return new BoundaryProjection<>(point, finiteOrNullPoint(a[0]), currentOffset);
  223.                 }
  224.             } else if (x <= a[1]) {
  225.                 // the test point lies within the current interval
  226.                 // offset will be negative
  227.                 final double offset0 = a[0] - x;
  228.                 final double offset1 = x - a[1];
  229.                 if (offset0 < offset1) {
  230.                     return new BoundaryProjection<>(point, finiteOrNullPoint(a[1]), offset1);
  231.                 } else {
  232.                     return new BoundaryProjection<>(point, finiteOrNullPoint(a[0]), offset0);
  233.                 }
  234.             }
  235.             previous = a[1];
  236.         }

  237.         // the test point if past the last sub-interval
  238.         return new BoundaryProjection<>(point, finiteOrNullPoint(previous), x - previous);

  239.     }

  240.     /** Build a finite point.
  241.      * @param x abscissa of the point
  242.      * @return a new point for finite abscissa, null otherwise
  243.      */
  244.     private Vector1D finiteOrNullPoint(final double x) {
  245.         return Double.isInfinite(x) ? null : new Vector1D(x);
  246.     }

  247.     /** Build an ordered list of intervals representing the instance.
  248.      * <p>This method builds this intervals set as an ordered list of
  249.      * {@link Interval Interval} elements. If the intervals set has no
  250.      * lower limit, the first interval will have its low bound equal to
  251.      * {@code Double.NEGATIVE_INFINITY}. If the intervals set has
  252.      * no upper limit, the last interval will have its upper bound equal
  253.      * to {@code Double.POSITIVE_INFINITY}. An empty tree will
  254.      * build an empty list while a tree representing the whole real line
  255.      * will build a one element list with both bounds being
  256.      * infinite.</p>
  257.      * @return a new ordered list containing {@link Interval Interval}
  258.      * elements
  259.      */
  260.     public List<Interval> asList() {
  261.         final List<Interval> list = new ArrayList<>();
  262.         for (final double[] a : this) {
  263.             list.add(new Interval(a[0], a[1]));
  264.         }
  265.         return list;
  266.     }

  267.     /** Get the first leaf node of a tree.
  268.      * @param root tree root
  269.      * @return first leaf node
  270.      */
  271.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  272.         getFirstLeaf(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> root) {

  273.         if (root.getCut() == null) {
  274.             return root;
  275.         }

  276.         // find the smallest internal node
  277.         BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> smallest = null;
  278.         for (BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> n = root; n != null; n = previousInternalNode(n)) {
  279.             smallest = n;
  280.         }

  281.         return leafBefore(smallest);

  282.     }

  283.     /** Get the node corresponding to the first interval boundary.
  284.      * @return smallest internal node,
  285.      * or null if there are no internal nodes (i.e. the set is either empty or covers the real line)
  286.      */
  287.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> getFirstIntervalBoundary() {

  288.         // start search at the tree root
  289.         BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node = getTree(false);
  290.         if (node.getCut() == null) {
  291.             return null;
  292.         }

  293.         // walk tree until we find the smallest internal node
  294.         node = getFirstLeaf(node).getParent();

  295.         // walk tree until we find an interval boundary
  296.         while (node != null && !(isIntervalStart(node) || isIntervalEnd(node))) {
  297.             node = nextInternalNode(node);
  298.         }

  299.         return node;

  300.     }

  301.     /** Check if an internal node corresponds to the start abscissa of an interval.
  302.      * @param node internal node to check
  303.      * @return true if the node corresponds to the start abscissa of an interval
  304.      */
  305.     private boolean isIntervalStart(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  306.         if ((Boolean) leafBefore(node).getAttribute()) {
  307.             // it has an inside cell before it, it may end an interval but not start it
  308.             return false;
  309.         }

  310.         if (!(Boolean) leafAfter(node).getAttribute()) {
  311.             // it has an outside cell after it, it is a dummy cut away from real intervals
  312.             return false;
  313.         }

  314.         // the cell has an outside before and an inside after it,
  315.         // it is the start of an interval
  316.         return true;

  317.     }

  318.     /** Check if an internal node corresponds to the end abscissa of an interval.
  319.      * @param node internal node to check
  320.      * @return true if the node corresponds to the end abscissa of an interval
  321.      */
  322.     private boolean isIntervalEnd(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  323.         if (!(Boolean) leafBefore(node).getAttribute()) {
  324.             // it has an outside cell before it, it may start an interval but not end it
  325.             return false;
  326.         }

  327.         if ((Boolean) leafAfter(node).getAttribute()) {
  328.             // it has an inside cell after it, it is a dummy cut in the middle of an interval
  329.             return false;
  330.         }

  331.         // the cell has an inside before and an outside after it,
  332.         // it is the end of an interval
  333.         return true;

  334.     }

  335.     /** Get the next internal node.
  336.      * @param node current internal node
  337.      * @return next internal node in ascending order, or null
  338.      * if this is the last internal node
  339.      */
  340.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  341.         nextInternalNode(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  342.         if (childAfter(node).getCut() != null) {
  343.             // the next node is in the subtree
  344.             return leafAfter(node).getParent();
  345.         }

  346.         // there is nothing left deeper in the tree, we backtrack
  347.         while (isAfterParent(node)) {
  348.             node = node.getParent();
  349.         }
  350.         return node.getParent();

  351.     }

  352.     /** Get the previous internal node.
  353.      * @param node current internal node
  354.      * @return previous internal node in ascending order, or null
  355.      * if this is the first internal node
  356.      */
  357.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  358.         previousInternalNode(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  359.         if (childBefore(node).getCut() != null) {
  360.             // the next node is in the subtree
  361.             return leafBefore(node).getParent();
  362.         }

  363.         // there is nothing left deeper in the tree, we backtrack
  364.         while (isBeforeParent(node)) {
  365.             node = node.getParent();
  366.         }
  367.         return node.getParent();

  368.     }

  369.     /** Find the leaf node just before an internal node.
  370.      * @param node internal node at which the subtree starts
  371.      * @return leaf node just before the internal node
  372.      */
  373.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  374.         leafBefore(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  375.         node = childBefore(node);
  376.         while (node.getCut() != null) {
  377.             node = childAfter(node);
  378.         }

  379.         return node;

  380.     }

  381.     /** Find the leaf node just after an internal node.
  382.      * @param node internal node at which the subtree starts
  383.      * @return leaf node just after the internal node
  384.      */
  385.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  386.         leafAfter(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {

  387.         node = childAfter(node);
  388.         while (node.getCut() != null) {
  389.             node = childBefore(node);
  390.         }

  391.         return node;

  392.     }

  393.     /** Check if a node is the child before its parent in ascending order.
  394.      * @param node child node considered
  395.      * @return true is the node has a parent end is before it in ascending order
  396.      */
  397.     private boolean isBeforeParent(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  398.         final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> parent = node.getParent();
  399.         if (parent == null) {
  400.             return false;
  401.         } else {
  402.             return node == childBefore(parent);
  403.         }
  404.     }

  405.     /** Check if a node is the child after its parent in ascending order.
  406.      * @param node child node considered
  407.      * @return true is the node has a parent end is after it in ascending order
  408.      */
  409.     private boolean isAfterParent(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  410.         final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> parent = node.getParent();
  411.         if (parent == null) {
  412.             return false;
  413.         } else {
  414.             return node == childAfter(parent);
  415.         }
  416.     }

  417.     /** Find the child node just before an internal node.
  418.      * @param node internal node at which the subtree starts
  419.      * @return child node just before the internal node
  420.      */
  421.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  422.         childBefore(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  423.         if (isDirect(node)) {
  424.             // smaller abscissas are on minus side, larger abscissas are on plus side
  425.             return node.getMinus();
  426.         } else {
  427.             // smaller abscissas are on plus side, larger abscissas are on minus side
  428.             return node.getPlus();
  429.         }
  430.     }

  431.     /** Find the child node just after an internal node.
  432.      * @param node internal node at which the subtree starts
  433.      * @return child node just after the internal node
  434.      */
  435.     private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint>
  436.         childAfter(BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  437.         if (isDirect(node)) {
  438.             // smaller abscissas are on minus side, larger abscissas are on plus side
  439.             return node.getPlus();
  440.         } else {
  441.             // smaller abscissas are on plus side, larger abscissas are on minus side
  442.             return node.getMinus();
  443.         }
  444.     }

  445.     /** Check if an internal node has a direct oriented point.
  446.      * @param node internal node to check
  447.      * @return true if the oriented point is direct
  448.      */
  449.     private boolean isDirect(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  450.         return node.getCut().getHyperplane().isDirect();
  451.     }

  452.     /** Get the abscissa of an internal node.
  453.      * @param node internal node to check
  454.      * @return abscissa
  455.      */
  456.     private double getAngle(final BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> node) {
  457.         return node.getCut().getHyperplane().getLocation().getX();
  458.     }

  459.     /** {@inheritDoc}
  460.      * <p>
  461.      * The iterator returns the limit values of sub-intervals in ascending order.
  462.      * </p>
  463.      * <p>
  464.      * The iterator does <em>not</em> support the optional {@code remove} operation.
  465.      * </p>
  466.      */
  467.     @Override
  468.     public Iterator<double[]> iterator() {
  469.         return new SubIntervalsIterator();
  470.     }

  471.     /** Local iterator for sub-intervals. */
  472.     private class SubIntervalsIterator implements Iterator<double[]> {

  473.         /** Current node. */
  474.         private BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> current;

  475.         /** Sub-interval no yet returned. */
  476.         private double[] pending;

  477.         /** Simple constructor.
  478.          */
  479.         SubIntervalsIterator() {

  480.             current = getFirstIntervalBoundary();

  481.             if (current == null) {
  482.                 // all the leaf tree nodes share the same inside/outside status
  483.                 if ((Boolean) getFirstLeaf(getTree(false)).getAttribute()) {
  484.                     // it is an inside node, it represents the full real line
  485.                     pending = new double[] {
  486.                         Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
  487.                     };
  488.                 } else {
  489.                     pending = null;
  490.                 }
  491.             } else if (isIntervalEnd(current)) {
  492.                 // the first boundary is an interval end,
  493.                 // so the first interval starts at infinity
  494.                 pending = new double[] {
  495.                     Double.NEGATIVE_INFINITY, getAngle(current)
  496.                 };
  497.             } else {
  498.                 selectPending();
  499.             }
  500.         }

  501.         /** Walk the tree to select the pending sub-interval.
  502.          */
  503.         private void selectPending() {

  504.             // look for the start of the interval
  505.             BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> start = current;
  506.             while (start != null && !isIntervalStart(start)) {
  507.                 start = nextInternalNode(start);
  508.             }

  509.             if (start == null) {
  510.                 // we have exhausted the iterator
  511.                 current = null;
  512.                 pending = null;
  513.                 return;
  514.             }

  515.             // look for the end of the interval
  516.             BSPTree<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> end = start;
  517.             while (end != null && !isIntervalEnd(end)) {
  518.                 end = nextInternalNode(end);
  519.             }

  520.             if (end != null) {

  521.                 // we have identified the interval
  522.                 pending = new double[] {
  523.                     getAngle(start), getAngle(end)
  524.                 };

  525.                 // prepare search for next interval
  526.                 current = end;

  527.             } else {

  528.                 // the final interval is open toward infinity
  529.                 pending = new double[] {
  530.                     getAngle(start), Double.POSITIVE_INFINITY
  531.                 };

  532.                 // there won't be any other intervals
  533.                 current = null;

  534.             }

  535.         }

  536.         /** {@inheritDoc} */
  537.         @Override
  538.         public boolean hasNext() {
  539.             return pending != null;
  540.         }

  541.         /** {@inheritDoc} */
  542.         @Override
  543.         public double[] next() {
  544.             if (pending == null) {
  545.                 throw new NoSuchElementException();
  546.             }
  547.             final double[] next = pending;
  548.             selectPending();
  549.             return next;
  550.         }

  551.         /** {@inheritDoc} */
  552.         @Override
  553.         public void remove() {
  554.             throw new UnsupportedOperationException();
  555.         }

  556.     }

  557. }