Bounds.java
- package swingtree.layout;
- import com.google.errorprone.annotations.Immutable;
- import java.awt.Rectangle;
- import java.util.Objects;
- /**
- * An immutable value object that represents the position and size of a component
- * in the form of an x and y coordinate modeled by a {@link Position} object
- * and a width and height modeled by a {@link Size} object.
- * Note the rectangular bounds object is positioned in a coordinate system
- * where the y-axis is growing positively downwards and the x-axis is growing
- * positively to the right. <br>
- * The bounds object may also be incomplete in the sense that the width and height
- * may not be defined, in which case the {@link Size#unknown()} object is used.
- * A bounds object with an unknown size located at the origin is considered
- * the null object for this class and can be accessed using the {@link #none()} method.
- * You may use this object instead of {@code null} to represent a missing bounds object.
- * <p>
- * Also note that the {@link #equals(Object)} and {@link #hashCode()} methods
- * are implemented to compare the {@link Position} and {@link Size} objects
- * for value based equality instead of reference based equality.
- */
- @Immutable
- public final class Bounds
- {
- private final static Bounds EMPTY = new Bounds(Position.origin(), Size.unknown());
- /**
- * Returns an empty bounds object, which is the null object for this class.
- * <p>
- * The returned bounds object has a location of {@link Position#origin()}
- * and a size of {@link Size#unknown()}.
- *
- * @return an empty bounds object that is the null object for this class.
- */
- public static Bounds none() {
- return EMPTY;
- }
- private final Position _position;
- private final Size _size;
- /**
- * Returns a bounds object with the specified location and size.
- * <p>
- * If the location is {@link Position#origin()} and the size is
- * {@link Size#unknown()} then the {@link #none()} object is returned.
- *
- * @param position the location of the bounds object.
- * @param size the size of the bounds object.
- * @return a bounds object with the specified location and size.
- */
- public static Bounds of(Position position, Size size ) {
- Objects.requireNonNull(position);
- Objects.requireNonNull(size);
- if ( position.equals(Position.origin()) && size.equals(Size.unknown()) )
- return EMPTY;
- return new Bounds(position, size);
- }
- /**
- * Returns a bounds object with the specified location and size
- * in the form of x and y coordinates, width and height.
- * <p>
- * If the width or height is less than zero then the {@link #none()}
- * object is returned.
- *
- * @param x the x coordinate of the location of the bounds object.
- * @param y the y coordinate of the location of the bounds object.
- * @param width the width of the bounds object.
- * @param height the height of the bounds object.
- * @return a bounds object with the specified location and size
- * in the form of x and y coordinates, width and height.
- */
- public static Bounds of( int x, int y, int width, int height ) {
- if ( width < 0 || height < 0 )
- return EMPTY;
- return new Bounds(Position.of(x, y), Size.of(width, height));
- }
- /**
- * Returns a bounds object with the specified location and size
- * in the form of x and y coordinates, width and height.
- * <p>
- * If the width or height is less than zero then the {@link #none()}
- * object is returned.
- *
- * @param x the x coordinate of the location of the bounds object.
- * @param y the y coordinate of the location of the bounds object.
- * @param width the width of the bounds object.
- * @param height the height of the bounds object.
- * @return a bounds object with the specified location and size
- * in the form of x and y coordinates, width and height.
- */
- public static Bounds of( float x, float y, float width, float height ) {
- if ( width < 0 || height < 0 )
- return EMPTY;
- return new Bounds(Position.of(x, y), Size.of(width, height));
- }
- /**
- * Creates a bounds object from an AWT {@link Rectangle} object.
- * <p>
- * If the width or height is less than zero then the {@link #none()}
- * object is returned.
- *
- * @param rectangle an AWT rectangle object to create a bounds object from.
- * @return a bounds object with the location and size of the AWT rectangle object.
- */
- public static Bounds of( java.awt.Rectangle rectangle ) {
- return of(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
- }
- private Bounds(Position position, Size size ) {
- _position = Objects.requireNonNull(position);
- _size = Objects.requireNonNull(size);
- }
- /**
- * If you think of the bounds object as a rectangle, then the
- * {@link Position} defines the top left corner and the {@link Size}
- * defines the width and height of the rectangle.
- * Note that the y-axis is growing positively downwards and the x-axis
- * is growing positively to the right.
- *
- * @return The {@link Position} of this bounds object,
- * which contains the x and y coordinates.
- */
- public Position location() {
- return _position;
- }
- /**
- * Allows you to check weather the bounds object has a width
- * that is greater than zero.
- *
- * @return The truth value of whether this bounds object has a width,
- * which is true if the width is greater than zero.
- */
- public boolean hasWidth() { return _size._width > 0; }
- /**
- * Allows you to check weather the bounds object has a height
- * that is greater than zero.
- *
- * @return The truth value of whether this bounds object has a height,
- * which is true if the height is greater than zero.
- */
- public boolean hasHeight() {
- return _size._height > 0;
- }
- /**
- * The {@link Size} of define the width and height of the bounds
- * starting from the x and y coordinates of the {@link Position}.
- * Note that the {@link Position} is always the top left corner
- * of the bounds object where the y-axis is growing positively downwards
- * and the x-axis is growing positively to the right.
- *
- * @return The {@link Size} of this bounds object,
- * which contains the width and height.
- */
- public Size size() {
- return _size;
- }
- /**
- * Allows you to create an updated version of this bounds object with the
- * specified x-coordinate and the same y-coordinate and size as this bounds instance.
- * See also {@link #withY(int)}, {@link #withWidth(int)}, and {@link #withHeight(int)}.
- *
- * @param x A new x coordinate for the location of this bounds object.
- * @return A new bounds object with a new location that has the specified x coordinate.
- */
- public Bounds withX( int x ) {
- return new Bounds(_position.withX(x), _size);
- }
- /**
- * Allows you to create an updated version of this bounds object with the
- * specified y-coordinate and the same x-coordinate and size as this bounds instance.
- * See also {@link #withX(int)}, {@link #withWidth(int)}, and {@link #withHeight(int)}.
- *
- * @param y A new y coordinate for the location of this bounds object.
- * @return A new bounds object with a new location that has the specified y coordinate.
- */
- public Bounds withY( int y ) {
- return new Bounds(_position.withY(y), _size);
- }
- /**
- * Allows you to create an updated version of this bounds object with the
- * specified width and the same x and y coordinates as well as height as this bounds instance.
- * See also {@link #withX(int)}, {@link #withY(int)}, and {@link #withHeight(int)}, and {@link #withSize(int, int)}.
- *
- * @param width A new width for the size of this bounds object.
- * @return A new bounds object with a new size that has the specified width.
- */
- public Bounds withWidth( int width ) {
- return new Bounds(_position, _size.withWidth(width));
- }
- /**
- * Allows you to create an updated version of this bounds object with the
- * specified height and the same x and y coordinates as well as width as this bounds instance.
- * See also {@link #withX(int)}, {@link #withY(int)}, and {@link #withWidth(int)}, and {@link #withSize(int, int)}.
- *
- * @param height A new height for the size of this bounds object.
- * @return A new bounds object with a new size that has the specified height.
- */
- public Bounds withHeight( int height ) {
- return new Bounds(_position, _size.withHeight(height));
- }
- /**
- * Allows you to create an updated version of this bounds object with the
- * specified width and height and the same x and y coordinates as this bounds instance.
- * See also {@link #withX(int)}, {@link #withY(int)}, {@link #withWidth(int)}, and {@link #withHeight(int)}.
- *
- * @param width A new width for the size of this bounds object.
- * @param height A new height for the size of this bounds object.
- * @return A new bounds object with a new size that has the specified width and height.
- */
- public Bounds withSize( int width, int height ) {
- return new Bounds(_position, Size.of(width, height));
- }
- /**
- * Creates a new bounds object which tightly fits around this bounds object
- * and the specified bounds object, effectively merging the two bounds objects.
- * This is done by finding the minimum x and y coordinates and
- * the maximum width and height of the two bounds objects.
- *
- * @param other The bounds object to merge with this bounds object.
- * @return A new bounds object that tightly fits around this bounds object and the specified bounds object.
- */
- public Bounds merge( Bounds other ) {
- Objects.requireNonNull(other);
- if ( this.equals(other) )
- return this;
- final float thisLeft = _position.x();
- final float thisTop = _position.y();
- final float thisRight = thisLeft + _size._width;
- final float thisBottom = thisTop + _size._height;
- final float otherLeft = other._position.x();
- final float otherTop = other._position.y();
- final float otherRight = otherLeft + other._size._width;
- final float otherBottom = otherTop + other._size._height;
- final float left = Math.min( thisLeft, otherLeft );
- final float top = Math.min( thisTop, otherTop );
- final float right = Math.max( thisRight, otherRight );
- final float bottom = Math.max( thisBottom, otherBottom );
- return Bounds.of(left, top, right - left, bottom - top);
- }
- /**
- * Allows you to check weather the bounds object has the specified
- * width and height, which is true if the width and height are equal
- * to the width and height of the {@link Size} of this bounds object
- * (see {@link #size()}).
- *
- * @param width A new width for the size of this bounds object.
- * @param height A new height for the size of this bounds object.
- * @return A new bounds object with a new size that has the specified width and height.
- */
- public boolean hasSize( int width, int height ) {
- return _size._width == width && _size._height == height;
- }
- /**
- * Allows you to check weather the bounds object has the specified
- * width, which is true if the width is equal to the width of the
- * {@link Size} of this bounds object (see {@link #size()}).
- *
- * @param width An integer value to compare to the width of this bounds object.
- * @return The truth value of whether the specified width is equal to the width of this bounds object.
- */
- public boolean hasWidth( int width ) {
- return _size._width == width;
- }
- /**
- * Allows you to check weather the bounds object has the specified
- * height, which is true if the height is equal to the height of the
- * {@link Size} of this bounds object (see {@link #size()}).
- *
- * @param height An integer value to compare to the height of this bounds object.
- * @return The truth value of whether the specified height is equal to the height of this bounds object.
- */
- public boolean hasHeight( int height ) {
- return _size._height == height;
- }
- /**
- * The bounds object has a location and size which form a rectangular area
- * exposed by this method as a float value defined by the width multiplied by the height.
- *
- * @return The area of this bounds object, which is the width multiplied by the height.
- */
- public float area() {
- return _size._width * _size._height;
- }
- /**
- * The bounds object has a location and size which form a rectangular area
- * which can easily be converted to a {@link Rectangle} object using this method.
- *
- * @return A {@link Rectangle} object with the same location and size as this bounds object.
- */
- public Rectangle toRectangle() {
- return new Rectangle((int) _position.x(), (int) _position.y(), (int) _size._width, (int) _size._height);
- }
- @Override
- public String toString() {
- return this.getClass().getSimpleName()+"[" +
- "location=" + _position + ", "+
- "size=" + _size +
- "]";
- }
- /**
- * A convent method to check if the specified x and y coordinates and width and height
- * are equal to the location and size of this bounds object.
- * This is equivalent to calling {@link #equals(Object)} with
- * a new bounds object created with the specified x and y coordinates and width and height
- * like so: {@code equals(Bounds.of(x, y, width, height))}.
- *
- * @param x An integer value to compare to the x coordinate of the location of this bounds object.
- * @param y An integer value to compare to the y coordinate of the location of this bounds object.
- * @param width An integer value to compare to the width of this bounds object.
- * @param height An integer value to compare to the height of this bounds object.
- * @return The truth value of whether the specified x and y coordinates and width and height
- * are equal to the location and size of this bounds object.
- */
- public boolean equals( int x, int y, int width, int height ) {
- return _position.x() == x && _position.y() == y && _size._width == width && _size._height == height;
- }
- @Override
- public boolean equals( Object o ) {
- if ( o == this ) return true;
- if ( o == null ) return false;
- if ( o.getClass() != this.getClass() ) return false;
- Bounds that = (Bounds)o;
- return Objects.equals(this._position, that._position) &&
- Objects.equals(this._size, that._size);
- }
- @Override
- public int hashCode() {
- return Objects.hash(_position, _size);
- }
- }