Mercurial > projects > dwt-addons
diff dwtx/draw2d/geometry/Rectangle.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children | 1082a0fc2bb8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Rectangle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,977 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.geometry.Rectangle; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.PositionConstants; +import tango.text.convert.Format; + +static import dwt.graphics.Rectangle; + +/** + * Represents a Rectangle(x, y, width, height). This class provides various methods + * for manipulating this Rectangle or creating new derived geometrical Objects. + */ +public class Rectangle + : Cloneable, /+java.io.Serializable, +/Translatable +{ +/** the X value */ +public int x; +/** the Y value */ +public int y; +/** the width*/ +public int width; +/** the height */ +public int height; + +/**A singleton for use in short calculations. Use to avoid newing unnecessary objects.*/ +public static const Rectangle SINGLETON; + +static this(){ + SINGLETON = new Rectangle(); +} + +static final long serialVersionUID = 1; + +/** + * Constructs a Rectangle at the origin with zero width and height. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a Rectangle given a location and size. + * + * @param p the location + * @param size the size + * @since 2.0 + */ +public this(Point p, Dimension size) { + this(p.x, p.y, size.width, size.height); +} + +/** + * Constructs a copy of the provided Rectangle. + * + * @param rect Rectangle supplying the initial values + * @since 2.0 + */ +public this(Rectangle rect) { + this(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Constructs a copy of the provided DWT {@link dwt.graphics.Rectangle}. + * + * @param rect The DWT Rectangle being copied + * @since 2.0 + */ +public this(dwt.graphics.Rectangle.Rectangle rect) { + this(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Constructs a Rectangle with the provided values. + * + * @param x X location + * @param y Y location + * @param width Width of the rectangle + * @param height Height of the rectangle + * @since 2.0 + */ +public this(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; +} + +/** + * Constructs the smallest Rectangle that contains the specified Points. + * + * @param p1 Upper left hand corner + * @param p2 Lower right hand corner + * @since 2.0 + */ +public this(Point p1, Point p2) { + this.x = Math.min(p1.x, p2.x); + this.y = Math.min(p1.y, p2.y); + this.width = Math.abs(p2.x - p1.x) + 1; + this.height = Math.abs(p2.y - p1.y) + 1; +} + +/** + * Returns the y-coordinate of the bottom of this Rectangle. + * + * @return The Y coordinate of the bottom + * @since 2.0 + */ +public int bottom() { + return y + height; +} + +/** + * Returns whether the given point is within the boundaries of this Rectangle. + * The boundaries are inclusive of the top and left edges, but exclusive of the + * bottom and right edges. + * + * @param pt Point being tested for containment + * @return true if the Point is within this Rectangle + * @since 2.0 + */ +public bool contains(Point pt) { + return contains(pt.x, pt.y); +} + +/** + * Returns <code>true</code> if the given rectangle is contained within the + * boundaries of this Rectangle. + * + * @param rect the Rectangle to test + * @return true if the Rectangle is within this Rectangle + */ +public bool contains(Rectangle rect) { + return x <= rect.x + && y <= rect.y + && right() >= rect.right() + && bottom() >= rect.bottom(); +} + +/** + * Returns whether the given coordinates are within the boundaries of this + * Rectangle. The boundaries are inclusive of the top and left edges, but + * exclusive of the bottom and right edges. + * + * @param x X value + * @param y Y value + * @return true if the coordinates are within this Rectangle + * @since 2.0 + */ +public bool contains(int x, int y) { + return y >= this.y + && y < this.y + this.height + && x >= this.x + && x < this.x + this.width; +} + +/** + * Crops this rectangle by the amount specified in <code>insets</code>. + * + * @param insets Insets to be removed from the Rectangle + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle crop(Insets insets) { + if (insets is null) + return this; + x += insets.left; + y += insets.top; + width -= (insets.getWidth()); + height -= (insets.getHeight()); + return this; +} + +/** + * Returns whether the input object is equal to this Rectangle or not. + * Rectangles are equivalent if their x, y, height, and width values are the + * same. + * + * @param o Object being tested for equality + * @return Returns the result of the equality test + * @since 2.0 + */ +public override int opEquals(Object o) { + if (this is o) return true; + if (auto r = cast(Rectangle)o ) { + return (x is r.x) + && (y is r.y) + && (width is r.width) + && (height is r.height); + } + return false; +} + +/** + * Expands the horizontal and vertical sides of this Rectangle with the values + * provided as input, and returns this for convenience. The location of its + * center is kept constant. + * + * @param h Horizontal increment + * @param v Vertical increment + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle expand(int h, int v) { + return shrink(-h, -v); +} + +/** + * Expands the horizontal and vertical sides of this Rectangle by the width and + * height of the given Insets, and returns this for convenience. + * + * @param insets contains the amounts to expand on each side + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle expand(Insets insets) { + x -= insets.left; + y -= insets.top; + height += insets.getHeight(); + width += insets.getWidth(); + return this; +} + +/** + * Returns a new Point representing the middle point of the bottom side of this + * Rectangle. + * + * @return Point at the bottom of the Rectangle + * @since 2.0 + */ +public Point getBottom() { + return new Point(x + width / 2, bottom()); +} + +/** + * Returns a new Point representing the bottom left point of this Rectangle. + * + * @return Point at the bottom left of the rectangle + * @since 2.0 + */ +public Point getBottomLeft() { + return new Point(x, y + height); +} + +/** + * Returns a new Point representing the bottom right point of this Rectangle. + * + * @return Point at the bottom right of the rectangle + * @since 2.0 + */ +public Point getBottomRight() { + return new Point(x + width, y + height); +} + +/** + * Returns a new point representing the center of this Rectangle. + * + * @return Point at the center of the rectangle + */ +public Point getCenter() { + return new Point(x + width / 2, y + height / 2); +} + +/** + * Returns a new Rectangle which has the exact same parameters as this + * Rectangle. + * + * @return Copy of this Rectangle + * @since 2.0 + */ +public Rectangle getCopy() { +// try { +// return cast(Rectangle)clone(); +// } catch (CloneNotSupportedException exc) { + return new Rectangle(this); +// } +} + +/** + * Returns a new Rectangle with the specified insets cropped. + * + * @param insets Insets being cropped from the Rectangle + * @return Cropped new Rectangle + */ +public Rectangle getCropped(Insets insets) { + Rectangle r = new Rectangle(this); + r.crop(insets); + return r; +} + +/** + * Returns a new incremented Rectangle, where the sides are expanded by the + * horizonatal and vertical values provided. The center of the Rectangle is + * maintained constant. + * + * @param h Horizontal increment + * @param v Vertical inrement + * @return A new expanded Rectangle + * @since 2.0 + */ +public Rectangle getExpanded(int h, int v) { + return (new Rectangle(this)).expand(h, v); +} + +/** + * Creates and returns a new Rectangle with the bounds of <code>this</code> + * Rectangle, expanded by the given Insets. + * + * @param insets The insets used to expand this rectangle + * @return A new expanded Rectangle + * @since 2.0 + */ +public Rectangle getExpanded(Insets insets) { + return (new Rectangle(this)).expand(insets); +} + + +/** + * Returns a new Rectangle which has the intersection of this Rectangle and the + * rectangle provided as input. Returns an empty Rectangle if there is no + * interection. + * + * @param rect Rectangle provided to test for intersection + * @return A new Rectangle representing the intersection + * @since 2.0 + */ +public Rectangle getIntersection(Rectangle rect) { + int x1 = Math.max(x, rect.x); + int x2 = Math.min(x + width, rect.x + rect.width); + int y1 = Math.max(y, rect.y); + int y2 = Math.min(y + height, rect.y + rect.height); + if (((x2 - x1) < 0) || ((y2 - y1) < 0)) + return new Rectangle(0, 0, 0, 0); // No intersection + else + return new Rectangle(x1, y1, x2 - x1, y2 - y1); +} + +/** + * Returns a new Point representing the middle point of the left hand side of + * this Rectangle. + * + * @return Point at the left of the Rectangle + */ +public Point getLeft() { + return new Point(x, y + height / 2); +} + +/** + * Returns the upper left hand corner of the rectangle. + * + * @return Location of the rectangle + * @see #setLocation(Point) + */ +public Point getLocation() { + return new Point(x, y); +} + +/** + * <P>Returns an integer which represents the position of the given point with respect to + * this rectangle. Possible return values are bitwise ORs of the constants WEST, EAST, + * NORTH, and SOUTH as found in {@link dwtx.draw2d.PositionConstants}. + * + * <P>Returns PositionConstant.NONE if the given point is inside this Rectangle. + * + * @param pt The Point whose position has to be determined + * @return An <code>int</code> which is a PositionConstant + * @see dwtx.draw2d.PositionConstants + * @since 2.0 + */ +public int getPosition(Point pt) { + int result = PositionConstants.NONE; + + if (contains(pt)) + return result; + + if (pt.x < x) + result = PositionConstants.WEST; + else if (pt.x >= (x + width)) + result = PositionConstants.EAST; + + if (pt.y < y) + result = result | PositionConstants.NORTH; + else if (pt.y >= (y + height)) + result = result | PositionConstants.SOUTH; + + return result; +} + +/** + * Returns a new Rectangle which is equivalent to this Rectangle with its + * dimensions modified by the passed width <i>w</i> and height <i>h</i>. + + * @param w Amount by which width is to be resized + * @param h Amount by which height is to be resized + * @return a new rectangle with its width and height modified + */ +public Rectangle getResized(int w, int h) { + return (new Rectangle(this)).resize(w, h); +} + +/** + * Returns a new Rectangle which is equivalent to this Rectangle with its + * dimensions modified by the passed Dimension <i>d</i>. + * + * @param d Dimensions by which the rectangle's size should be modified + * @return The new rectangle with the modified dimensions + * @since 2.0 + */ +public Rectangle getResized(Dimension d) { + return (new Rectangle(this)).resize(d); +} + +/** + * Returns a new Point which represents the middle point of the right hand side + * of this Rectangle. + * + * @return Point at the right of the Rectangle + * @since 2.0 + */ +public Point getRight() { + return new Point(right(), y + height / 2); +} + +/** + * Retuns the dimensions of this Rectangle. + * + * @return Size of this Rectangle as a Dimension + * @since 2.0 + */ +public Dimension getSize() { + return new Dimension(width, height); +} + +/** + * Returns a new Point which represents the middle point of the top side of this + * Rectangle. + * + * @return Point at the top of the Rectangle + * @since 2.0 + */ +public Point getTop() { + return new Point(x + width / 2, y); +} + +/** + * Returns a new Point which represents the top left hand corner of this + * Rectangle. + * + * @return Point at the top left of the rectangle + * @since 2.0 + */ +public Point getTopLeft() { + return new Point(x, y); +} + +/** + * Returns a new Point which represents the top right hand corner of this + * Rectangle. + * + * @return Point at the top right of the rectangle + * @since 2.0 + */ +public Point getTopRight() { + return new Point(x + width, y); +} + +/** + * Returns a new Rectangle which is shifted along each axis by the passed + * values. + * + * @param dx Displacement along X axis + * @param dy Displacement along Y axis + * @return The new translated rectangle + * @since 2.0 + */ +public Rectangle getTranslated(int dx, int dy) { + return (new Rectangle(this)).translate(dx, dy); +} + +/** + * Returns a new Rectangle which is shifted by the position of the given Point. + * + * @param pt Point providing the amount of shift along each axis + * @return The new translated Rectangle + * @since 2.0 + */ +public Rectangle getTranslated(Point pt) { + return (new Rectangle(this)).translate(pt); +} + +/** + * Returns a new rectangle whose width and height have been interchanged, as well + * as its x and y values. This can be useful in orientation changes. + * + * @return The transposed rectangle + * @since 2.0 + */ +public Rectangle getTransposed() { + Rectangle r = new Rectangle(this); + r.transpose(); + return r; +} + +/** + * Returns a new Rectangle which contains both this Rectangle and the Rectangle + * supplied as input. + * + * @param rect Rectangle for calculating union_ + * @return A new unioned Rectangle + * @since 2.0 + */ +public Rectangle getUnion(Rectangle rect) { + if (rect is null || rect.isEmpty()) + return new Rectangle(this); + Rectangle union_ = new Rectangle( + Math.min(x, rect.x), + Math.min(y, rect.y), 0, 0); + union_.width = Math.max(x + width, rect.x + rect.width) - union_.x; + union_.height = Math.max(y + height, rect.y + rect.height) - union_.y; + return union_; +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return (x + height + 1) * (y + width + 1) ^ x ^ y; +} + +/** + * Sets the size of this Rectangle to the intersection region with the Rectangle + * supplied as input, and returns this for convenience. The location and + * dimensions are set to zero if there is no intersection with the input + * Rectangle. + * + * @param rect Rectangle for the calculating intersection. + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle intersect(Rectangle rect) { + int x1 = Math.max(x, rect.x); + int x2 = Math.min(x + width, rect.x + rect.width); + int y1 = Math.max(y, rect.y); + int y2 = Math.min(y + height, rect.y + rect.height); + if (((x2 - x1) < 0) || ((y2 - y1) < 0)) + x = y = width = height = 0; // No intersection + else { + x = x1; + y = y1; + width = x2 - x1; + height = y2 - y1; + } + return this; +} + +/** + * Returns <code>true</code> if the input Rectangle intersects this Rectangle. + * + * @param rect Rectangle for the intersetion test + * @return <code>true</code> if the input Rectangle intersects this Rectangle + * @since 2.0 + */ +public bool intersects(Rectangle rect) { + return rect.x < x + width + && rect.y < y + height + && rect.x + rect.width > x + && rect.y + rect.height > y; +} + +/** + * Returns <code>true</code> if this Rectangle's width or height is less than or + * equal to 0. + * + * @return <code>true</code> if this Rectangle is empty + * @since 2.0 + */ +public bool isEmpty() { + return width <= 0 || height <= 0; +} + +/** + * @see Translatable#performScale(double) + */ +public void performScale(double factor) { + scale(factor); +} + +/** + * @see Translatable#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + translate(dx, dy); +} + +/** + * Resizes this Rectangle by the Dimension provided as input and returns this + * for convenience. This Rectange's width will become this.width + sizeDelta.width. + * Likewise for height. + * + * @param sizeDelta Resize data as a Dimension + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle resize(Dimension sizeDelta) { + width += sizeDelta.width; + height += sizeDelta.height; + return this; +} + +/** + * Resizes this Rectangle by the values supplied as input and returns this for + * convenience. This Rectangle's width will become this.width + dw. This + * Rectangle's height will become this.height + dh. + * + * @param dw Amount by which width is to be resized + * @param dh Amount by which height is to be resized + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle resize(int dw, int dh) { + width += dw; + height += dh; + return this; +} + +/** + * Returns the x-coordinate of the right side of this Rectangle. + * + * @return The X coordinate of the right side + * @since 2.0 + */ +public int right() { + return x + width; +} + +/** + * Scales the location and size of this Rectangle by the given scale and returns + * this for convenience. + * + * @param scaleFactor The factor by which this rectangle will be scaled + * @return <code>this</code> for convenience + * @since 2.0 + */ +public final Rectangle scale(double scaleFactor) { + return scale(scaleFactor, scaleFactor); +} + +/** + * Scales the location and size of this Rectangle by the given scales and returns + * this for convenience. + * + * @param scaleX the factor by which the X dimension has to be scaled + * @param scaleY the factor by which the Y dimension has to be scaled + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle scale(double scaleX, double scaleY) { + int oldX = x; + int oldY = y; + x = cast(int)(Math.floor(x * scaleX)); + y = cast(int)(Math.floor(y * scaleY)); + width = cast(int)(Math.ceil((oldX + width) * scaleX)) - x; + height = cast(int)(Math.ceil((oldY + height) * scaleY)) - y; + return this; +} + +/** + * Sets the parameters of this Rectangle from the Rectangle passed in and + * returns this for convenience. + * + * @return <code>this</code> for convenience + * @param rect Rectangle providing the bounding values + * @since 2.0 + */ +public Rectangle setBounds(Rectangle rect) { + x = rect.x; + y = rect.y; + width = rect.width; + height = rect.height; + return this; +} + +/** + * Sets the location of this Rectangle to the point given as input and returns + * this for convenience. + * + * @return <code>this</code> for convenience + * @param p New position of this Rectangle + * @since 2.0 + */ +public Rectangle setLocation(Point p) { + x = p.x; + y = p.y; + return this; +} + +/** + * Sets the location of this Rectangle to the coordinates given as input and + * returns this for convenience. + * + * @param x1 The new X coordinate + * @param y1 The new Y coordinate + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle setLocation(int x1, int y1) { + x = x1; + y = y1; + return this; +} + +/** + * Sets the width and height of this Rectangle to the width and height of the + * given Dimension and returns this for convenience. + * + * @param d The new Dimension + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle setSize(Dimension d) { + width = d.width; + height = d.height; + return this; +} + +/** + * Sets the width of this Rectangle to <i>w</i> and the height of this + * Rectangle to <i>h</i> and returns this for convenience. + * + * @return <code>this</code> for convenience + * @param w The new width + * @param h The new height + * @since 2.0 + */ +public Rectangle setSize(int w, int h) { + width = w; + height = h; + return this; +} + +/** + * Shrinks the sides of this Rectangle by the horizontal and vertical values + * provided as input, and returns this Rectangle for convenience. The center of + * this Rectangle is kept constant. + * + * @param h Horizontal reduction amount + * @param v Vertical reduction amount + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle shrink(int h, int v) { + x += h; + width -= (h + h); + y += v; + height -= (v + v); + return this; +} + +/** + * Returns the description of this Rectangle. + * + * @return String containing the description + * @since 2.0 + */ +public override String toString() { + return Format("Rectangle({}, {}, {}, {})", x, y, width, height ); +} + +/** + * Returns <code>true</code> if the input Rectangle touches this Rectangle. + * + * @param rect Rectangle being checked for contact + * @return <code>true</code> if rect touches this Rectangle + * @since 2.0 + */ +public bool touches(Rectangle rect) { + return rect.x <= x + width + && rect.y <= y + height + && rect.x + rect.width >= x + && rect.y + rect.height >= y; +} + +/** + * Moves this Rectangle horizontally by the x value of the given Point and + * vertically by the y value of the given Point, then returns this Rectangle for + * convenience. + * + * @param p Point which provides translation information + * @return <code>this</code> for convenience + */ +public Rectangle translate(Point p) { + x += p.x; + y += p.y; + return this; +} + +/** + * Moves this Rectangle horizontally by dx and vertically by dy, then returns + * this Rectangle for convenience. + * + * @param dx Shift along X axis + * @param dy Shift along Y axis + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle translate(int dx, int dy) { + x += dx; + y += dy; + return this; +} + +/** + * Switches the x and y values, as well as the width and height of this Rectangle. + * Useful for orientation changes. + * + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle transpose() { + int temp = x; + x = y; + y = temp; + temp = width; + width = height; + height = temp; + return this; +} + +/** + * Unions this Rectangle's width and height with the specified Dimension. + * + * @param d Dimension being unioned + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle union_(Dimension d) { + width = Math.max(width, d.width); + height = Math.max(height, d.height); + return this; +} + +/** + * Updates this Rectangle's bounds to the minimum size which can hold both this + * Rectangle and the coordinate (x,y). + * + * @return <code>this</code> for convenience + * @param x1 X coordinate + * @param y1 Y coordinate + * @since 2.0 + */ +public Rectangle union_(int x1, int y1) { + if (x1 < x) { + width += (x - x1); + x = x1; + } else { + int right = x + width; + if (x1 >= right) { + right = x1 + 1; + width = right - x; + } + } + if (y1 < y) { + height += (y - y1); + y = y1; + } else { + int bottom = y + height; + if (y1 >= bottom) { + bottom = y1 + 1; + height = bottom - y; + } + } + return this; +} + +/** + * Updates this Rectangle's bounds to the minimum size which can hold both this + * Rectangle and the given Point. + * + * @param p Point to be unioned with this Rectangle + * @since 2.0 + */ +public void union_(Point p) { + union_(p.x, p.y); +} + +/** + * Updates this Rectangle's dimensions to the minimum size which can hold both + * this Rectangle and the given Rectangle. + * + * @return <code>this</code> for convenience + * @param rect Rectangle to be unioned with this Rectangle + * @since 2.0 + */ +public Rectangle union_(Rectangle rect) { + if (rect is null) + return this; + return union_(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Updates this Rectangle's dimensions to the minimum size which can hold both + * this Rectangle and the rectangle (x, y, w, h). + * + * @param x X coordiante of desired union_. + * @param y Y coordiante of desired union_. + * @param w Width of desired union_. + * @param h Height of desired union_. + * @return <code>this</code> for convenience + * @since 2.0 + */ +public Rectangle union_(int x, int y, int w, int h) { + int right = Math.max(this.x + width, x + w); + int bottom = Math.max(this.y + height, y + h); + this.x = Math.min(this.x, x); + this.y = Math.min(this.y, y); + this.width = right - this.x; + this.height = bottom - this.y; + return this; +} + +/** + * Returns <code>double</code> x coordinate + * + * @return <code>double</code> x coordinate + * @since 3.4 + */ +public double preciseX() { + return x; +} + +/** + * Returns <code>double</code> y coordinate + * + * @return <code>double</code> y coordinate + * @since 3.4 + */ +public double preciseY() { + return y; +} + +/** + * Returns <code>double</code> width + * + * @return <code>double</code> width + * @since 3.4 + */ +public double preciseWidth() { + return width; +} + +/** + * Returns <code>double</code> height + * + * @return <code>double</code> height + * @since 3.4 + */ +public double preciseHeight() { + return height; +} + +}