Rectangle.java

package org.djutils.quadtree;

import java.io.Serializable;

import org.djutils.exceptions.Throw;

/**
 * Rectangle defines an area bounded by a lower and left edges (inclusive) and a upper and right edges (not inclusive). The
 * boundary values are stored in the object; unlike the Rectangle2D class where the width and height are stored and to the left
 * and bottom. Doing it this way absolutely ensures that we can make a grid of rectangles that have no ULP width gaps.
 * Additionally, this Rectangle object is immutable. Finally, there is no annoying name collision with the java.lang.Double
 * class.<br>
 * <br>
 * Copyright (c) 2020-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
 * distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
 */
public class Rectangle implements Serializable
{
    /** ... */
    private static final long serialVersionUID = 20200904L;

    /** Left boundary (inclusive). */
    private final double left;

    /** Bottom boundary (inclusive). */
    private final double bottom;

    /** Right boundary (not inclusive). */
    private final double right;

    /** Top boundary (not inclusive). */
    private final double top;

    /**
     * Construct a new Rectangle; all arguments are checked for having sensible values.
     * @param left double; the left boundary (inclusive)
     * @param bottom double; the bottom boundary (inclusive)
     * @param right double; the right boundary (not inclusive)
     * @param top double; the top boundary (not inclusive)
     * @param check boolean; if true; the values are checked for making sense
     */
    public Rectangle(final double left, final double bottom, final double right, final double top, final boolean check)
    {
        this(left, bottom, right, top);
        if (check)
        {
            Throw.when(Double.isNaN(left), IllegalArgumentException.class, "The value of left may not be NaN");
            Throw.when(Double.isNaN(bottom), IllegalArgumentException.class, "The value of bottom may not be NaN");
            Throw.when(Double.isNaN(right), IllegalArgumentException.class, "The value of right may not be NaN");
            Throw.when(Double.isNaN(top), IllegalArgumentException.class, "The value of top may not be NaN");
            Throw.when(left > right, IllegalArgumentException.class, "The value of left may not exceed the value of right");
            Throw.when(bottom > top, IllegalArgumentException.class, "The value of bottom may not exceed the value of top");
        }
    }

    /**
     * Construct a new Rectangle without checking the arguments for making sense.
     * @param left double; the left boundary (inclusive)
     * @param bottom double; the bottom boundary (inclusive)
     * @param right double; the right boundary (not inclusive)
     * @param top double; the top boundary (not inclusive)
     */
    public Rectangle(final double left, final double bottom, final double right, final double top)
    {
        this.left = left;
        this.bottom = bottom;
        this.right = right;
        this.top = top;
    }

    /**
     * Retrieve the left boundary value.
     * @return double; the left boundary value
     */
    public double getLeft()
    {
        return this.left;
    }

    /**
     * Retrieve the bottom boundary value.
     * @return double; the bottom boundary value
     */
    public double getBottom()
    {
        return this.bottom;
    }

    /**
     * Retrieve the right boundary value.
     * @return double; the right boundary value
     */
    public double getRight()
    {
        return this.right;
    }

    /**
     * Retrieve the top boundary value.
     * @return double; the top boundary value
     */
    public double getTop()
    {
        return this.top;
    }

    /**
     * Return the width of this Rectangle.
     * @return double; the width of this Rectangle
     */
    public double getWidth()
    {
        return this.right - this.left;
    }

    /**
     * Return the height of this Rectangle.
     * @return double; the height of this Rectangle
     */
    public double getHeight()
    {
        return this.top - this.bottom;
    }

    /**
     * Determine if this Rectangle intersects another Rectangle.
     * @param other Rectangle; the other rectangle
     * @return boolean; true if the rectangles intersect, false if the rectangles do not intersect
     */
    public boolean intersects(final Rectangle other)
    {
        return this.left < other.right && this.bottom < other.top && other.left < this.right && other.bottom < this.top;
    }

    /**
     * Determine if this Rectangle contains a point.
     * @param x double; x-coordinate of the point
     * @param y double; y-coordinate of the point
     * @return boolean; true if this Rectangle contains the point; false if this Rectangle does not contain the point
     */
    public boolean contains(final double x, final double y)
    {
        return (this.left <= x && x < this.right && this.bottom <= y && y < this.top);
    }

    /**
     * Determine if this Rectangle contains all points of another Rectangle.
     * @param other Rectangle; the other rectangle
     * @return boolean; true if this Rectangle contains all points of the other Rectangle; false if this Rectangle does not
     *         contain all points of the other Rectangle
     */
    public boolean contains(final Rectangle other)
    {
        return other.left >= this.left && other.right <= this.right && other.bottom >= this.bottom && other.top <= this.top;
    }

    @Override
    public String toString()
    {
        return "Rectangle [LB=" + this.left + "," + this.bottom + ", RT=" + this.right + "," + this.top + "]";
    }

}