All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.math.IRectangularArea Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.math;

import java.util.*;

/**
 * 

Rectangular integer area, i.e. * hyperparallelepiped in multidimensional space with integer coordinates of vertices. * All edges of the hyperparallelepiped are parallel to coordinate axes. * In 1-dimensional case it is an equivalent of {@link IRange} class, * in 2-dimensional case it is an analog of the standard java.awt.Rectangle class.

* *

More precisely, the region, specified by this class, is defined by two n-dimensional points * with integer coordinates ({@link IPoint}), * named the minimal vertex min and maximal vertex max, * and consists of all such points * (x0, x1, ..., xn−1), that:

* *
* min.{@link IPoint#coord(int) coord(0)} ≤ x0 ≤ * max.{@link IPoint#coord(int) coord(0)},
* min.{@link IPoint#coord(int) coord(1)} ≤ x1 ≤ * max.{@link IPoint#coord(int) coord(1)},
* ...,
* min.{@link IPoint#coord(int) coord(n-1)} ≤ xn−1 ≤ * max.{@link IPoint#coord(int) coord(n-1)}. *
* *

The min and max points are specified while creating an instance of this class * and can be retrieved by {@link #min()} and {@link #max()} methods.

* *

The coordinates of the minimal vertex * min.{@link IPoint#coord(int) coord(i)} * are never greater than the corresponding coordinates of the maximal vertex * max.{@link IPoint#coord(int) coord(i)}, * the coordinates of the minimal and maximal vertices are always in range * -Long.MAX_VALUE+1..Long.MAX_VALUE-1, * and their difference is always less than Long.MAX_VALUE. * In other words, * "max.{@link IPoint#coord(int) * coord(i)}-min.{@link IPoint#coord(int) coord(i)}+1" expression, * returned by {@link #size(int)} method, and also * "min.{@link IPoint#coord(int) coord(i)}-1", * "min.{@link IPoint#coord(int) coord(i)}-2" and * "max.{@link IPoint#coord(int) coord(i)}+1" expressions * are always calculated without overflow, * and the {@link #range(int)} method is always possible to return an allowed range. * *

This class is immutable and thread-safe: * there are no ways to modify settings of the created instance.

* * @author Daniel Alievsky * @see RectangularArea */ public class IRectangularArea { final IPoint min; final IPoint max; private IRectangularArea(IPoint min, IPoint max) { this.min = min; this.max = max; } /** * Returns an instance of this class with the given minimal vertex min and * maximal vertex max. * See the {@link IRectangularArea comments to this class} for more details. * * @param min the minimal vertex, inclusive. * @param max the maximal vertex, inclusive. * @return the new rectangular area "between" these vertices. * @throws NullPointerException if one of arguments is {@code null}. * @throws IllegalArgumentException if the {@link #coordCount() numbers of dimensions} in min * and max points are different, * or if, for some i, * min.{@link IPoint#coord(int) coord}(i) * > max.{@link IPoint#coord(int) coord}(i), * or if max.{@link IPoint#coord(int) * coord}(i)-min.{@link IPoint#coord(int) * coord}(i)+1 * > Long.MAX_VALUE * (more precisely, if this Java expression is nonpositive * due to integer overflow), * or if min.{@link IPoint#coord(int) * coord}(i) <= -Long.MAX_VALUE, * or if max.{@link IPoint#coord(int) * coord}(i) == Long.MAX_VALUE. */ public static IRectangularArea valueOf(IPoint min, IPoint max) { return valueOf(min, max, false); } /** * Returns the Cartesian product of the specified coordinate ranges. * More precisely, return an n-dimensional {@link IRectangularArea rectangular area} * with the minimal vertex min and maximal vertex max, where * n=coordRanges.length, * min.{@link IPoint#coord(int) * coord(i)}=coordRanges[i].{@link IRange#min() min()}, * max.{@link IPoint#coord(int) * coord(i)}=coordRanges[i].{@link IRange#max() max()}. * See the {@link IRectangularArea comments to this class} for more details. * * @param coordRanges the coordinate ranges. * @return the Cartesian product of the specified coordinate ranges. * @throws NullPointerException if the argument is {@code null} * or if one of specified coordRanges is {@code null}. * @throws IllegalArgumentException if the passed array is empty (no ranges are passed). */ public static IRectangularArea valueOf(IRange... coordRanges) { Objects.requireNonNull(coordRanges, "Null coordRanges argument"); int n = coordRanges.length; if (n == 0) { throw new IllegalArgumentException("Empty coordRanges array"); } coordRanges = coordRanges.clone(); // cloning before checking guarantees correct check while multithreading for (int k = 0; k < n; k++) { Objects.requireNonNull(coordRanges[k], "Null coordRanges[" + k + "]"); } long[] min = new long[n]; long[] max = new long[n]; for (int k = 0; k < n; k++) { min[k] = coordRanges[k].min; max[k] = coordRanges[k].max; } return new IRectangularArea(new IPoint(min), new IPoint(max)); } /** * Returns a 1-dimensional rectangular area (range) with the given minimal and maximal vertex. * Equivalent to *
     * {@link #valueOf(IPoint, IPoint) valueOf}(
     *      {@link IPoint#valueOf(long) IPoint.valueOf}(minX),
     *      {@link IPoint#valueOf(long) IPoint.valueOf}(maxX));
     * 
* * @param minX the minimal x-coordinate, inclusive. * @param maxX the maximal x-coordinate, inclusive. * @return the new 1-dimensional rectangular area. * @throws IllegalArgumentException in the same situations as {@link #valueOf(IPoint, IPoint)} method. */ public static IRectangularArea valueOf(long minX, long maxX) { return valueOf( IPoint.valueOf(minX), IPoint.valueOf(maxX)); } /** * Returns a 2-dimensional rectangle with the given minimal and maximal vertex. * Equivalent to *
     * {@link #valueOf(IPoint, IPoint) valueOf}(
     *      {@link IPoint#valueOf(long...) IPoint.valueOf}(minX, minY),
     *      {@link IPoint#valueOf(long...) IPoint.valueOf}(maxX, maxY));
     * 
* * @param minX the minimal x-coordinate, inclusive. * @param minY the minimal y-coordinate, inclusive. * @param maxX the maximal x-coordinate, inclusive. * @param maxY the maximal y-coordinate, inclusive. * @return the new 2-dimensional rectangle. * @throws IllegalArgumentException in the same situations as {@link #valueOf(IPoint, IPoint)} method. */ public static IRectangularArea valueOf(long minX, long minY, long maxX, long maxY) { return valueOf( IPoint.valueOf(minX, minY), IPoint.valueOf(maxX, maxY)); } /** * Returns a 3-dimensional parallelepiped with the given minimal and maximal vertex. * Equivalent to *
     * {@link #valueOf(IPoint, IPoint) valueOf}(
     *      {@link IPoint#valueOf(long...) IPoint.valueOf}(minX, minY, minZ),
     *      {@link IPoint#valueOf(long...) IPoint.valueOf}(maxX, maxY, maxZ));
     * 
* * @param minX the minimal x-coordinate, inclusive. * @param minY the minimal y-coordinate, inclusive. * @param minZ the minimal z-coordinate, inclusive. * @param maxX the maximal x-coordinate, inclusive. * @param maxY the maximal y-coordinate, inclusive. * @param maxZ the maximal z-coordinate, inclusive. * @return the new 3-dimensional parallelepiped. * @throws IllegalArgumentException in the same situations as {@link #valueOf(IPoint, IPoint)} method. */ public static IRectangularArea valueOf(long minX, long minY, long minZ, long maxX, long maxY, long maxZ) { return valueOf( IPoint.valueOf(minX, minY, minZ), IPoint.valueOf(maxX, maxY, maxZ)); } /** * Returns a new rectangular area with the same coordinates as the given area. * All double coordinates of the passed area are converted * to long coordinates of the returned area by standard * Java typecast (long)doubleValue. * Equivalent to {@link #valueOf(IPoint, IPoint) valueOf}({@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #min() min()}), {@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #max() max()})). * * @param area the real rectangular area. * @return the integer rectangular area with same (cast) coordinates. * @throws NullPointerException if the passed area is {@code null}. * @throws IllegalArgumentException if the points {@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #min() min()}) * and {@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #max() max()}) * do not match requirements of {@link #valueOf(IPoint, IPoint)} method. */ public static IRectangularArea valueOf(RectangularArea area) { Objects.requireNonNull(area, "Null area argument"); return valueOf(IPoint.valueOf(area.min), IPoint.valueOf(area.max)); } /** * Returns a new rectangular area with the same coordinates as the given area. * All double coordinates of the passed area are converted * to long coordinates of the returned area by StrictMath.round method. * Java typecast (long)doubleValue. * Equivalent to {@link #valueOf(IPoint, IPoint) valueOf}({@link IPoint#roundOf(Point) * IPoint.roundOf}(area.{@link #min() min()}), {@link IPoint#roundOf(Point) * IPoint.roundOf}(area.{@link #max() max()})). * * @param area the real rectangular area. * @return the integer rectangular area with same (rounded) coordinates. * @throws NullPointerException if the passed area is {@code null}. * @throws IllegalArgumentException if the points {@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #min() min()}) * and {@link IPoint#valueOf(Point) * IPoint.valueOf}(area.{@link #max() max()}) * do not match requirements of {@link #valueOf(IPoint, IPoint)} method. */ public static IRectangularArea roundOf(RectangularArea area) { Objects.requireNonNull(area, "Null area argument"); return valueOf(IPoint.roundOf(area.min), IPoint.roundOf(area.max)); } /** * Returns the number of dimensions of this rectangular area. * Equivalent to {@link #min()}.{@link IPoint#coordCount() coordCount()} * or {@link #max()}.{@link IPoint#coordCount() coordCount()}, but works faster. * *

The result of this method is always positive (>0). * * @return the number of dimensions of this rectangular area. */ public int coordCount() { return min.coordinates.length; } /** * Returns the minimal vertex of this rectangular area: * the point with minimal coordinates, belonging to this area. * See the {@link IRectangularArea comments to this class} for more details. * * @return the minimal vertex of this rectangular area. */ public IPoint min() { return min; } /** * Returns the maximal vertex of this rectangular area: * the point with maximal coordinates, belonging to this area. * See the {@link IRectangularArea comments to this class} for more details. * * @return the maximal vertex of this rectangular area. */ public IPoint max() { return max; } /** * Returns all sizes of this rectangular area in a form of {@link IPoint}. * Equivalent to {@link IPoint#valueOf(long...) IPoint.valueOf}({@link #sizes()}). * The coordinates of the returned point are greater by 1 than coordinates of * {@link #max()}.{@link IPoint#subtract(IPoint) subtract}({@link #min()}). * * @return all sizes of this rectangular area in a form of {@link IPoint}. */ public IPoint size() { return new IPoint(sizes()); } /** * Returns all sizes of this rectangular area, decreased by 1, in a form of {@link IPoint}. * Equivalent to {@link IPoint#valueOf(long...) IPoint.valueOf}({@link #widths()}). * The coordinates of the returned point are equal to coordinates of * {@link #max()}.{@link IPoint#subtract(IPoint) subtract}({@link #min()}). * * @return all sizes of this rectangular area, decreased by 1, in a form of {@link IPoint}. */ public IPoint width() { return new IPoint(widths()); } /** * Returns {@link #min()}.{@link IPoint#coord(int) coord}(coordIndex). * * @param coordIndex the index of the coordinate. * @return {@link #min()}.{@link IPoint#coord(int) coord}(coordIndex). * @throws IndexOutOfBoundsException if coordIndex<0 or * coordIndex>={@link #coordCount()}. */ public long min(int coordIndex) { return min.coordinates[coordIndex]; } /** * Returns {@link #max()}.{@link IPoint#coord(int) coord}(coordIndex). * * @param coordIndex the index of the coordinate. * @return {@link #max()}.{@link IPoint#coord(int) coord}(coordIndex). * @throws IndexOutOfBoundsException if coordIndex<0 or * coordIndex>={@link #coordCount()}. */ public long max(int coordIndex) { return max.coordinates[coordIndex]; } /** * Returns {@link #max(int) max}(coordIndex) - {@link #min(int) min}(coordIndex) + 1. * * @param coordIndex the index of the coordinate. * @return {@link #max(int) max}(coordIndex) - {@link #min(int) min}(coordIndex) + 1. * @throws IndexOutOfBoundsException if coordIndex<0 or * coordIndex>={@link #coordCount()}. */ public long size(int coordIndex) { return max.coordinates[coordIndex] - min.coordinates[coordIndex] + 1; } /** * Returns {@link #max(int) max}(coordIndex) - {@link #min(int) min}(coordIndex). * * @param coordIndex the index of the coordinate. * @return {@link #max(int) max}(coordIndex) - {@link #min(int) min}(coordIndex). * @throws IndexOutOfBoundsException if coordIndex<0 or * coordIndex>={@link #coordCount()}. */ public long width(int coordIndex) { return max.coordinates[coordIndex] - min.coordinates[coordIndex]; } /** * Returns {@link #min()}.{@link IPoint#x() x()}. * * @return {@link #min()}.{@link IPoint#x() x()}. */ public long minX() { return min.coordinates[0]; } /** * Returns {@link #max()}.{@link IPoint#x() x()}. * * @return {@link #max()}.{@link IPoint#x() x()}. */ public long maxX() { return max.coordinates[0]; } /** * Returns {@link #maxX() maxX()} - {@link #minX() minX()} + 1. * * @return {@link #maxX() maxX()} - {@link #minX() minX()} + 1. */ public long sizeX() { return max.coordinates[0] - min.coordinates[0] + 1; } /** * Returns {@link #maxX() maxX()} - {@link #minX() minX()}. * * @return {@link #maxX() maxX()} - {@link #minX() minX()}. */ public long widthX() { return max.coordinates[0] - min.coordinates[0]; } /** * Returns {@link #min()}.{@link IPoint#y() y()}. * * @return {@link #min()}.{@link IPoint#y() y()}. * @throws IllegalStateException if {@link #coordCount()}<2. */ public long minY() { if (min.coordinates.length < 2) { throw new IllegalStateException("Cannot get y-coordinates of " + coordCount() + "-dimensional area"); } return min.coordinates[1]; } /** * Returns {@link #max()}.{@link IPoint#y() y()}. * * @return {@link #max()}.{@link IPoint#y() y()}. * @throws IllegalStateException if {@link #coordCount()}<2. */ public long maxY() { if (min.coordinates.length < 2) { throw new IllegalStateException("Cannot get y-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[1]; } /** * Returns {@link #maxY() maxY()} - {@link #minY() minY()} + 1. * * @return {@link #maxY() maxY()} - {@link #minY() minY()} + 1. * @throws IllegalStateException if {@link #coordCount()}<2. */ public long sizeY() { if (min.coordinates.length < 2) { throw new IllegalStateException("Cannot get y-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[1] - min.coordinates[1] + 1; } /** * Returns {@link #maxY() maxY()} - {@link #minY() minY()}. * * @return {@link #maxY() maxY()} - {@link #minY() minY()}. * @throws IllegalStateException if {@link #coordCount()}<2. */ public long widthY() { if (min.coordinates.length < 2) { throw new IllegalStateException("Cannot get y-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[1] - min.coordinates[1]; } /** * Returns {@link #min()}.{@link IPoint#z() z()}. * * @return {@link #min()}.{@link IPoint#z() z()}. * @throws IllegalStateException if {@link #coordCount()}<3. */ public long minZ() { if (min.coordinates.length < 3) { throw new IllegalStateException("Cannot get z-coordinates of " + coordCount() + "-dimensional area"); } return min.coordinates[2]; } /** * Returns {@link #max()}.{@link IPoint#z() z()}. * * @return {@link #max()}.{@link IPoint#z() z()}. * @throws IllegalStateException if {@link #coordCount()}<3. */ public long maxZ() { if (min.coordinates.length < 3) { throw new IllegalStateException("Cannot get z-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[2]; } /** * Returns {@link #maxZ() maxZ()} - {@link #minZ() minZ()} + 1. * * @return {@link #maxZ() maxZ()} - {@link #minZ() minZ()} + 1. * @throws IllegalStateException if {@link #coordCount()}<3. */ public long sizeZ() { if (min.coordinates.length < 3) { throw new IllegalStateException("Cannot get z-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[2] - min.coordinates[2] + 1; } /** * Returns {@link #maxZ() maxZ()} - {@link #minZ() minZ()}. * * @return {@link #maxZ() maxZ()} - {@link #minZ() minZ()}. * @throws IllegalStateException if {@link #coordCount()}<3. */ public long widthZ() { if (min.coordinates.length < 3) { throw new IllegalStateException("Cannot get z-coordinates of " + coordCount() + "-dimensional area"); } return max.coordinates[2] - min.coordinates[2]; } /** * Returns the sizes of this rectangular area along all dimensions. * The returned array consists of {@link #coordCount()} elements, * and the element #k contains {@link #size(int) size}(k). * * @return the sizes of this rectangular area along all dimensions. */ public long[] sizes() { long[] sizes = new long[min.coordinates.length]; for (int k = 0; k < sizes.length; k++) { sizes[k] = max.coordinates[k] - min.coordinates[k] + 1; } return sizes; } /** * Returns the sizes of this rectangular area along all dimensions, decreased by 1. * The returned array consists of {@link #coordCount()} elements, * and the element #k contains {@link #width(int) width}(k). * * @return the sizes of this rectangular area along all dimensions, decreased by 1. */ public long[] widths() { long[] widths = new long[min.coordinates.length]; for (int k = 0; k < widths.length; k++) { widths[k] = max.coordinates[k] - min.coordinates[k]; } return widths; } /** * Returns the volume of this rectangular area: the product of all sizes * returned by {@link #sizes()} method. This area is calculated in double values. * * @return the multidimensional volume of this rectangular area (usual area in 2-dimensional case). */ public double volume() { double result = max.coordinates[0] - min.coordinates[0] + 1; for (int k = 1; k < min.coordinates.length; k++) { result *= max.coordinates[k] - min.coordinates[k] + 1; } return result; } /** * Returns {@link IRange}.{@link IRange#valueOf(long, long) * valueOf}({@link #min(int) min}(coordIndex), {@link #max(int) max}(coordIndex)). * * @param coordIndex the index of the coordinate. * @return {@link IRange}.{@link IRange#valueOf(long, long) * valueOf}({@link #min(int) min}(coordIndex), {@link #max(int) max}(coordIndex)). */ public IRange range(int coordIndex) { return new IRange(min.coordinates[coordIndex], max.coordinates[coordIndex]); } /** * Returns the projections of this rectangular area to all axes. * The returned array consists of {@link #coordCount()} elements, * and the element #k contains {@link #range(int) range}(k). * * @return the projections of this rectangular area to all axes. */ public IRange[] ranges() { IRange[] ranges = new IRange[min.coordinates.length]; for (int k = 0; k < ranges.length; k++) { ranges[k] = IRange.valueOf(min.coordinates[k], max.coordinates[k]); } return ranges; } /** * Returns true if and only if * {@link #min(int) min}(k)<=point.{@link IPoint#coord(int) coord}(k)<={@link #max(int) max}(k) * for all k. * * @param point the checked point. * @return true if this rectangular area contains the given point. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if point.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public boolean contains(IPoint point) { Objects.requireNonNull(point, "Null point argument"); int n = min.coordinates.length; if (point.coordinates.length != n) { throw new IllegalArgumentException("Dimensions count mismatch: " + point.coordinates.length + " instead of " + n); } for (int k = 0; k < n; k++) { if (point.coordinates[k] < min.coordinates[k] || point.coordinates[k] > max.coordinates[k]) { return false; } } return true; } /** * Returns true if at least one of the specified areas contains * the passed point * (see {@link #contains(IPoint)} method). * * @param areas list of checked rectangular areas. * @param point the checked point. * @return true if one of the passed areas contains the given point. * @throws NullPointerException if one of the arguments or one of the areas is {@code null}. * @throws IllegalArgumentException if point.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of one of areas. */ public static boolean contains(Collection areas, IPoint point) { Objects.requireNonNull(areas, "Null areas argument"); Objects.requireNonNull(point, "Null point argument"); for (IRectangularArea a : areas) { if (a.contains(point)) { return true; } } return false; } /** * Returns true if and only if * {@link #min(int) min}(k)<=area.{@link #min(int) min}(k) * and area.{@link #max(int) max}(k)<={@link #max(int) max}(k) * for all k. * * @param area the checked rectangular area. * @return true if the checked rectangular area is a subset of this area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public boolean contains(IRectangularArea area) { Objects.requireNonNull(area, "Null area argument"); int n = min.coordinates.length; if (area.min.coordinates.length != n) { throw new IllegalArgumentException("Dimensions count mismatch: " + area.min.coordinates.length + " instead of " + n); } for (int k = 0; k < n; k++) { if (area.min.coordinates[k] < min.coordinates[k] || area.max.coordinates[k] > max.coordinates[k]) { return false; } } return true; } /** * Returns true if at least one of the specified areas * contains the passed area * (see {@link #contains(IRectangularArea)} method). * * @param areas list of checked rectangular areas. * @param area the checked area. * @return true if one of the passed areas (1st argument) contains the given area (2nd argument). * @throws NullPointerException if one of the arguments or one of the areas is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of one of areas * in the 1st argument. */ public static boolean contains(Collection areas, IRectangularArea area) { Objects.requireNonNull(areas, "Null areas argument"); Objects.requireNonNull(area, "Null area argument"); for (IRectangularArea a : areas) { if (a.contains(area)) { return true; } } return false; } /** * Returns true if and only if * {@link #min(int) min}(k)<=area.{@link #max(int) max}(k) * and area.{@link #min(int) min}(k)<={@link #max(int) max}(k) * for all k. * * @param area the checked rectangular area. * @return true if the checked rectangular area overlaps with this area, * maybe in boundary points only. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public boolean intersects(IRectangularArea area) { Objects.requireNonNull(area, "Null area argument"); int n = min.coordinates.length; if (area.min.coordinates.length != n) { throw new IllegalArgumentException("Dimensions count mismatch: " + area.min.coordinates.length + " instead of " + n); } for (int k = 0; k < n; k++) { if (area.max.coordinates[k] < min.coordinates[k] || area.min.coordinates[k] > max.coordinates[k]) { return false; } } return true; } /*Repeat.SectionStart operationsAndParallelDistance*/ /** * Returns the set-theoretical intersection A ∩ B of this (A) and * the passed rectangular area (B) or {@code null} if they * do not {@link #intersects(IRectangularArea) intersect} * (A ∩ B = ∅). * Equivalent to *

thisInstance.{@link #intersects(IRectangularArea) intersects}(area) ? {@link #valueOf(IPoint, IPoint)
     * IRectangularArea.valueOf}(
     * thisInstance.{@link #min()}.{@link IPoint#max(IPoint) max}(area.{@link #min()}),
     * thisInstance.{@link #max()}.{@link IPoint#min(IPoint) min}(area.{@link #max()})) :
     * null
. * * @param area the second rectangular area. * @return intersection of this and the second rectangular area or {@code null} if they do not intersect. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public IRectangularArea intersection(IRectangularArea area) { Objects.requireNonNull(area, "Null area argument"); int n = min.coordinates.length; if (area.min.coordinates.length != n) { throw new IllegalArgumentException("Dimensions count mismatch: " + area.min.coordinates.length + " instead of " + n); } long[] newMin = new long[n]; long[] newMax = new long[n]; for (int k = 0; k < n; k++) { newMin[k] = Math.max(min.coordinates[k], area.min.coordinates[k]); newMax[k] = Math.min(max.coordinates[k], area.max.coordinates[k]); if (newMin[k] > newMax[k]) { return null; } } return new IRectangularArea(new IPoint(newMin), new IPoint(newMax)); } /** * Returns a list of set-theoretical intersections A ∩ Bi * of this rectangular area (A) and all rectangular areas (Bi), specified * by areas argument. * If the passed collection doesn't contain areas, intersecting this area, the result will be an empty list. *

Equivalent to the following loop: *

     * final List<IRectangularArea>gt; result = ... (some empty list);
     * for (IRectangularArea area : areas) {
     *     IRectangularArea intersection = {@link #intersection(IRectangularArea) intersection}(area);
     *     if (intersection != null) {
     *         result.add(intersection);
     *     }
     * }
     * 
. * * @param areas collection of areas (we find intersection with each from them). * @return intersection of this and the second rectangular area or {@code null} if they do not intersect. * @throws NullPointerException if the argument is {@code null} or one of its elements is {@code null}. * @throws IllegalArgumentException if this rectangular area or some of the elements of the passed collection * have different {@link #coordCount()}. */ public List intersection(Collection areas) { Objects.requireNonNull(areas, "Null areas argument"); final List result = new ArrayList<>(); for (IRectangularArea area : areas) { IRectangularArea intersection = intersection(area); if (intersection != null) { result.add(intersection); } } return result; } /** * Calculates the set-theoretical difference A \ B of this (A) and * the passed rectangular area (B) * in a form of N rectangular areas * R1,R2,...,RN, * the set-theoretical union of which is equal to this difference * (R1R2∪...∪RN = * A \ B). * The resulting areas R1,R2,...,RN * are added into the collection results by Collection.add(...) method. * So, the collection results must be not-null and support adding elements * (usually it is List or Queue). * *

It is possible that the difference is empty (A \ B = ∅), * i.e. this area A is a subset of the passed one B. In this case, this method does nothing. * *

It is possible that the difference is equal to this area * (A \ B = A), * i.e. this area A does not intersect the passed one B. * In this case, this method is equivalent to results.add(thisInstance) call. * *

In other cases, there is more than 1 way to represent the resulting difference * in a form of union of several rectangular areas * R1,R2,...,RN. * The precise way, how this method forms this set of rectangular areas Ri, * is not documented, but this method tries to minimize the number N of such areas. * In any case, there is a guarantee that N≤2*{@link #coordCount()}. * * @param results the collection to store results (new areas will be added to this collection). * @param area the area B, subtracted from this area A. * @return a reference to the results argument. * @throws NullPointerException if result or area argument is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. * @see #subtractCollection(java.util.Queue, java.util.Collection) */ public Collection difference(Collection results, IRectangularArea area) { Objects.requireNonNull(results, "Null results argument"); if (!intersects(area)) { // also checks number of dimensions results.add(this); return results; } long[] min = this.min.coordinates.clone(); long[] max = this.max.coordinates.clone(); for (int k = min.length - 1; k >= 0; k--) { assert area.max.coordinates[k] >= min[k] && area.min.coordinates[k] <= max[k]; // because they intersect if (area.min.coordinates[k] > this.min.coordinates[k]) { min[k] = this.min.coordinates[k]; max[k] = area.min.coordinates[k] - 1; results.add(new IRectangularArea(IPoint.valueOf(min), IPoint.valueOf(max))); } if (area.max.coordinates[k] < this.max.coordinates[k]) { min[k] = area.max.coordinates[k] + 1; max[k] = this.max.coordinates[k]; results.add(new IRectangularArea(IPoint.valueOf(min), IPoint.valueOf(max))); } min[k] = Math.max(area.min.coordinates[k], this.min.coordinates[k]); max[k] = Math.min(area.max.coordinates[k], this.max.coordinates[k]); // - intersection of two ranges area.min-max[k] and this.min-max[k] } return results; } /** * Calculates the set-theoretical difference A \ B of * the set-theoretical union A of all elements of the collection fromWhatToSubtract * and the set-theoretical union B of all elements of the collection whatToSubtract, * in a form of a union of N rectangular areas, and replaces * the old content of fromWhatToSubtract with the resulting N areas. * *

More precisely, this method is equivalent to the following loop: * *

     * for (IRectangularArea area : whatToSubtract) {
     *     for (int i = 0, n = fromWhatToSubtract.size(); i < n; i++) {
     *         IRectangularArea minuend = fromWhatToSubtract.poll();
     *         minuend.{@link #difference(Collection, IRectangularArea) difference}(fromWhatToSubtract, area);
     *     }
     *     if (fromWhatToSubtract.isEmpty()) {
     *         break;
     *     }
     * }
     * 
* *

Note: if some exception occurs while execution of the listed loop (for example, * some elements of the collections are {@code null} or have different number of dimensions), * the fromWhatToSubtract stays partially modified. * In other words, this method is non-atomic regarding failures. * * @param fromWhatToSubtract the minuend A, which will be replaced with A \ B. * @param whatToSubtract the subtrahend B. * @return a reference to fromWhatToSubtract argument, which will contain * the difference A \ B. * @throws NullPointerException if fromWhatToSubtract or whatToSubtract argument * is {@code null} or if one of their elements it {@code null}. * @throws IllegalArgumentException if some of the elements of the passed collections * have different {@link #coordCount()}. * @see #subtractCollection(java.util.Queue, IRectangularArea...) */ public static Queue subtractCollection( Queue fromWhatToSubtract, Collection whatToSubtract) { Objects.requireNonNull(fromWhatToSubtract, "Null fromWhatToSubtract"); Objects.requireNonNull(whatToSubtract, "Null whatToSubtract"); for (IRectangularArea area : whatToSubtract) { for (int i = 0, n = fromWhatToSubtract.size(); i < n; i++) { IRectangularArea minuend = fromWhatToSubtract.poll(); minuend.difference(fromWhatToSubtract, area); } if (fromWhatToSubtract.isEmpty()) { break; } } return fromWhatToSubtract; } /** * Equivalent to {@link #subtractCollection(Queue, Collection) * subtractCollection}(fromWhatToSubtract, java.util.Arrays.asList(whatToSubtract)). * * @param fromWhatToSubtract the minuend A, which will be replaced with A \ B. * @param whatToSubtract the subtrahend B. * @return a reference to fromWhatToSubtract argument, which will contain * the difference A \ B. * @throws NullPointerException if fromWhatToSubtract or whatToSubtract argument * is {@code null} or if one of their elements it {@code null}. * @throws IllegalArgumentException if some of the elements of the passed collection and array * have different {@link #coordCount()}. */ public static Queue subtractCollection( Queue fromWhatToSubtract, IRectangularArea... whatToSubtract) { return subtractCollection(fromWhatToSubtract, java.util.Arrays.asList(whatToSubtract)); } /** * Equivalent to {@link #subtractCollection(Queue, Collection) * subtractCollection}(fromWhatToSubtract, whatToSubtract, * where fromWhatToSubtract contains this object as the only element. * * @param whatToSubtract the subtrahend B. * @return new collection, containing the difference A \ B * (A = this object, B = union of all whatToSubtract). * @throws NullPointerException if whatToSubtract argument * is {@code null} or if one of their elements it {@code null}. * @throws IllegalArgumentException if this rectangular area or some of the elements of the passed collection * have different {@link #coordCount()}. */ public Queue subtract(Collection whatToSubtract) { Objects.requireNonNull(whatToSubtract, "Null whatToSubtract"); Queue difference = new ArrayDeque<>(); difference.add(this); IRectangularArea.subtractCollection(difference, whatToSubtract); return difference; } /** * Equivalent to {@link #subtract(Collection) * subtract}(java.util.Arrays.asList(whatToSubtract)). * * @param whatToSubtract the subtrahend B. * @return new collection, containing the difference A \ B * (A = this object, B = union of all whatToSubtract). * @throws NullPointerException if whatToSubtract argument * is {@code null} or if one of their elements it {@code null}. * @throws IllegalArgumentException if this rectangular area or some of the elements of the passed array * have different {@link #coordCount()}. */ public Queue subtract(IRectangularArea... whatToSubtract) { return subtract(java.util.Arrays.asList(whatToSubtract)); } /** * Returns the minimal rectangular area, containing this area and the given point. * In the returned area, the {@link #min() minimal vertex} is equal to * thisInstance.{@link #min()}.{@link IPoint#min(IPoint) min}(point) and * the {@link #max() maximal vertex} is equal to * thisInstance.{@link #max()}.{@link IPoint#max(IPoint) max}(point). * * @param point some point that should be included to the new rectangular area. * @return the expanded rectangular area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if point.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance, * or if the points * thisInstance.{@link #min()}.{@link IPoint#min(IPoint) min}(point) * and * thisInstance.{@link #max()}.{@link IPoint#max(IPoint) max}(point) * do not match requirements of {@link #valueOf(IPoint, IPoint)} method. */ public IRectangularArea expand(IPoint point) { if (contains(point)) { // - also checks number of dimensions return this; } long[] newMin = new long[min.coordinates.length]; long[] newMax = new long[min.coordinates.length]; for (int k = 0; k < min.coordinates.length; k++) { newMin[k] = min.coordinates[k] <= point.coordinates[k] ? min.coordinates[k] : point.coordinates[k]; newMax[k] = max.coordinates[k] >= point.coordinates[k] ? max.coordinates[k] : point.coordinates[k]; } return valueOf(new IPoint(newMin), new IPoint(newMax)); } /** * Returns the minimal rectangular area, containing this and the passed area. * Equivalent to *

{@link #valueOf(IPoint, IPoint) IRectangularArea.valueOf}(
     * thisInstance.{@link #min()}.{@link IPoint#min(IPoint) min}(area.{@link #min()}),
     * thisInstance.{@link #max()}.{@link IPoint#max(IPoint) max}(area.{@link #max()}))
. * * @param area the second rectangular area. * @return the minimal rectangular area, containing this and the passed area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if area.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public IRectangularArea expand(IRectangularArea area) { if (contains(area)) { // - also checks number of dimensions return this; } long[] newMin = new long[min.coordinates.length]; long[] newMax = new long[min.coordinates.length]; for (int k = 0; k < min.coordinates.length; k++) { newMin[k] = min.coordinates[k] <= area.min.coordinates[k] ? min.coordinates[k] : area.min.coordinates[k]; newMax[k] = max.coordinates[k] >= area.max.coordinates[k] ? max.coordinates[k] : area.max.coordinates[k]; } return new IRectangularArea(new IPoint(newMin), new IPoint(newMax)); } /** * Returns the minimal rectangular area, containing all passed areas. * Equivalent to the loop of {@link #expand(IRectangularArea)} methods, called for each element * of the passed collection, but works faster. * *

If the passed collection is empty, returns {@code null}. * * @param areas some collection of rectangular areas. * @return the minimal rectangular area, containing all them, or {@code null} for empty collection. * @throws NullPointerException if the argument or one of the passed areas is {@code null}. * @throws IllegalArgumentException if {@link #coordCount() coordCount()} is not equal for all areas. */ public static IRectangularArea minimalContainingArea(Collection areas) { Objects.requireNonNull(areas, "Null areas"); if (areas.isEmpty()) { return null; } final int coordCount = areas.iterator().next().coordCount(); final long[] min = new long[coordCount]; final long[] max = new long[coordCount]; java.util.Arrays.fill(min, Long.MAX_VALUE); java.util.Arrays.fill(max, Long.MIN_VALUE); for (IRectangularArea area : areas) { if (area.coordCount() != coordCount) { throw new IllegalArgumentException("Some areas have different number of dimension: " + area.coordCount() + " and " + coordCount); } for (int k = 0; k < coordCount; k++) { min[k] = Math.min(min[k], area.min(k)); max[k] = Math.max(max[k], area.max(k)); } } return IRectangularArea.valueOf(new IPoint(min), new IPoint(max)); } /** * Returns the parallel distance from the given point to this rectangular area. * The parallel distance is a usual distance, with plus or minus sign, * from the point to some of hyperplanes, containing the hyperfacets of this hyperparallelepiped, * chosen so that: * *

    *
  1. the parallel distance is zero at the hyperfacets, negative inside the rectangular area and * positive outside it;
  2. *
  3. for any constant c, * the set of all such points, that the parallel distance from them to this rectangular area ≤c, * is also hyperparallelepiped (rectangular area) wich hyperfacets, * parallel to the the coordinate hyperplanes, * or an empty set if c<c0, where c0 is the (negative) * parallel distance from the geometrical center of this hyperparallelepiped.
  4. *
* *

Formally, let p is any point with coordinates * p0, p1, ..., pn−1, * li = {@link #min(int) min}(i), * ri = {@link #max(int) max}(i), * di = max(lipi, * piri). * Note that di is positive if pi<li * or pi>ri and negative if pi * is inside li..ri range. * The parallel distance from the point p to this rectangular area * is defined as maximal value from all di: * max(d0, d1, ..., dn−1). * * @param point some point. * @return the parallel distance from this point to this rectangular area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if point.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public long parallelDistance(IPoint point) { Objects.requireNonNull(point, "Null point argument"); return parallelDistance(point.coordinates); } /** * Equivalent to {@link #parallelDistance(IPoint) parallelDistance}({@link IPoint#valueOf(long...) * IPoint.valueOf}(coordinates)), but works faster because does not require to create an instance * of {@link IPoint} class. * * @param coordinates coordinates of some point. * @return the parallel distance from this point to this rectangular area. * @throws NullPointerException if coordinates argument is {@code null}. * @throws IllegalArgumentException if coordinates.length is not equal to * the {@link #coordCount() number of dimensions} of this instance. */ public long parallelDistance(long... coordinates) { Objects.requireNonNull(coordinates, "Null coordinates argument"); int n = this.min.coordinates.length; if (coordinates.length != n) { throw new IllegalArgumentException("Dimensions count mismatch: " + coordinates.length + " instead of " + n); } long min = this.min.coordinates[0]; long max = this.max.coordinates[0]; long x = coordinates[0]; long maxD = min - x >= x - max ? min - x : x - max; for (int k = 1; k < n; k++) { min = this.min.coordinates[k]; max = this.max.coordinates[k]; long xk = coordinates[k]; long d = min - xk >= xk - max ? min - xk : xk - max; if (d > maxD) { maxD = d; } } return maxD; } /** * Equivalent to {@link #parallelDistance(IPoint) parallelDistance}({@link IPoint#valueOf(long...) * IPoint.valueOf}(x, y)), but works faster because does not require to allocate any objects. * Works only for 2-dimensional rectangular areas, in other cases throws * IllegalArgumentException. * * @param x the 1st coordinate of some point. * @param y the 2nd coordinate of some point. * @return the parallel distance from this point to this rectangular area. * @throws IllegalArgumentException if coordinates.length!=2 . */ public long parallelDistance(long x, long y) { int n = min.coordinates.length; if (n != 2) { throw new IllegalArgumentException("Dimensions count mismatch: 2 instead of " + n); } long min = this.min.coordinates[0]; long max = this.max.coordinates[0]; long maxD = min - x >= x - max ? min - x : x - max; min = this.min.coordinates[1]; max = this.max.coordinates[1]; long d = min - y >= y - max ? min - y : y - max; if (d > maxD) { maxD = d; } return maxD; } /** * Equivalent to {@link #parallelDistance(IPoint) parallelDistance}({@link IPoint#valueOf(long...) * IPoint.valueOf}(x, y, z)), but works faster because does not require to allocate any objects. * Works only for 3-dimensional rectangular areas, in other cases throws * IllegalArgumentException. * * @param x the 1st coordinate of some point. * @param y the 2nd coordinate of some point. * @param z the 3rd coordinate of some point. * @return the parallel distance from this point to this rectangular area. * @throws IllegalArgumentException if coordinates.length!=2 . */ public long parallelDistance(long x, long y, long z) { int n = min.coordinates.length; if (n != 3) { throw new IllegalArgumentException("Dimensions count mismatch: 3 instead of " + n); } long min = this.min.coordinates[0]; long max = this.max.coordinates[0]; long maxD = min - x >= x - max ? min - x : x - max; min = this.min.coordinates[1]; max = this.max.coordinates[1]; long d = min - y >= y - max ? min - y : y - max; if (d > maxD) { maxD = d; } min = this.min.coordinates[2]; max = this.max.coordinates[2]; d = min - z >= z - max ? min - z : z - max; if (d > maxD) { maxD = d; } return maxD; } /*Repeat.SectionEnd operationsAndParallelDistance*/ /** * Shifts this rectangular area by the specified vector and returns the shifted area. * Equivalent to *

{@link #valueOf(IPoint, IPoint)
     * valueOf}(thisInstance.{@link #min()}.{@link IPoint#addExact(IPoint)
     * addExact}(vector), thisInstance.{@link #max()}.{@link IPoint#addExact(IPoint) addExact}(vector))
* *

Note: the coordinates of new areas are calculated with overflow control. * * @param vector the vector which is added to all vertices of this area. * @return the shifted area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if vector.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance, * or if the result is illegal due to the integer overflow. * @throws ArithmeticException in a case of long overflow. */ public IRectangularArea shift(IPoint vector) { Objects.requireNonNull(vector, "Null vector argument"); if (vector.coordinates.length != min.coordinates.length) { throw new IllegalArgumentException("Dimensions count mismatch: " + vector.coordinates.length + " instead of " + min.coordinates.length); } if (vector.isOrigin()) { return this; } return IRectangularArea.valueOf(min.addExact(vector), max.addExact(vector)); } /** * Shifts this rectangular area by vector.{@link IPoint#symmetric() symmetric()} * and returns the shifted area. * Equivalent to *

{@link #valueOf(IPoint, IPoint)
     * valueOf}(thisInstance.{@link #min()}.{@link IPoint#subtractExact(IPoint)
     * subtractExact}(vector), thisInstance.{@link #max()}.{@link IPoint#subtractExact(IPoint) subtractExact}(vector))
* *

Note: the coordinates of new areas are calculated with overflow control. * * @param vector the vector which is subtracted from all vertices of this area. * @return the shifted area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if vector.{@link IPoint#coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance, * or if the result is illegal due to the integer overflow. * @throws ArithmeticException in a case of long overflow. */ public IRectangularArea shiftBack(IPoint vector) { Objects.requireNonNull(vector, "Null vector argument"); if (vector.coordinates.length != min.coordinates.length) { throw new IllegalArgumentException("Dimensions count mismatch: " + vector.coordinates.length + " instead of " + min.coordinates.length); } if (vector.isOrigin()) { return this; } return IRectangularArea.valueOf(min.subtractExact(vector), max.subtractExact(vector)); } /** * Returns this rectangular area, dilated (expanded) according the argument. More precisely, * returns *

IRectangularArea.valueOf(
     * thisInstance.{@link #min() min()}.{@link IPoint#subtractExact(IPoint) subtractExact}(expansion),
     * thisInstance.{@link #max() max()}.{@link IPoint#addExact(IPoint) addExact}(expansion))
* (but if expansion.{@link IPoint#isOrigin() isOrigin()}, return this object without changes). * * @param expansion how to dilate this area. * @return dilated area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if expansion.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance, * or if the result area will be incorrect (see comments to * {@link #valueOf(IPoint, IPoint)} method). * @throws ArithmeticException in a case of long overflow. */ public IRectangularArea dilate(IPoint expansion) { Objects.requireNonNull(expansion, "Null expansion"); if (expansion.coordCount() != coordCount()) { throw new IllegalArgumentException("Dimensions count mismatch: " + expansion.coordCount() + " instead of " + coordCount()); } if (expansion.isOrigin()) { return this; } return IRectangularArea.valueOf(min().subtractExact(expansion), max().addExact(expansion)); } /** * Equivalent to 3{@link #dilate(IPoint) dilate}(IPoint.valueOfEqualCoordinates(thisObjects.{@link * #coordCount() coordCount()}, expansion). * * @param expansion how to dilate this area. * @return dilated area. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if the result area will be incorrect (see comments to * {@link #valueOf(IPoint, IPoint)} method). * @throws ArithmeticException in a case of long overflow. */ public IRectangularArea dilate(long expansion) { return dilate(IPoint.valueOfEqualCoordinates(coordCount(), expansion)); } /** * Returns this area, dilated according the argument only along coordinate axes, * without full hypercube areas near vertices (like in {@link #dilate(IPoint)} method). * *

More precisely, the result is a list, consisting of this area and (usually) 2*{@link #coordCount()} * rectangular areas, lying along facets of this area, like in the following picture: *

     *     aaaaaaaaaaaa
     *   bbRRRRRRRRRRRRcc
     *   bbRRRRRRRRRRRRcc
     *   bbRRRRRRRRRRRRcc
     *     ddddddddddd
     * 
* This figure shows dilation of some 2-dimensional rectangle R by * expansion=IPoint.valueOf(2,1): * the results consists of the original rectangle and 4 rectangles a, b (height 1) and * c, d (width 2). * *

Note: all coordinates of expansion argument must be non-negative * (unlike {@link #dilate(IPoint)} method). * *

Note: the coordinates of new areas are calculated with overflow control: * if the result cannot be exactly represented by 64-bit long integers, * this method throws ArithmeticException. * *

If some of coordinates of the point expansion are zero, new areas along the corresponding * facets are not added (recanglar area cannot be empty). * In particular, if expansion.{@link IPoint#isOrigin() isOrigin()}, * the result will contain this area as the only element. * * @param results the list to store results (new areas will be added to the end of this list). * @param expansion how to dilate this area. * @return a reference to the results argument. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if expansion.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of this instance, * or if one of coordinates of expansion is negative, * or if the result area will be incorrect (see comments to * {@link #valueOf(IPoint, IPoint)} method). * @throws ArithmeticException in a case of long overflow. */ public List dilateStraightOnly(List results, IPoint expansion) { Objects.requireNonNull(results, "Null results"); Objects.requireNonNull(expansion, "Null expansion"); results.add(this); final int coordCount = coordCount(); if (expansion.coordCount() != coordCount) { throw new IllegalArgumentException("Dimensions count mismatch: " + expansion.coordCount() + " instead of " + coordCount); } final long[] min = this.min.coordinates(); final long[] max = this.max.coordinates(); for (int k = 0; k < coordCount; k++) { final long delta = expansion.coordinates[k]; if (delta == 0) { continue; } if (delta < 0) { throw new IllegalArgumentException("Negative expansion is impossible: " + expansion); } final long saveMin = min[k]; final long saveMax = max[k]; min[k] = IPoint.subtractExact(saveMin, delta); max[k] = IPoint.subtractExact(saveMin, 1); results.add(IRectangularArea.valueOf(IPoint.valueOf(min), IPoint.valueOf(max))); min[k] = IPoint.addExact(saveMax, 1); max[k] = IPoint.addExact(saveMax, delta); results.add(IRectangularArea.valueOf(IPoint.valueOf(min), IPoint.valueOf(max))); min[k] = saveMin; max[k] = saveMax; } return results; } /** * Equivalent to {@link #dilateStraightOnly(List, IPoint) * dilateStraightOnly}(results, IPoint.valueOfEqualCoordinates(thisObjects.{@link * #coordCount() coordCount()}, expansion). * * @param results the list to store results (new areas will be added to the end of this list). * @param expansion how to dilate this area. * @return a reference to the results argument. * @throws NullPointerException if the argument is {@code null}. * @throws IllegalArgumentException if expansion < 0 * or if the result area will be incorrect (see comments to * {@link #valueOf(IPoint, IPoint)} method). * @throws ArithmeticException in a case of long overflow. */ public List dilateStraightOnly(List results, long expansion) { return dilateStraightOnly(results, IPoint.valueOfEqualCoordinates(coordCount(), expansion)); } /** * Dilates all areas, specified by the argument, by {@link #dilate(IPoint) dilate} or * {@link #dilateStraightOnly(List, IPoint) dilateStraightOnly} method, * and returns the list of dilated areas. *

If straightOnly argument is false, this method is equivalent to the following code: *

     * final List<IRectangularArea> result = new ArrayList<IRectangularArea>();
     * for (IRectangularArea area : areas) {
     *     result.add(area.{@link #dilate(IPoint) dilate}(expansion));
     * }
*

If straightOnly argument is true, this method is equivalent to the following code: *

     * final List<IRectangularArea> result = new ArrayList<IRectangularArea>();
     * for (IRectangularArea area : areas) {
     *     area.{@link #dilateStraightOnly(List, IPoint) dilateStraightOnly}(result, expansion);
     * }
*

Note that in the second case the resulting list will usually contain more elements than * the source areas collection. * * @param areas areas to be dilated. * @param expansion how to dilate these areas. * @param straightOnly dilation mode. * @return list of dilated areas. * @throws NullPointerException if one of the arguments is {@code null} or one of areas is {@code null}. * @throws IllegalArgumentException if expansion.{@link #coordCount() coordCount()} is not equal to * the {@link #coordCount() number of dimensions} of one of areas, * or if straightOnly amd one of coordinates * of expansion * is negative (and collection of areas is not empty), * or if one of the result areas will be incorrect (see comments to * {@link #valueOf(IPoint, IPoint)} method). * @throws ArithmeticException in a case of long overflow. */ public static List dilate( Collection areas, IPoint expansion, boolean straightOnly) { Objects.requireNonNull(areas, "Null areas"); final List result = new ArrayList<>(); for (IRectangularArea area : areas) { if (straightOnly) { area.dilateStraightOnly(result, expansion); } else { result.add(area.dilate(expansion)); } } return result; } /** * Equivalent to * {@link RectangularArea#valueOf(IRectangularArea) RectangularArea.valueOf}(thisInstance). * * @return the rectangular area with same real coordinates as this one. */ public RectangularArea toRectangularArea() { return RectangularArea.valueOf(this); } /** * Returns a brief string description of this object. * *

The result of this method may depend on implementation and usually contains * information about all coordinates ranges between the minimum and maximum vertices of this area. * * @return a brief string description of this object. */ public String toString() { StringBuilder sb = new StringBuilder("[" + range(0) + "]"); for (int k = 1; k < min.coordinates.length; k++) { sb.append("x").append("[").append(range(k)).append("]"); } return sb.toString(); } /** * Returns the hash code of this rectangular area. * * @return the hash code of this rectangular area. */ public int hashCode() { return min.hashCode() * 37 + max.hashCode(); } /** * Indicates whether some other rectangular area is equal to this instance. * Returns true if and only if obj instanceof IRectangularArea, * ((IRectangularArea)obj).min().equals(this.min()) and * ((IRectangularArea)obj).max().equals(this.max()). * * @param obj the object to be compared for equality with this instance. * @return true if the specified object is a rectangular area equal to this one. */ public boolean equals(Object obj) { return obj instanceof IRectangularArea && ((IRectangularArea) obj).min.equals(this.min) && ((IRectangularArea) obj).max.equals(this.max); } static IRectangularArea valueOf(IPoint min, IPoint max, boolean ise) { Objects.requireNonNull(min, "Null min vertex"); Objects.requireNonNull(max, "Null max vertex"); int n = min.coordinates.length; if (n != max.coordinates.length) { throw new IllegalArgumentException("min.coordCount() = " + n + " does not match max.coordCount() = " + max.coordinates.length); } for (int k = 0; k < n; k++) { if (min.coordinates[k] > max.coordinates[k]) { throw IRange.invalidBoundsException("min.coord(" + k + ") > max.coord(" + k + ")" + " (min = " + min + ", max = " + max + ")", ise); } if (max.coordinates[k] == Long.MAX_VALUE) { throw IRange.invalidBoundsException("max.coord(" + k + ") == Long.MAX_VALUE", ise); } if (min.coordinates[k] <= -Long.MAX_VALUE) { throw IRange.invalidBoundsException("min.coord(" + k + ") == Long.MAX_VALUE or Long.MIN_VALUE+1", ise); } if (max.coordinates[k] - min.coordinates[k] + 1L <= 0L) { throw IRange.invalidBoundsException("max.coord(" + k + ") - min.coord(" + k + ")" + " >= Long.MAX_VALUE (min = " + min + ", max = " + max + ")", ise); } } return new IRectangularArea(min, max); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy