/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.math.geometry.euclidean.oned; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.math.geometry.partitioning.AbstractRegion; import org.apache.commons.math.geometry.partitioning.BSPTree; import org.apache.commons.math.geometry.partitioning.SubHyperplane; /** This class represents a 1D region: a set of intervals. * @version $Id: IntervalsSet.java 1131153 2011-06-03 19:23:56Z luc $ * @since 3.0 */ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> { /** Build an intervals set representing the whole real line. */ public IntervalsSet() { super(); } /** Build an intervals set corresponding to a single interval. * @param lower lower bound of the interval, must be lesser or equal * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY}) * @param upper upper bound of the interval, must be greater or equal * to {@code lower} (may be {@code Double.POSITIVE_INFINITY}) */ public IntervalsSet(final double lower, final double upper) { super(buildTree(lower, upper)); } /** Build an intervals set from an inside/outside BSP tree. * <p>The leaf nodes of the BSP tree <em>must</em> have a * {@code Boolean} attribute representing the inside status of * the corresponding cell (true for inside cells, false for outside * cells). In order to avoid building too many small objects, it is * recommended to use the predefined constants * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p> * @param tree inside/outside BSP tree representing the intervals set */ public IntervalsSet(final BSPTree<Euclidean1D> tree) { super(tree); } /** Build an intervals set from a Boundary REPresentation (B-rep). * <p>The boundary is provided as a collection of {@link * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the * interior part of the region on its minus side and the exterior on * its plus side.</p> * <p>The boundary elements can be in any order, and can form * several non-connected sets (like for example polygons with holes * or a set of disjoints polyhedrons considered as a whole). In * fact, the elements do not even need to be connected together * (their topological connections are not used here). However, if the * boundary does not really separate an inside open from an outside * open (open having here its topological meaning), then subsequent * calls to the {@link * org.apache.commons.math.geometry.partitioning.Region#checkPoint(org.apache.commons.math.geometry.Vector) * checkPoint} method will not be meaningful anymore.</p> * <p>If the boundary is empty, the region will represent the whole * space.</p> * @param boundary collection of boundary elements */ public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary) { super(boundary); } /** Build an inside/outside tree representing a single interval. * @param lower lower bound of the interval, must be lesser or equal * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY}) * @param upper upper bound of the interval, must be greater or equal * to {@code lower} (may be {@code Double.POSITIVE_INFINITY}) * @return the built tree */ private static BSPTree<Euclidean1D> buildTree(final double lower, final double upper) { if (Double.isInfinite(lower) && (lower < 0)) { if (Double.isInfinite(upper) && (upper > 0)) { // the tree must cover the whole real line return new BSPTree<Euclidean1D>(Boolean.TRUE); } // the tree must be open on the negative infinity side final SubHyperplane<Euclidean1D> upperCut = new OrientedPoint(new Vector1D(upper), true).wholeHyperplane(); return new BSPTree<Euclidean1D>(upperCut, new BSPTree<Euclidean1D>(Boolean.FALSE), new BSPTree<Euclidean1D>(Boolean.TRUE), null); } final SubHyperplane<Euclidean1D> lowerCut = new OrientedPoint(new Vector1D(lower), false).wholeHyperplane(); if (Double.isInfinite(upper) && (upper > 0)) { // the tree must be open on the positive infinity side return new BSPTree<Euclidean1D>(lowerCut, new BSPTree<Euclidean1D>(Boolean.FALSE), new BSPTree<Euclidean1D>(Boolean.TRUE), null); } // the tree must be bounded on the two sides final SubHyperplane<Euclidean1D> upperCut = new OrientedPoint(new Vector1D(upper), true).wholeHyperplane(); return new BSPTree<Euclidean1D>(lowerCut, new BSPTree<Euclidean1D>(Boolean.FALSE), new BSPTree<Euclidean1D>(upperCut, new BSPTree<Euclidean1D>(Boolean.FALSE), new BSPTree<Euclidean1D>(Boolean.TRUE), null), null); } /** {@inheritDoc} */ public IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) { return new IntervalsSet(tree); } /** {@inheritDoc} */ protected void computeGeometricalProperties() { if (getTree(false).getCut() == null) { setBarycenter(Vector1D.NaN); setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0); } else { double size = 0.0; double sum = 0.0; for (final Interval interval : asList()) { size += interval.getLength(); sum += interval.getLength() * interval.getMidPoint(); } setSize(size); setBarycenter(Double.isInfinite(size) ? Vector1D.NaN : new Vector1D(sum / size)); } } /** Get the lowest value belonging to the instance. * @return lowest value belonging to the instance * ({@code Double.NEGATIVE_INFINITY} if the instance doesn't * have any low bound, {@code Double.POSITIVE_INFINITY} if the * instance is empty) */ public double getInf() { BSPTree<Euclidean1D> node = getTree(false); double inf = Double.POSITIVE_INFINITY; while (node.getCut() != null) { final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane(); inf = op.getLocation().getX(); node = op.isDirect() ? node.getMinus() : node.getPlus(); } return ((Boolean) node.getAttribute()) ? Double.NEGATIVE_INFINITY : inf; } /** Get the highest value belonging to the instance. * @return highest value belonging to the instance * ({@code Double.POSITIVE_INFINITY} if the instance doesn't * have any high bound, {@code Double.NEGATIVE_INFINITY} if the * instance is empty) */ public double getSup() { BSPTree<Euclidean1D> node = getTree(false); double sup = Double.NEGATIVE_INFINITY; while (node.getCut() != null) { final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane(); sup = op.getLocation().getX(); node = op.isDirect() ? node.getPlus() : node.getMinus(); } return ((Boolean) node.getAttribute()) ? Double.POSITIVE_INFINITY : sup; } /** Build an ordered list of intervals representing the instance. * <p>This method builds this intervals set as an ordered list of * {@link Interval Interval} elements. If the intervals set has no * lower limit, the first interval will have its low bound equal to * {@code Double.NEGATIVE_INFINITY}. If the intervals set has * no upper limit, the last interval will have its upper bound equal * to {@code Double.POSITIVE_INFINITY}. An empty tree will * build an empty list while a tree representing the whole real line * will build a one element list with both bounds beeing * infinite.</p> * @return a new ordered list containing {@link Interval Interval} * elements */ public List<Interval> asList() { final List<Interval> list = new ArrayList<Interval>(); recurseList(getTree(false), list, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); return list; } /** Update an intervals list. * @param node current node * @param list list to update * @param lower lower bound of the current convex cell * @param upper upper bound of the current convex cell */ private void recurseList(final BSPTree<Euclidean1D> node, final List<Interval> list, final double lower, final double upper) { if (node.getCut() == null) { if ((Boolean) node.getAttribute()) { // this leaf cell is an inside cell: an interval list.add(new Interval(lower, upper)); } } else { final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane(); final Vector1D loc = op.getLocation(); double x = loc.getX(); // make sure we explore the tree in increasing order final BSPTree<Euclidean1D> low = op.isDirect() ? node.getMinus() : node.getPlus(); final BSPTree<Euclidean1D> high = op.isDirect() ? node.getPlus() : node.getMinus(); recurseList(low, list, lower, x); if ((checkPoint(low, loc) == Location.INSIDE) && (checkPoint(high, loc) == Location.INSIDE)) { // merge the last interval added and the first one of the high sub-tree x = ((Interval) list.remove(list.size() - 1)).getLower(); } recurseList(high, list, x, upper); } } }