Mercurial > projects > dwt-addons
diff dwtx/jface/util/Geometry.d @ 4:c87617952847
some JFace modules
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Fri, 28 Mar 2008 17:08:33 +0100 |
parents | |
children | da5ad8eedf5d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/util/Geometry.d Fri Mar 28 17:08:33 2008 +0100 @@ -0,0 +1,812 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 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.jface.util.Geometry; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Integer; + +/** + * Contains static methods for performing simple geometric operations + * on the DWT geometry classes. + * + * @since 3.0 + */ +public class Geometry { + + /** + * Prevent this class from being instantiated. + * + * @since 3.0 + */ + private this() { + //This is not instantiated + } + + /** + * Returns the square of the distance between two points. + * <p>This is preferred over the real distance when searching + * for the closest point, since it avoids square roots.</p> + * + * @param p1 first endpoint + * @param p2 second endpoint + * @return the square of the distance between the two points + * + * @since 3.0 + */ + public static int distanceSquared(Point p1, Point p2) { + int term1 = p1.x - p2.x; + int term2 = p1.y - p2.y; + return term1 * term1 + term2 * term2; + } + + /** + * Returns the magnitude of the given 2d vector (represented as a Point) + * + * @param p point representing the 2d vector whose magnitude is being computed + * @return the magnitude of the given 2d vector + * @since 3.0 + */ + public static double magnitude(Point p) { + return Math.sqrt( cast(real) magnitudeSquared(p)); + } + + /** + * Returns the square of the magnitude of the given 2-space vector (represented + * using a point) + * + * @param p the point whose magnitude is being computed + * @return the square of the magnitude of the given vector + * @since 3.0 + */ + public static int magnitudeSquared(Point p) { + return p.x * p.x + p.y * p.y; + } + + /** + * Returns the dot product of the given vectors (expressed as Points) + * + * @param p1 the first vector + * @param p2 the second vector + * @return the dot product of the two vectors + * @since 3.0 + */ + public static int dotProduct(Point p1, Point p2) { + return p1.x * p2.x + p1.y * p2.y; + } + + /** + * Returns a new point whose coordinates are the minimum of the coordinates of the + * given points + * + * @param p1 a Point + * @param p2 a Point + * @return a new point whose coordinates are the minimum of the coordinates of the + * given points + * @since 3.0 + */ + public static Point min(Point p1, Point p2) { + return new Point(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y)); + } + + /** + * Returns a new point whose coordinates are the maximum of the coordinates + * of the given points + * @param p1 a Point + * @param p2 a Point + * @return point a new point whose coordinates are the maximum of the coordinates + * @since 3.0 + */ + public static Point max(Point p1, Point p2) { + return new Point(Math.max(p1.x, p2.x), Math.max(p1.y, p2.y)); + } + + /** + * Returns a vector in the given direction with the given + * magnitude. Directions are given using DWT direction constants, and + * the resulting vector is in the screen's coordinate system. That is, + * the vector (0, 1) is down and the vector (1, 0) is right. + * + * @param distance magnitude of the vector + * @param direction one of DWT.TOP, DWT.BOTTOM, DWT.LEFT, or DWT.RIGHT + * @return a point representing a vector in the given direction with the given magnitude + * @since 3.0 + */ + public static Point getDirectionVector(int distance, int direction) { + switch (direction) { + case DWT.TOP: + return new Point(0, -distance); + case DWT.BOTTOM: + return new Point(0, distance); + case DWT.LEFT: + return new Point(-distance, 0); + case DWT.RIGHT: + return new Point(distance, 0); + } + + return new Point(0, 0); + } + + /** + * Returns the point in the center of the given rectangle. + * + * @param rect rectangle being computed + * @return a Point at the center of the given rectangle. + * @since 3.0 + */ + public static Point centerPoint(Rectangle rect) { + return new Point(rect.x + rect.width / 2, rect.y + rect.height / 2); + } + + /** + * Returns a copy of the given point + * + * @param toCopy point to copy + * @return a copy of the given point + */ + public static Point copy(Point toCopy) { + return new Point(toCopy.x, toCopy.y); + } + + /** + * Sets result equal to toCopy + * + * @param result object that will be modified + * @param toCopy object that will be copied + * @since 3.1 + */ + public static void set(Point result, Point toCopy) { + result.x = toCopy.x; + result.y = toCopy.y; + } + + /** + * Sets result equal to toCopy + * + * @param result object that will be modified + * @param toCopy object that will be copied + * @since 3.1 + */ + public static void set(Rectangle result, Rectangle toCopy) { + result.x = toCopy.x; + result.y = toCopy.y; + result.width = toCopy.width; + result.height = toCopy.height; + } + + /** + * <p>Returns a new difference Rectangle whose x, y, width, and height are equal to the difference of the corresponding + * attributes from the given rectangles</p> + * + * <p></p> + * <b>Example: Compute the margins for a given Composite, and apply those same margins to a new GridLayout</b> + * + * <code><pre> + * // Compute the client area, in the coordinate system of the input composite's parent + * Rectangle clientArea = Display.getCurrent().map(inputComposite, + * inputComposite.getParent(), inputComposite.getClientArea()); + * + * // Compute the margins for a given Composite by subtracting the client area from the composite's bounds + * Rectangle margins = Geometry.subtract(inputComposite.getBounds(), clientArea); + * + * // Now apply these margins to a new GridLayout + * GridLayout layout = GridLayoutFactory.fillDefaults().margins(margins).create(); + * </pre></code> + * + * @param rect1 first rectangle + * @param rect2 rectangle to subtract + * @return the difference between the two rectangles (computed as rect1 - rect2) + * @since 3.3 + */ + public static Rectangle subtract(Rectangle rect1, Rectangle rect2) { + return new Rectangle(rect1.x - rect2.x, rect1.y - rect2.y, rect1.width - rect2.width, rect1.height - rect2.height); + } + + /** + * <p>Returns a new Rectangle whose x, y, width, and height is the sum of the x, y, width, and height values of + * both rectangles respectively.</p> + * + * @param rect1 first rectangle to add + * @param rect2 second rectangle to add + * @return a new rectangle whose x, y, height, and width attributes are the sum of the corresponding attributes from + * the arguments. + * @since 3.3 + */ + public static Rectangle add(Rectangle rect1, Rectangle rect2) { + return new Rectangle(rect1.x + rect2.x, rect1.y + rect2.y, + rect1.width + rect2.width, rect1.height + rect2.height); + } + + /** + * Adds two points as 2d vectors. Returns a new point whose coordinates are + * the sum of the original two points. + * + * @param point1 the first point (not null) + * @param point2 the second point (not null) + * @return a new point whose coordinates are the sum of the given points + * @since 3.0 + */ + public static Point add(Point point1, Point point2) { + return new Point(point1.x + point2.x, point1.y + point2.y); + } + + /** + * Divides both coordinates of the given point by the given scalar. + * + * @since 3.1 + * + * @param toDivide point to divide + * @param scalar denominator + * @return a new Point whose coordinates are equal to the original point divided by the scalar + */ + public static Point divide(Point toDivide, int scalar) { + return new Point(toDivide.x / scalar, toDivide.y / scalar); + } + + + /** + * Performs vector subtraction on two points. Returns a new point equal to + * (point1 - point2). + * + * @param point1 initial point + * @param point2 vector to subtract + * @return the difference (point1 - point2) + * @since 3.0 + */ + public static Point subtract(Point point1, Point point2) { + return new Point(point1.x - point2.x, point1.y - point2.y); + } + + /** + * Swaps the X and Y coordinates of the given point. + * + * @param toFlip modifies this point + * @since 3.1 + */ + public static void flipXY(Point toFlip) { + int temp = toFlip.x; + toFlip.x = toFlip.y; + toFlip.y = temp; + } + + /** + * Swaps the X and Y coordinates of the given rectangle, along with the height and width. + * + * @param toFlip modifies this rectangle + * @since 3.1 + */ + public static void flipXY(Rectangle toFlip) { + int temp = toFlip.x; + toFlip.x = toFlip.y; + toFlip.y = temp; + + temp = toFlip.width; + toFlip.width = toFlip.height; + toFlip.height = temp; + } + + /** + * Returns the height or width of the given rectangle. + * + * @param toMeasure rectangle to measure + * @param width returns the width if true, and the height if false + * @return the width or height of the given rectangle + * @since 3.0 + */ + public static int getDimension(Rectangle toMeasure, bool width) { + if (width) { + return toMeasure.width; + } + return toMeasure.height; + } + + /** + * Returns the x or y coordinates of the given point. + * + * @param toMeasure point being measured + * @param width if true, returns x. Otherwise, returns y. + * @return the x or y coordinate + * @since 3.1 + */ + public static int getCoordinate(Point toMeasure, bool width) { + return width ? toMeasure.x : toMeasure.y; + } + + /** + * Returns the x or y coordinates of the given rectangle. + * + * @param toMeasure rectangle being measured + * @param width if true, returns x. Otherwise, returns y. + * @return the x or y coordinate + * @since 3.1 + */ + public static int getCoordinate(Rectangle toMeasure, bool width) { + return width ? toMeasure.x : toMeasure.y; + } + + /** + * Sets one dimension of the given rectangle. Modifies the given rectangle. + * + * @param toSet rectangle to modify + * @param width if true, the width is modified. If false, the height is modified. + * @param newCoordinate new value of the width or height + * @since 3.1 + */ + public static void setDimension(Rectangle toSet, bool width, int newCoordinate) { + if (width) { + toSet.width = newCoordinate; + } else { + toSet.height = newCoordinate; + } + } + + /** + * Sets one coordinate of the given rectangle. Modifies the given rectangle. + * + * @param toSet rectangle to modify + * @param width if true, the x coordinate is modified. If false, the y coordinate is modified. + * @param newCoordinate new value of the x or y coordinates + * @since 3.1 + */ + public static void setCoordinate(Rectangle toSet, bool width, int newCoordinate) { + if (width) { + toSet.x = newCoordinate; + } else { + toSet.y = newCoordinate; + } + } + + /** + * Sets one coordinate of the given point. Modifies the given point. + * + * @param toSet point to modify + * @param width if true, the x coordinate is modified. If false, the y coordinate is modified. + * @param newCoordinate new value of the x or y coordinates + * @since 3.1 + */ + public static void setCoordinate(Point toSet, bool width, int newCoordinate) { + if (width) { + toSet.x = newCoordinate; + } else { + toSet.y = newCoordinate; + } + } + + /** + * Returns the distance of the given point from a particular side of the given rectangle. + * Returns negative values for points outside the rectangle. + * + * @param rectangle a bounding rectangle + * @param testPoint a point to test + * @param edgeOfInterest side of the rectangle to test against + * @return the distance of the given point from the given edge of the rectangle + * @since 3.0 + */ + public static int getDistanceFromEdge(Rectangle rectangle, Point testPoint, + int edgeOfInterest) { + switch (edgeOfInterest) { + case DWT.TOP: + return testPoint.y - rectangle.y; + case DWT.BOTTOM: + return rectangle.y + rectangle.height - testPoint.y; + case DWT.LEFT: + return testPoint.x - rectangle.x; + case DWT.RIGHT: + return rectangle.x + rectangle.width - testPoint.x; + } + + return 0; + } + + /** + * Extrudes the given edge inward by the given distance. That is, if one side of the rectangle + * was sliced off with a given thickness, this returns the rectangle that forms the slice. Note + * that the returned rectangle will be inside the given rectangle if size > 0. + * + * @param toExtrude the rectangle to extrude. The resulting rectangle will share three sides + * with this rectangle. + * @param size distance to extrude. A negative size will extrude outwards (that is, the resulting + * rectangle will overlap the original iff this is positive). + * @param orientation the side to extrude. One of DWT.LEFT, DWT.RIGHT, DWT.TOP, or DWT.BOTTOM. The + * resulting rectangle will always share this side with the original rectangle. + * @return a rectangle formed by extruding the given side of the rectangle by the given distance. + * @since 3.0 + */ + public static Rectangle getExtrudedEdge(Rectangle toExtrude, int size, + int orientation) { + Rectangle bounds = new Rectangle(toExtrude.x, toExtrude.y, + toExtrude.width, toExtrude.height); + + if (!isHorizontal(orientation)) { + bounds.width = size; + } else { + bounds.height = size; + } + + switch (orientation) { + case DWT.RIGHT: + bounds.x = toExtrude.x + toExtrude.width - bounds.width; + break; + case DWT.BOTTOM: + bounds.y = toExtrude.y + toExtrude.height - bounds.height; + break; + } + + normalize(bounds); + + return bounds; + } + + /** + * Returns the opposite of the given direction. That is, returns DWT.LEFT if + * given DWT.RIGHT and visa-versa. + * + * @param swtDirectionConstant one of DWT.LEFT, DWT.RIGHT, DWT.TOP, or DWT.BOTTOM + * @return one of DWT.LEFT, DWT.RIGHT, DWT.TOP, or DWT.BOTTOM + * @since 3.0 + */ + public static int getOppositeSide(int swtDirectionConstant) { + switch (swtDirectionConstant) { + case DWT.TOP: + return DWT.BOTTOM; + case DWT.BOTTOM: + return DWT.TOP; + case DWT.LEFT: + return DWT.RIGHT; + case DWT.RIGHT: + return DWT.LEFT; + } + + return swtDirectionConstant; + } + + /** + * Converts the given bool into an DWT orientation constant. + * + * @param horizontal if true, returns DWT.HORIZONTAL. If false, returns DWT.VERTICAL + * @return DWT.HORIZONTAL or DWT.VERTICAL. + * @since 3.0 + */ + public static int getSwtHorizontalOrVerticalConstant(bool horizontal) { + if (horizontal) { + return DWT.HORIZONTAL; + } + return DWT.VERTICAL; + } + + /** + * Returns true iff the given DWT side constant corresponds to a horizontal side + * of a rectangle. That is, returns true for the top and bottom but false for the + * left and right. + * + * @param swtSideConstant one of DWT.TOP, DWT.BOTTOM, DWT.LEFT, or DWT.RIGHT + * @return true iff the given side is horizontal. + * @since 3.0 + */ + public static bool isHorizontal(int swtSideConstant) { + return !(swtSideConstant is DWT.LEFT || swtSideConstant is DWT.RIGHT); + } + + /** + * Moves the given rectangle by the given delta. + * + * @param rect rectangle to move (will be modified) + * @param delta direction vector to move the rectangle by + * @since 3.0 + */ + public static void moveRectangle(Rectangle rect, Point delta) { + rect.x += delta.x; + rect.y += delta.y; + } + + /** + * Moves each edge of the given rectangle outward by the given amount. Negative values + * cause the rectangle to contract. Does not allow the rectangle's width or height to be + * reduced below zero. + * + * @param rect normalized rectangle to modify + * @param differenceRect difference rectangle to be added to rect + * @since 3.3 + */ + public static void expand(Rectangle rect, Rectangle differenceRect) { + rect.x += differenceRect.x; + rect.y += differenceRect.y; + rect.height = Math.max(0, rect.height + differenceRect.height); + rect.width = Math.max(0, rect.width + differenceRect.width); + } + + /** + * <p>Returns a rectangle which, when added to another rectangle, will expand each side + * by the given number of units.</p> + * + * <p>This is commonly used to store margin sizes. For example:</p> + * + * <code><pre> + * // Expands the left, right, top, and bottom + * // of the given control by 10, 5, 1, and 15 units respectively + * + * Rectangle margins = Geometry.createDifferenceRect(10,5,1,15); + * Rectangle bounds = someControl.getBounds(); + * someControl.setBounds(Geometry.add(bounds, margins)); + * </pre></code> + * + * @param left distance to expand the left side (negative values move the edge inward) + * @param right distance to expand the right side (negative values move the edge inward) + * @param top distance to expand the top (negative values move the edge inward) + * @param bottom distance to expand the bottom (negative values move the edge inward) + * + * @return a difference rectangle that, when added to another rectangle, will cause each + * side to expand by the given number of units + * @since 3.3 + */ + public static Rectangle createDiffRectangle(int left, int right, int top, int bottom) { + return new Rectangle(-left, -top, left + right, top + bottom); + } + + /** + * Moves each edge of the given rectangle outward by the given amount. Negative values + * cause the rectangle to contract. Does not allow the rectangle's width or height to be + * reduced below zero. + * + * @param rect normalized rectangle to modify + * @param left distance to move the left edge outward (negative values move the edge inward) + * @param right distance to move the right edge outward (negative values move the edge inward) + * @param top distance to move the top edge outward (negative values move the edge inward) + * @param bottom distance to move the bottom edge outward (negative values move the edge inward) + * @since 3.1 + */ + public static void expand(Rectangle rect, int left, int right, int top, int bottom) { + rect.x -= left; + rect.width = Math.max(0, rect.width + left + right); + rect.y -= top; + rect.height = Math.max(0, rect.height + top + bottom); + } + + /** + * Normalizes the given rectangle. That is, any rectangle with + * negative width or height becomes a rectangle with positive + * width or height that extends to the upper-left of the original + * rectangle. + * + * @param rect rectangle to modify + * @since 3.0 + */ + public static void normalize(Rectangle rect) { + if (rect.width < 0) { + rect.width = -rect.width; + rect.x -= rect.width; + } + + if (rect.height < 0) { + rect.height = -rect.height; + rect.y -= rect.height; + } + } + + /** + * Converts the given rectangle from display coordinates to the local coordinate system + * of the given object into display coordinates. + * + * @param coordinateSystem local coordinate system being converted to + * @param toConvert rectangle to convert + * @return a rectangle in control coordinates + * @since 3.0 + */ + public static Rectangle toControl(Control coordinateSystem, + Rectangle toConvert) { + return(coordinateSystem.getDisplay().map + (null,coordinateSystem,toConvert)); + } + + /** + * Converts the given rectangle from the local coordinate system of the given object + * into display coordinates. + * + * @param coordinateSystem local coordinate system being converted from + * @param toConvert rectangle to convert + * @return a rectangle in display coordinates + * @since 3.0 + */ + public static Rectangle toDisplay(Control coordinateSystem, + Rectangle toConvert) { + return(coordinateSystem.getDisplay().map + (coordinateSystem,null,toConvert)); + + } + + /** + * Determines where the given point lies with respect to the given rectangle. + * Returns a combination of DWT.LEFT, DWT.RIGHT, DWT.TOP, and DWT.BOTTOM, combined + * with bitwise or (for example, returns DWT.TOP | DWT.LEFT if the point is to the + * upper-left of the rectangle). Returns 0 if the point lies within the rectangle. + * Positions are in screen coordinates (ie: a point is to the upper-left of the + * rectangle if its x and y coordinates are smaller than any point in the rectangle) + * + * @param boundary normalized boundary rectangle + * @param toTest point whose relative position to the rectangle is being computed + * @return one of DWT.LEFT | DWT.TOP, DWT.TOP, DWT.RIGHT | DWT.TOP, DWT.LEFT, 0, + * DWT.RIGHT, DWT.LEFT | DWT.BOTTOM, DWT.BOTTOM, DWT.RIGHT | DWT.BOTTOM + * @since 3.0 + */ + public static int getRelativePosition(Rectangle boundary, Point toTest) { + int result = 0; + + if (toTest.x < boundary.x) { + result |= DWT.LEFT; + } else if (toTest.x >= boundary.x + boundary.width) { + result |= DWT.RIGHT; + } + + if (toTest.y < boundary.y) { + result |= DWT.TOP; + } else if (toTest.y >= boundary.y + boundary.height) { + result |= DWT.BOTTOM; + } + + return result; + } + + /** + * Returns the distance from the point to the nearest edge of the given + * rectangle. Returns negative values if the point lies outside the rectangle. + * + * @param boundary rectangle to test + * @param toTest point to test + * @return the distance between the given point and the nearest edge of the rectangle. + * Returns positive values for points inside the rectangle and negative values for points + * outside the rectangle. + * @since 3.1 + */ + public static int getDistanceFrom(Rectangle boundary, Point toTest) { + int side = getClosestSide(boundary, toTest); + return getDistanceFromEdge(boundary, toTest, side); + } + + /** + * Returns the edge of the given rectangle is closest to the given + * point. + * + * @param boundary rectangle to test + * @param toTest point to compare + * @return one of DWT.LEFT, DWT.RIGHT, DWT.TOP, or DWT.BOTTOM + * + * @since 3.0 + */ + public static int getClosestSide(Rectangle boundary, Point toTest) { + int[] sides = [ DWT.LEFT, DWT.RIGHT, DWT.TOP, DWT.BOTTOM ]; + + int closestSide = DWT.LEFT; + int closestDistance = Integer.MAX_VALUE; + + for (int idx = 0; idx < sides.length; idx++) { + int side = sides[idx]; + + int distance = getDistanceFromEdge(boundary, toTest, side); + + if (distance < closestDistance) { + closestDistance = distance; + closestSide = side; + } + } + + return closestSide; + } + + /** + * Returns a copy of the given rectangle + * + * @param toCopy rectangle to copy + * @return a copy of the given rectangle + * @since 3.0 + */ + public static Rectangle copy(Rectangle toCopy) { + return new Rectangle(toCopy.x, toCopy.y, toCopy.width, toCopy.height); + } + + /** + * Returns the size of the rectangle, as a Point + * + * @param rectangle rectangle whose size is being computed + * @return the size of the given rectangle + * @since 3.0 + */ + public static Point getSize(Rectangle rectangle) { + return new Point(rectangle.width, rectangle.height); + } + + /** + * Sets the size of the given rectangle to the given size + * + * @param rectangle rectangle to modify + * @param newSize new size of the rectangle + * @since 3.0 + */ + public static void setSize(Rectangle rectangle, Point newSize) { + rectangle.width = newSize.x; + rectangle.height = newSize.y; + } + + /** + * Sets the x,y position of the given rectangle. For a normalized + * rectangle (a rectangle with positive width and height), this will + * be the upper-left corner of the rectangle. + * + * @param rectangle rectangle to modify + * @param newSize new size of the rectangle + * + * @since 3.0 + */ + public static void setLocation(Rectangle rectangle, Point newSize) { + rectangle.width = newSize.x; + rectangle.height = newSize.y; + } + + /** + * Returns the x,y position of the given rectangle. For normalized rectangles + * (rectangles with positive width and height), this is the upper-left + * corner of the rectangle. + * + * @param toQuery rectangle to query + * @return a Point containing the x,y position of the rectangle + * + * @since 3.0 + */ + public static Point getLocation(Rectangle toQuery) { + return new Point(toQuery.x, toQuery.y); + } + + /** + * Returns a new rectangle with the given position and dimensions, expressed + * as points. + * + * @param position the (x,y) position of the rectangle + * @param size the size of the new rectangle, where (x,y) -> (width, height) + * @return a new Rectangle with the given position and size + * + * @since 3.0 + */ + public static Rectangle createRectangle(Point position, Point size) { + return new Rectangle(position.x, position.y, size.x, size.y); + } + + /** + * Repositions the 'inner' rectangle to lie completely within the bounds of the 'outer' + * rectangle if possible. One use for this is to ensure that, when setting a control's bounds, + * that they will always lie within its parent's client area (to avoid clipping). + * + * @param inner The 'inner' rectangle to be repositioned (should be smaller than the 'outer' rectangle) + * @param outer The 'outer' rectangle + */ + public static void moveInside(Rectangle inner, Rectangle outer) { + // adjust X + if (inner.x < outer.x) { + inner.x = outer.x; + } + if ((inner.x + inner.width) > (outer.x + outer.width)) { + inner.x -= (inner.x + inner.width) - (outer.x + outer.width); + } + + // Adjust Y + if (inner.y < outer.y) { + inner.y = outer.y; + } + if ((inner.y + inner.height) > (outer.y + outer.height)) { + inner.y -= (inner.y + inner.height) - (outer.y + outer.height); + } + } + +}