/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.util; /** * Class to implement multi-dimensional intervals based on * n intervals of class Interval1D. <p> * The order of the basic data type of each dimension * is represented by means of comparators. If * no comparator for a dimension is specified in a constructor * a ComparableComparator.DEFAULT_INSTANCE is used instead. * <p> * <b>IMPORTANT:</b> If two intervals should be checked for equality, overlapping, * intersection or containment these two intervals must be based on * the same dimension and also have to use exactly * the same co-ordinate system. * That means the ith dimension of the two intervals must have the * same basic data type. It is necessary, because these * operations work in terms * of dimensions, i.e. the appertaining method of the class * Interval1D will be called. If the basic type of one * dimension is not the same, that is the case if the two * intervals use different co-ordinate systems, then an * <tt>IllegalArgument</tt> exception will be thrown, because * the operation could not be performed properly. * <p> * <b>Example usage:</b> * <br><br> * <code><pre> * AbstractIntervalND interval1 = new AbstractIntervalND (new Interval1D[] { * new Interval1D (new Integer(-25), true, new Integer(25), false), * new Interval1D (new Character('a'), new Character('z')), * new Interval1D (new Float(0.75), new Float(13.125)), * }); * * AbstractIntervalND interval2 = new AbstractIntervalND (new Interval1D[] { * new Interval1D (new Integer(-10), new Integer(10)), * new Interval1D (new Character('e'), false, new Character('u'), false), * new Interval1D (new Float(-1.25), true, new Float(7.625), false), * }); * * System.out.println("interval1: " +interval1); * System.out.println("interval2: " +interval2); * System.out.println("Are the intervals equal? " +interval1.equals(interval2)); * System.out.println("Do the intervals overlap? " +interval1.overlaps(interval2)); * System.out.println("Does interval1 contain interval2? " +interval1.contains(interval2)); * System.out.println("Interval3 gets a clone of interval1."); * AbstractIntervalND interval3 = (AbstractIntervalND)interval1.clone(); * System.out.println("Union of interval1 and interval2: " +interval1.union(interval2)); * System.out.println("Intersection of interval3 and interval2: " +interval3.intersect(interval2)); * </code></pre> * <p> * The following output was created: * <pre> * interval1: {[-25,25[; [a,z]; [0.75,13.125]} * interval2: {[-10,10]; ]e,u[; [-1.25,7.625[} * * Are the intervals equal? false * Do the intervals overlap? true * Does interval1 contain interval2? false * Interval3 gets a clone of interval1. * Union of interval1 and interval2: {[-25,25[; [a,z]; [-1.25,13.125]} * Intersection of interval3 and interval2: {[-10,10]; ]e,u[; [0.75,7.625[} * </pre> * * @see xxl.core.util.Interval1D * @see java.lang.Cloneable */ public class IntervalND implements Cloneable { /** Array containing n one-dimensional intervals. */ protected Interval1D [] intervals; /** * Constructs a new n-dimensional interval using the given array * of one-dimensional intervals. * @param intervals array of one-dimensional intervals */ public IntervalND (Interval1D [] intervals) { this.intervals = intervals; } /** * Returns a String representation of this interval. * <p>Overrides the <code>toString</code> method of <code>Object</code>. * * @return the String representation of this interval. */ public String toString () { String result = "{"; for (int i=0; i<dimensions(); i++) { result += intervals()[i].toString(); result = i<dimensions()-1 ? result+"; " : result; } return result+"}"; } /** * Clones this interval. * The produced new <tt>AbstractIntervalND</tt> will be equal to this interval. * The clone of the interval is another interval that has exactly the * same border properties and the same comparators as the current interval. * <p>Overrides the <code>clone</code> method of <code>Object</code>. * In particular, all one-dimensional intervals are cloned, too. * * @return a clone of this interval. * @see xxl.core.util.Interval1D#clone() */ public Object clone () { try { IntervalND clone = (IntervalND)super.clone(); clone.intervals = new Interval1D[intervals.length]; for (int i=0; i<intervals.length; i++) clone.intervals[i] = (Interval1D)intervals[i].clone(); return clone; } catch (CloneNotSupportedException cnse) { } return null; } /** * Returns <tt>true</tt> iff the given object is a multi-dimensional interval * of the same dimension and equal one-dimensional intervals. <br> * The implementation is as follows: * <br><br> * <code><pre> * AbstractIntervalND interval = (AbstractIntervalND)object; * * if (dimensions()!=interval.dimensions()) * return false; * for (int i=0; i<tt><</tt>dimensions(); i++) * if (!intervals()[i].equals(interval.intervals()[i])) * return false; * return true; * </code></pre> * Becaues n-dimensional intervals (AbstractIntervalND) are designed as n one-dimensional intervals * (Interval1D) the method <code>equals</code> in class Interval1D is called for every * one-dimenonsial interval with the intention to compare each one-dimensional interval of * this object with that of the given object. * * @param object The object, an n-dimensional interval, to be compared with this interval. * @return Returns <tt>true</tt> if the given object is an n-dimensional interval * having the same dimension as this interval and also each one-dimensional interval * has to be equal to this one-dimensional intervals. * Returns <tt>false</tt> if the given object is not an n-dimensional interval, * or one of the basic one-dimensional intervals differs in one border property of * the one-dimensional intervals of this interval. * @see xxl.core.util.Interval1D#equals(Object) */ public boolean equals (Object object) { try { IntervalND interval = (IntervalND)object; if (dimensions()!=interval.dimensions()) return false; for (int i=0; i<dimensions(); i++) if (!intervals()[i].equals(interval.intervals()[i])) return false; return true; } catch (ClassCastException cce) { return false; } catch (NullPointerException npe) { return false; } } /** * Returns the internal array of one-dimensional intervals used to * design an n-dimensional array. * * @return The internal array containing the one-dimensional intervals. */ public Interval1D [] intervals () { return intervals; } /** * Returns the number of dimensions of this interval. * That means <code>intervals.length</code> is returned. * * @return This interval's dimension. */ public int dimensions () { return intervals.length; } /** * Checks whether a n-dimensional point is contained by this interval. <br> * <b>Note:</b> The point to be checked has to be an object array with * the same length (dimension) as this n-dimensional interval calling * this method, otherwise an <tt>IllegalArgumentException</tt> will be thrown. <br> * * The implementation is as follows: * <br><br> * <code><pre> * for (int i=0; i<tt><</tt>intervals.length; i++) * if (intervals[i].contains(point[i])!=0) * return false; * return true; * </code></pre> * For each one-dimensional interval of this AbstractIntervalND the condition, if the * component of the specified point (<tt>point[i]</tt>) is contained, * is checked. So the result is only <tt>true</tt> if each dimension contains * the appertaining component of the specified n-dimensional point. * * @param point The point to be tested. * @return Returns <tt>true</tt> iff the point is contained by this interval, * otherwise <tt>false</tt>. * @throws IllegalArgumentException if the point can not be tested properly. * @see xxl.core.util.Interval1D#contains(Object point) */ public boolean contains (Object [] point) throws IllegalArgumentException { try { if (dimensions()!=point.length) throw new IllegalArgumentException("Point and interval do not have the same dimension."); for (int i=0; i<intervals.length; i++) if (intervals[i].contains(point[i])!=0) return false; return true; } catch (Exception e) { throw new IllegalArgumentException(); } } /** * Checks whether a specified n-dimensional interval is contained * by this n-dimensional interval. <br> * <b>Note:</b> The intervals must have the same dimension, * otherwise an <tt>IllegalArgumentException</tt> will be thrown. <br> * The implementation is as follows: * <br><br> * <code><pre> * for (int i=0; i<tt><</tt>intervals.length; i++) * if (!intervals[i].contains(multiDimInterval.intervals[i])) * return false; * return true; * </code></pre> * Because the intervals have the same dimension the method * {@link xxl.core.util.Interval1D#contains(Interval1D)} is called * for each dimension. If each one dimensional interval that * belongs to the AbstractIntervalND, that called this method, covers * the same dimension belonging to the given interval * <tt>multiDimInterval</tt> the result is <tt>true</tt>, * otherwise the result is <tt>false</tt>, because one dimension * of the given interval is not contained by the interval * that called this method. * * @param multiDimInterval The interval to be tested. * @return Returns <tt>true</tt> iff the given interval is contained * by this interval. * @throws IllegalArgumentException if the interval can not be tested properly. * @see xxl.core.util.Interval1D#contains(Interval1D) */ public boolean contains (IntervalND multiDimInterval) throws IllegalArgumentException { try { if (dimensions()!=multiDimInterval.dimensions()) throw new IllegalArgumentException("Intervals do not have the same dimension."); for (int i=0; i<intervals.length; i++) if (!intervals[i].contains(multiDimInterval.intervals[i])) return false; return true; } catch (Exception e) { throw new IllegalArgumentException(); } } /** * Checks whether an interval and this interval do overlap. <br> * <b>Note:</b> The intervals must have the same dimension, * otherwise an <tt>IllegalArgumentException</tt> will be thrown. <br> * The implementation is as follows: * <br><br> * <code><pre> * for (int i=0; i<tt><</tt>intervals.length; i++) * if (intervals[i].overlaps(multiDimInterval.intervals[i])!=0) * return false; * return true; * </code></pre> * Two intervals do only overlap,i.e. <tt>true</tt> is returned, * if they overlap in each dimension, * therefore {@link xxl.core.util.Interval1D#overlaps(Interval1D)} is * called to compare every dimension. * * @param multiDimInterval The interval to be tested. * @return Returns <tt>true</tt> iff the interval and this interval do overlap. * @throws IllegalArgumentException if the given interval cannot be tested properly. */ public boolean overlaps (IntervalND multiDimInterval) throws IllegalArgumentException { try { if (dimensions()!=multiDimInterval.dimensions()) throw new IllegalArgumentException("Intervals do not have the same dimension."); for (int i=0; i<intervals.length; i++) if (intervals[i].overlaps(multiDimInterval.intervals[i])!=0) return false; return true; } catch (Exception e) { throw new IllegalArgumentException(); } } /** * Extends this interval to contain a given interval, too. <br> * <b>Note:</b> The intervals must have the same dimension, * otherwise an <tt>IllegalArgumentException</tt> will be thrown. <br> * The implementation is as follows: * <br><br> * <code><pre> * for (int i=0; i<tt><</tt>intervals.length; i++) * intervals[i].union(multiDimInterval.intervals[i]); * return this; * </code></pre> * Each dimension of the interval calling this method is extended by * calling {@link xxl.core.util.Interval1D#union(Interval1D)} using the * same dimension of the given interval <tt>multiDimInterval</tt>. * * @param multiDimInterval The interval which defines the extension of this interval. * @return The interval calling this method. * @throws IllegalArgumentException if the union can not be performed properly. */ public IntervalND union (IntervalND multiDimInterval) throws IllegalArgumentException { try { if (dimensions()!=multiDimInterval.dimensions()) throw new IllegalArgumentException("Intervals do not have the same dimension."); else { for (int i=0; i<intervals.length; i++) intervals[i].union(multiDimInterval.intervals[i]); } return this; } catch (Exception e) { throw new IllegalArgumentException(); } } /** * Shrinks this interval to reflect the intersection with a given interval. * <b>Note:</b> The intervals must have the same dimension, * and have to overlap, otherwise an <tt>IllegalArgumentException</tt> * will be thrown. <br> * The implementation is as follows: * <br><br> * <code><pre> * for (int i=0; i<tt><</tt>intervals.length; i++) * intervals[i].intersect(multiDimInterval.intervals[i]); * return this; * </code></pre> * The intersection is applied to every dimension of the two * intervals by invoking the method {@link xxl.core.util.Interval1D#intersect(Interval1D)} * on this interval's ith component with the appertaining ith component of * the specified array. * * @param multiDimInterval The interval to be intersected with. * @return The interval calling this method. * @throws IllegalArgumentException if the intersection cannot be performed properly. */ public IntervalND intersect (IntervalND multiDimInterval) throws IllegalArgumentException { if (!overlaps(multiDimInterval)) throw new IllegalArgumentException("Intervals do not overlap."); try { for (int i=0; i<intervals.length; i++) intervals[i].intersect(multiDimInterval.intervals[i]); return this; } catch (Exception e) { throw new IllegalArgumentException(); } } }