/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.gef.draw2d.geometry;
import static java.lang.Math.PI;
import static java.lang.Math.atan;
import static java.lang.Math.cos;
import static java.lang.Math.max;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.xmind.gef.draw2d.geometry.PrecisionLine.LineType;
public class Geometry {
public static final double MIN_DISTANCE = 0.00000001;
public static final double TWO_PI = PI * 2;
public static final double HALF_PI = PI / 2;
public static final double NEG_HALF_PI = -PI / 2;
public static final int QUADRANT_ONE = 1;
public static final int QUADRANT_TWO = 2;
public static final int QUADRANT_THREE = 3;
public static final int QUADRANT_FOUR = 4;
public static final int SIDE_ONE = 1;
public static final int SIDE_TWO = 2;
public static final int SIDE_THREE = 3;
public static final int SIDE_FOUR = 4;
public static final int SIDE_FIVE = 5;
public static final int SIDE_SIX = 6;
public static final int SIDE_SEVEN = 7;
public static final int SIDE_EIGHT = 8;
protected Geometry() {
}
/**
* Calculates the radian angle of the line from (x, y) to (0, 0).
*
* @param x
* The X coordinate
* @param y
* The Y coordinate
* @return The radian angle of the line from (x, y) to (0, 0). Varies from
* <code>-pi</code>(excluded) to <code>pi</code>(included).
* <code>0</code> means rightwards; <code>pi/2</code> means
* downwards; <code>pi</code> means leftwards; <code>-pi/2</code>
* means upwards.
*/
public static double getAngle(double x, double y) {
if (x == 0) {
if (y > 0)
return HALF_PI;
if (y < 0)
return NEG_HALF_PI;
return 0;
}
double a = atan(y / x);
if (x > 0)
return a;
if (y < 0)
return a - PI;
return a + PI;
}
public static double getAngle(Dimension d) {
return getAngle(d.width, d.height);
}
public static double getAngle(PrecisionDimension d) {
return getAngle(d.width, d.height);
}
public static double getAngle(Point p) {
return getAngle(p.x, p.y);
}
public static double getAngle(PrecisionPoint p) {
return getAngle(p.x, p.y);
}
public static double getAngle(Point p, Point origin) {
return getAngle(p.x - origin.x, p.y - origin.y);
}
public static double getAngle(PrecisionPoint p, PrecisionPoint origin) {
return getAngle(p.x - origin.x, p.y - origin.y);
}
/**
*
*
* @param x1
* @param y1
* @param x2
* @param y2
* @param deltaAngle
* The angle between the line (x1, y1)-(x, y) and the line (x2,
* y2)-(x1, y1)
* @param amount
* The amount of the ratio of the distance (x1, y1)-(x, y) to the
* distance (x1, y1)-(x2, y2)
* @param minDistance
* The minimum distance from (x1, y1) to (x2, y2) to avoid
* division by zero
* @return An point by the <code>deltaAngle</code> and <code>amount</code>
* from (x1, y1) to (x2, y2).
*/
public static PrecisionPoint getPoint(double x1, double y1, double x2,
double y2, double deltaAngle, double amount, double minDistance,
PrecisionPoint result) {
double dx = x2 - x1;
double dy = y2 - y1;
double d = Math.max(Math.hypot(dx, dy), minDistance);
double angle = getAngle(dx, dy);
return result.setLocation(x1, y1).move(angle + deltaAngle, d * amount);
}
public static PrecisionPoint getPoint(double x1, double y1, double x2,
double y2, double deltaAngle, double amount, PrecisionPoint result) {
return getPoint(x1, y1, x2, y2, deltaAngle, amount, MIN_DISTANCE,
result);
}
public static PrecisionPoint getPoint2(double x1, double y1, double x2,
double y2, double deltaAngle, double dist, PrecisionPoint result) {
double dx = x2 - x1;
double dy = y2 - y1;
double angle = getAngle(dx, dy);
return result.setLocation(x1, y1).move(angle + deltaAngle, dist);
}
/**
* Calculates the amount of the ratio of the distance (x1, y1)-(x, y) to the
* distance (x1, y1)-(x2, y2).
*
* @param x
* @param y
* @param x1
* @param y1
* @param x2
* @param y2
* @param minDistance
* The minimum distance from (x1, y1) to (x2, y2) to avoid
* division by zero
* @return
* @throws IllegalArgumentException
* if minDistance is equal to or less than zero
*/
public static double getAmount(double x, double y, double x1, double y1,
double x2, double y2, double minDistance) {
if (minDistance <= 0)
throw new IllegalArgumentException();
double d = Math.hypot(x - x1, y - y1);
double d2 = Math.max(Math.hypot(x2 - x1, y2 - y1), minDistance);
return d / d2;
}
public static double getAmount(double x, double y, double x1, double y1,
double x2, double y2) {
return getAmount(x, y, x1, y1, x2, y2, MIN_DISTANCE);
}
/**
* Calculates the angle between the line (x1, y1)-(x, y) and the line (x1,
* y1)-(x2, y2).
*
* @param x
* @param y
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
public static double getDeltaAngle(double x, double y, double x1,
double y1, double x2, double y2) {
double angle = getAngle(x - x1, y - y1);
double angle2 = getAngle(x2 - x1, y2 - y1);
return constrainAngleRadian(angle - angle2);
}
/**
* Converts a polar coordinate system to rectangular coordinate system.
*
* @param angle
* The angular coordinate; <code>0</code> means rightwards;
* <code>pi/2</code> means downwards; <code>pi</code> means
* leftwards; <code>-pi/2</code> means upwards.
* @param distance
* The radial coordinate.
* @return A point representing the given position in rectangular coordinate
* form.
*/
public static Point getPoint(double angle, double distance) {
double x = distance * cos(angle);
double y = distance * sin(angle);
return new Point((int) x, (int) y);
}
/**
*
* @param angle
* @param distance
* @return
*/
public static Point getPoint(Point p, double angle, double distance) {
return p.getTranslated(getPoint(angle, distance));
}
public static boolean isSameAngleDegree(double angle1, double angle2,
double tolerance) {
return Math.abs(constrainAngleDegree(angle1)
- constrainAngleDegree(angle2)) < tolerance;
}
public static double constrainAngleDegree(double angle) {
angle = angle - ((int) (angle / 360)) * 360;
if (angle < -180)
angle += 360;
else if (angle > 180)
angle -= 360;
return angle;
}
public static boolean isSameAngleRadian(double angle1, double angle2,
double tolerance) {
return Math.abs(constrainAngleRadian(angle1)
- constrainAngleRadian(angle2)) < tolerance;
}
public static double constrainAngleRadian(double angle) {
angle = angle - ((int) (angle / TWO_PI)) * TWO_PI;
if (angle < NEG_HALF_PI)
angle += TWO_PI;
else if (angle > PI)
angle -= TWO_PI;
return angle;
}
public static Insets expand(Insets ins, int exp) {
return ins.add(new Insets(exp, exp, exp, exp));
}
public static Insets expand(Insets ins, int width, int height) {
return ins.add(new Insets(height, width, height, width));
}
public static Insets getExpanded(Insets ins, int exp) {
return expand(new Insets(ins), exp);
}
public static Insets getExpanded(Insets ins, int width, int height) {
return expand(new Insets(ins), width, height);
}
public static Insets union(Insets ins, Insets other) {
if (other == null)
return ins;
return union(ins, other.top, other.left, other.bottom, other.right);
}
public static Insets union(Insets ins, int top, int left, int bottom,
int right) {
if (ins == null)
return new Insets(top, left, bottom, right);
ins.top = Math.max(ins.top, top);
ins.left = Math.max(ins.left, left);
ins.bottom = Math.max(ins.bottom, bottom);
ins.right = Math.max(ins.right, right);
return ins;
}
public static Insets add(Insets ins, Insets other) {
if (other == null)
return ins;
if (ins == null)
return new Insets(other);
return ins.add(other);
}
public static Insets add(Insets ins, int margin) {
return add(ins, margin, margin, margin, margin);
}
public static Insets add(Insets ins, int top, int left, int bottom,
int right) {
if (ins == null)
return new Insets(top, left, bottom, right);
ins.top += top;
ins.left += left;
ins.bottom += bottom;
ins.right += right;
return ins;
}
public static Insets getUnioned(Insets ins, Insets other) {
if (other == null)
return ins == null ? null : new Insets(ins);
if (ins == null)
return new Insets(other);
return union(new Insets(ins), other);
}
public static Insets remove(Insets from, Insets toRemove) {
from.top = max(0, from.top - toRemove.top);
from.bottom = max(0, from.bottom - toRemove.bottom);
from.left = max(0, from.left - toRemove.left);
from.right = max(0, from.right - toRemove.right);
return from;
}
public static Insets getRemoved(Insets from, Insets toRemove) {
return remove(new Insets(from), toRemove);
}
public static Insets scale(Insets ins, double scale) {
ins.top = (int) (Math.floor(ins.top * scale));
ins.left = (int) (Math.floor(ins.left * scale));
ins.bottom = (int) (Math.floor(ins.bottom * scale));
ins.right = (int) (Math.floor(ins.right * scale));
return ins;
}
public static Insets getScaled(Insets ins, double scale) {
return scale(new Insets(ins), scale);
}
public static Rectangle shrink(Rectangle r, Insets i) {
r.x += i.left;
r.y += i.top;
r.width -= i.getWidth();
r.height -= i.getHeight();
return r;
}
public static Rectangle getShrinked(Rectangle r, Insets i) {
return shrink(r.getCopy(), i);
}
public static Insets getCropped(Rectangle box, Rectangle clip) {
Insets ins = new Insets();
ins.top = clip.y - box.y;
ins.left = clip.x - box.x;
ins.bottom = box.bottom() - clip.bottom();
ins.right = box.right() - clip.right();
return ins;
}
/**
* Calculates distances from a point to the four sides of a rectangle.
*
* @param p
* the point
* @param box
* the rectangle
* @return the distances from the point to the four sides of the rectangle
*/
public static Insets getInsets(Point p, Rectangle box) {
return new Insets(p.y - box.y, p.x - box.x, box.bottom() - p.y,
box.right() - p.x);
}
public static Rectangle getExpanded(Point p, Insets ins) {
return getExpanded(p.x, p.y, ins);
}
public static Rectangle getExpanded(int x, int y, Insets ins) {
return new Rectangle(x - ins.left, y - ins.top, ins.getWidth(),
ins.getHeight());
}
public static Rectangle getBounds(Rectangle box, boolean outline,
int lineWidth, int expansion) {
if (outline)
box = box.getResized(-lineWidth, -lineWidth);
if (expansion != 0)
box = box.getExpanded(expansion, expansion);
return box;
}
public static Point getLocation(int orientation, Rectangle box,
boolean outline, int lineWidth, int expansion) {
box = getBounds(box, outline, lineWidth, expansion);
switch (orientation) {
case PositionConstants.EAST:
return box.getRight();
case PositionConstants.WEST:
return box.getLeft();
case PositionConstants.SOUTH:
return box.getBottom();
case PositionConstants.NORTH:
return box.getTop();
case PositionConstants.NORTH_EAST:
return box.getTopRight();
case PositionConstants.NORTH_WEST:
return box.getTopLeft();
case PositionConstants.SOUTH_EAST:
return box.getBottomRight();
case PositionConstants.SOUTH_WEST:
return box.getBottomLeft();
}
return box.getCenter();
}
/**
* @param originSize
* @param constrainedSize
* @return
*/
public static Dimension getScaledConstrainedSize(Dimension originSize,
Dimension constrainedSize) {
return getScaledConstrainedSize(originSize.width, originSize.height,
constrainedSize.width, constrainedSize.height);
}
public static Dimension getScaledConstrainedSize(int w, int h,
int maxWidth, int maxHeight) {
if (w == 0 || h == 0)
return new Dimension();
if (maxWidth < 0 && maxHeight < 0)
return new Dimension(w, h);
if (w <= maxWidth && h <= maxHeight)
return new Dimension(w, h);
int nw = w * maxHeight / h;
int nh = h * maxWidth / w;
if (maxWidth < 0)
return new Dimension(nw, maxHeight);
if (maxHeight < 0)
return new Dimension(maxWidth, nh);
if (nw < maxWidth)
maxWidth = nw;
if (nh < maxHeight)
maxHeight = nh;
return new Dimension(Math.max(1, maxWidth), Math.max(1, maxHeight));
}
public static Point getPopupLocation(Dimension size, Rectangle host,
Rectangle display) {
Point p = host.getBottomLeft();
if (p.y + size.height > display.bottom()) {
p.y = host.y - size.height;
if (p.y < 0) {
p.y = display.y - size.height;
}
}
if (p.y < display.y) {
p.y = display.y;
}
if (p.x + size.width > display.right()) {
p.x = display.right() - size.width;
}
if (p.x < display.x) {
p.x = display.x;
}
return p;
}
public static int getOrientation(Point p) {
return getOrientation(p.x, p.y, 0, 0);
}
public static int getOrientation(Point p, Point origin) {
if (p == null)
return PositionConstants.NONE;
return getOrientation(p.x, p.y, origin.x, origin.y);
}
public static int getOrientation(int x1, int y1, int x2, int y2) {
if (x1 == x2 && y1 < y2)
return PositionConstants.NORTH;
if (x1 > x2 && y1 < y2)
return PositionConstants.NORTH_EAST;
if (x1 > x2 && y1 == y2)
return PositionConstants.EAST;
if (x1 > x2 && y1 > y2)
return PositionConstants.SOUTH_EAST;
if (x1 == x2 && y1 > y2)
return PositionConstants.SOUTH;
if (x1 < x2 && y1 > y2)
return PositionConstants.SOUTH_WEST;
if (x1 < x2 && y1 == y2)
return PositionConstants.WEST;
if (x1 < x2 && y1 < y2)
return PositionConstants.NORTH_WEST;
return PositionConstants.NONE;
}
public static int getOppositePosition(int position) {
if (position == PositionConstants.EAST)
return PositionConstants.WEST;
if (position == PositionConstants.WEST)
return PositionConstants.EAST;
if (position == PositionConstants.SOUTH)
return PositionConstants.NORTH;
if (position == PositionConstants.NORTH)
return PositionConstants.SOUTH;
if (position == PositionConstants.NORTH_EAST)
return PositionConstants.SOUTH_WEST;
if (position == PositionConstants.NORTH_WEST)
return PositionConstants.SOUTH_EAST;
if (position == PositionConstants.SOUTH_EAST)
return PositionConstants.NORTH_WEST;
if (position == PositionConstants.SOUTH_WEST)
return PositionConstants.NORTH_EAST;
if (position == PositionConstants.NORTH_SOUTH)
return PositionConstants.EAST_WEST;
if (position == PositionConstants.EAST_WEST)
return PositionConstants.NORTH_SOUTH;
return position;
}
public static Point getPositionWithin(int alignment, Rectangle boundingBox,
Dimension toPlace) {
int x;
if ((alignment & PositionConstants.LEFT) != 0) {
x = boundingBox.x;
} else if ((alignment & PositionConstants.RIGHT) != 0) {
x = boundingBox.right() - toPlace.width;
} else {
x = boundingBox.x + (boundingBox.width - toPlace.width) / 2;
}
int y;
if ((alignment & PositionConstants.TOP) != 0) {
y = boundingBox.y;
} else if ((alignment & PositionConstants.BOTTOM) != 0) {
y = boundingBox.bottom() - toPlace.height;
} else {
y = boundingBox.y + (boundingBox.height - toPlace.height) / 2;
}
return new Point(x, y);
}
public static Point getRotatedPoint(Point p, double angle) {
angle += getAngle(p);
double distance = p.getDistance(new Point());
return getPoint(angle, distance);
}
public static PointList getRotatedRectangle(Rectangle r, double theta) {
PointList pl = new PointList(4);
pl.addPoint(getRotatedPoint(r.getTopLeft(), theta));
pl.addPoint(getRotatedPoint(r.getTopRight(), theta));
pl.addPoint(getRotatedPoint(r.getBottomLeft(), theta));
pl.addPoint(getRotatedPoint(r.getBottomRight(), theta));
return pl;
}
public static Dimension getConstrainedSize(Dimension size,
Dimension minSize, Dimension maxSize) {
return new Dimension(
constrain(size.width, minSize.width, maxSize.width), constrain(
size.height, minSize.height, maxSize.height));
}
public static int constrain(int i, int min, int max) {
return Math.max(Math.min(i, max), min);
}
public static double constrain(double d, double min, double max) {
return Math.max(Math.min(d, max), min);
}
public static Dimension getScaledConstrainedSize2(Dimension size,
Dimension origin, Dimension minSize, Dimension maxSize) {
return getScaledConstrainedSize2(size.width, size.height,
slope(origin), minSize.width, minSize.height, maxSize.width,
maxSize.height);
}
public static Dimension getScaledConstrainedSize2(double width,
double height, Dimension origin, Dimension minSize,
Dimension maxSize) {
return getScaledConstrainedSize2(width, height, slope(origin),
minSize.width, minSize.height, maxSize.width, maxSize.height);
}
public static Dimension getScaledConstrainedSize2(double width,
double height, double originWidth, double originHeight,
double minWidth, double minHeight, double maxWidth, double maxHeight) {
return getScaledConstrainedSize2(width, height,
slope(originWidth, originHeight), minWidth, minHeight,
maxWidth, maxHeight);
}
public static Dimension getScaledConstrainedSize2(double width,
double height, double slope, double minWidth, double minHeight,
double maxWidth, double maxHeight) {
double prefH = constrain(width * slope, minHeight, maxHeight);
if (height < prefH) {
if (height >= 0)
height = prefH;
else
width = constrain(width, minWidth, maxWidth);
} else {
if (height >= 0)
width = constrain(height / slope, minWidth, maxWidth);
}
height = constrain(width * slope, minHeight, maxHeight);
width = constrain(height / slope, minWidth, maxWidth);
return new Dimension((int) (Math.floor(width)),
(int) (Math.floor(height)));
}
public static double slope(Dimension size) {
return slope(size.width, size.height);
}
public static double slope(double width, double height) {
return height / width;
}
/**
* Method getLineSegments. Converts the points of this polyline into a list
* of <code>PrecisionLine</code> objects
*
* @param points
* PointList to get PrecisionLine equivalents of.
* @return List of PrecisionLine objects.
*/
public static List<PrecisionLine> getLineSegments(
List<PrecisionPoint> points) {
if (points.size() <= 1)
return new ArrayList<PrecisionLine>(0);
ArrayList<PrecisionLine> lines = new ArrayList<PrecisionLine>(
points.size() - 1);
for (int i = 0; i < points.size() - 1; i++) {
lines.add(new PrecisionLine(points.get(i), points.get(i + 1),
LineType.LineSegment));
}
return lines;
}
public static List<PrecisionLine> getLineSegments(PrecisionPoint... points) {
return getLineSegments(Arrays.asList(points));
}
/**
* Method getLineSegments. Converts the points of this polyline into a list
* of <code>PrecisionLine</code> objects
*
* @param points
* PointList to get PrecisionLine equivalents of.
* @return List of PrecisionLine objects.
*/
public static List<PrecisionLine> getLineSegments(PrecisionPoint origin,
List<PrecisionPoint> points) {
if (points.size() <= 1)
return new ArrayList<PrecisionLine>(0);
ArrayList<PrecisionLine> lines = new ArrayList<PrecisionLine>(
points.size() - 1);
for (int i = 0; i < points.size() - 1; i++) {
lines.add(new PrecisionLine(new PrecisionPoint(origin)
.translate(points.get(i)), new PrecisionPoint(origin)
.translate(points.get(i + 1)), LineType.LineSegment));
}
return lines;
}
public static PrecisionPoint getRayXLine(PrecisionLine ray,
PrecisionLine line, int tolerance) {
List<PrecisionPoint> intersections = ray.getLinesIntersections(line);
for (int i = 0; i < intersections.size(); i++) {
PrecisionPoint p = intersections.get(i);
if (rayContainsPoint(ray, p, tolerance))
return p;
}
return null;
}
public static PrecisionPoint getRayXLineSeg(PrecisionLine ray,
PrecisionLine lineSeg, int tolerance) {
List<PrecisionPoint> intersections = ray.getLinesIntersections(lineSeg);
for (int i = 0; i < intersections.size(); i++) {
PrecisionPoint p = intersections.get(i);
if (lineSeg.contains(p, tolerance)
&& rayContainsPoint(ray, p, tolerance))
return p;
}
return null;
}
public static PrecisionPoint getRayXRect(PrecisionLine ray,
PrecisionRectangle r, int tolerance) {
List<PrecisionLine> lines = getLineSegments(r.getTopLeft(),
r.getTopRight(), r.getBottomRight(), r.getBottomLeft(),
r.getTopLeft());
for (PrecisionLine line : lines) {
PrecisionPoint p = getRayXLineSeg(ray, line, tolerance);
if (p != null)
return p;
}
return null;
}
public static PrecisionPoint getRayXRectFarther(PrecisionLine ray,
PrecisionRectangle r, int tolerance) {
PrecisionPoint ret = null;
double dist = -1;
List<PrecisionLine> lines = getLineSegments(r.getTopLeft(),
r.getTopRight(), r.getBottomRight(), r.getBottomLeft(),
r.getTopLeft());
for (PrecisionLine line : lines) {
PrecisionPoint p = getRayXLineSeg(ray, line, tolerance);
if (p != null) {
if (ret == null) {
ret = p;
dist = ret.getDistance(ray.getOrigin());
} else {
double d = p.getDistance(ray.getOrigin());
if (d > dist) {
ret = p;
dist = d;
}
}
}
}
return ret;
}
public static boolean rayContainsPoint(PrecisionLine ray, PrecisionPoint p,
int tolerance) {
return ray.contains(p, tolerance)
|| new PrecisionLine(ray.getOrigin(), p).contains(
ray.getTerminus(), tolerance);
}
// public static PrecisionPoint getCenter( PrecisionPoint p1, PrecisionPoint p2 ) {
// return new PrecisionPoint( ( p1.x + p2.x ) / 2, ( p1.y + p2.y) / 2 );
// }
public static Rectangle union(Rectangle r1, Rectangle r2) {
if (r2 == null)
return r1;
if (r1 == null)
return r2.getCopy();
return r1.union(r2);
}
public static PrecisionRectangle union(PrecisionRectangle r1,
PrecisionRectangle r2) {
if (r2 == null)
return r1;
if (r1 == null)
return r2.getCopy();
return r1.union(r2);
}
public static Rectangle intersect(Rectangle r1, Rectangle r2) {
if (r2 == null)
return r1;
if (r1 == null)
return r2.getCopy();
return r1.intersect(r2);
}
public static PrecisionRectangle intersect(PrecisionRectangle r1,
PrecisionRectangle r2) {
if (r2 == null)
return r1;
if (r1 == null)
return r2.getCopy();
return r1.intersect(r2);
}
public static PrecisionPoint[] intersectQuadBezier(PrecisionLine line,
double x1, double y1, double x2, double y2, double x3, double y3) {
double u1 = x1 - 2 * x2 + x3;
double v1 = y1 - 2 * y2 + y3;
double u2 = 2 * x2 - 2 * x3;
double v2 = 2 * y2 - 2 * y3;
double u3 = x3;
double v3 = y3;
double[] eq = line.getEquation();
double a = eq[0] * u1 + eq[1] * v1;
double b = eq[0] * u2 + eq[1] * v2;
double c = eq[0] * u3 + eq[1] * v3 - eq[2];
double delta = b * b - 4 * a * c;
if (Math.abs(delta) < 0.000000001) {
double t = -b / (2 * a);
if (t >= 0 && t <= 1) {
double x = u1 * t * t + u2 * t + u3;
double y = v1 * t * t + v2 * t + v3;
PrecisionPoint p = new PrecisionPoint(x, y);
if (line.contains(p))
return new PrecisionPoint[] { p };
}
} else if (delta > 0) {
double d = Math.sqrt(delta);
List<PrecisionPoint> list = new ArrayList<PrecisionPoint>();
double t1 = (-b + d) / (2 * a);
if (t1 >= 0 && t1 <= 1) {
double x = u1 * t1 * t1 + u2 * t1 + u3;
double y = v1 * t1 * t1 + v2 * t1 + v3;
PrecisionPoint p = new PrecisionPoint(x, y);
if (line.contains(p)) {
list.add(p);
}
}
double t2 = (-b - d) / (2 * a);
if (t2 >= 0 && t2 <= 1) {
double x = u1 * t2 * t2 + u2 * t2 + u3;
double y = v1 * t2 * t2 + v2 * t2 + v3;
PrecisionPoint p = new PrecisionPoint(x, y);
if (line.contains(p)) {
list.add(p);
}
}
return list.toArray(new PrecisionPoint[0]);
}
return new PrecisionPoint[0];
}
public static PrecisionPoint getChopBoxLocation(double refX, double refY,
Rectangle r, double expansion) {
double centerX = r.x + 0.5 * r.width;
double centerY = r.y + 0.5 * r.height;
double dx = refX - centerX;
double dy = refY - centerY;
if (dx == 0)
return new PrecisionPoint(refX, (dy > 0) ? r.bottom() + expansion
: r.y - expansion);
if (dy == 0)
return new PrecisionPoint((dx > 0) ? r.right() + expansion : r.x
- expansion, refY);
double scale = 0.5 / Math.max(Math.abs(dx) / r.width, Math.abs(dy)
/ r.height);
dx *= scale;
dy *= scale;
double d = Math.hypot(dx, dy);
if (d != 0) {
double s = expansion / d;
dx += dx * s;
dy += dy * s;
}
centerX += dx;
centerY += dy;
return new PrecisionPoint(centerX, centerY);
}
public static Point getChopBoxLocation(int x, int y, Rectangle box) {
float centerX = box.x + 0.5f * box.width;
float centerY = box.y + 0.5f * box.height;
float dx = x - centerX;
float dy = y - centerY;
// when preference in the center
if (dx == 0)
return new Point(x, (dy > 0) ? box.bottom() : box.y);
if (dy == 0)
return new Point((dx > 0) ? box.right() : box.x, y);
// r.width, r.height, dx, and dy are guaranteed to be non-zero.
float scale = 0.5f / Math.max(Math.abs(dx) / box.width, Math.abs(dy)
/ box.height);
dx *= scale;
dy *= scale;
centerX += dx;
centerY += dy;
return new Point(Math.round(centerX), Math.round(centerY));
}
public static Point getChopBoxLocation(Point reference, Rectangle box) {
return getChopBoxLocation(reference.x, reference.y, box);
}
public static PrecisionPoint getChopOvalLocation(double refX, double refY,
Rectangle r, double expansion) {
double cx = r.x + r.width * 0.5d;
double cy = r.y + r.height * 0.5d;
double rx = refX - cx;
double ry = refY - cy;
if (rx == 0)
return new PrecisionPoint(refX, (ry > 0) ? r.bottom() + expansion
: r.y - expansion);
if (ry == 0)
return new PrecisionPoint((rx > 0) ? r.right() + expansion : r.x
- expansion, refY);
double scaleX = (rx > 0) ? 0.5d : -0.5d;
double scaleY = (ry > 0) ? 0.5d : -0.5d;
// ref.x, ref.y, r.width, r.height != 0 => safe to proceed
double k = (ry * r.width) / (rx * r.height);
k = k * k;
double dx = r.width * scaleX / sqrt(1 + k);
double dy = r.height * scaleY / sqrt(1 + 1 / k);
double d = Math.hypot(dx, dy);
if (d != 0) {
double s = expansion / d;
dx += dx * s;
dy += dy * s;
}
return new PrecisionPoint(cx + dx, cy + dy);
}
public static Point getChopOvalLocation(int x, int y, Rectangle r) {
Point ref = r.getCenter().negate().translate(x, y);
if (ref.x == 0)
return new Point(x, (ref.y > 0) ? r.bottom() : r.y);
if (ref.y == 0)
return new Point((ref.x > 0) ? r.right() : r.x, y);
float dx = (ref.x > 0) ? 0.5f : -0.5f;
float dy = (ref.y > 0) ? 0.5f : -0.5f;
// ref.x, ref.y, r.width, r.height != 0 => safe to proceed
float k = (float) (ref.y * r.width) / (ref.x * r.height);
k = k * k;
return r.getCenter().translate((int) (r.width * dx / sqrt(1 + k)),
(int) (r.height * dy / sqrt(1 + 1 / k)));
}
public static Point getChopOvalLocation(Point reference, Rectangle r) {
return getChopOvalLocation(reference.x, reference.y, r);
}
public static PrecisionPointPair calculatePositionPair(PrecisionPoint from,
PrecisionPoint to, double w) {
PrecisionPointPair result = new PrecisionPointPair(from.getCopy(),
from.getCopy());
if (from.equals(to))
return result;
PrecisionDimension d = from.getDifference(to);
double wScale = d.width == 0 ? 1 : Math.abs(w / d.width);
double hScale = d.height == 0 ? 1 : Math.abs(w / d.height);
d.scale(wScale, hScale);
result.translate(d);
d.width = -d.width;
result.translateFirstPoint(d.transpose());
result.translateSecondPoint(d.negate());
return result;
}
public static double getDistance(PrecisionPoint p, PrecisionLine line) {
PrecisionPoint p1 = line.getOrigin();
PrecisionPoint p2 = line.getTerminus();
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
if (dx == 0 && dy == 0)
return 0;
if (dy == 0)
return Math.abs(p.y - p1.y);
if (dx == 0)
return Math.abs(p.x - p1.x);
double k = dx / dy;
double m = dy / dx;
double y = (p1.y * k - p1.x + p.y * m + p.x) / (k + m);
double x = (y - p1.y) * k + p1.x;
return Math.hypot(p.x - x, p.y - y);
}
// * --------------------------
// * | | |
// * | 3 | 4 |
// * ------------------------------>
// * | | |
// * | 2 | 1 |
// * --------------------------
// * \/
public static int getQuadrant(double x, double y, Point center) {
if (x <= center.x && y <= center.y) {
return QUADRANT_THREE;
} else if (x <= center.x && y >= center.y) {
return QUADRANT_TWO;
} else if (x >= center.x && y <= center.y) {
return QUADRANT_FOUR;
} else {
return QUADRANT_ONE;
}
}
/**
* Calculate angle of point(x,y)->rectangle center; Angles are interpreted
* such that 0 degrees is at the 3 o'clock position. A positive value
* indicates a counter-clockwise rotation while a negative value indicates a
* clockwise rotation. the returned angle is in the range <i>-pi</i> through
* <i>pi</i>.
*/
public static double getAngle(double x, double y, Rectangle r) {
double cx = r.x + r.width * 0.5d;
double cy = r.y + r.height * 0.5d;
if (x == cx && y >= cy)
return HALF_PI;
if (x == cx && y <= cy)
return NEG_HALF_PI;
double angrad = Math.atan((y - cy) / (x - cx));
int quadrant = getQuadrant(x, y, r.getCenter());
if (quadrant == QUADRANT_ONE) {
angrad = -angrad;
} else if (quadrant == QUADRANT_TWO) {
angrad = -PI - angrad;
} else if (quadrant == QUADRANT_THREE) {
angrad = PI - angrad;
} else if (quadrant == QUADRANT_FOUR) {
angrad = -angrad;
}
return angrad;
}
/**
* Calculate oval angle of point correspondence;Angles are interpreted such
* that 0 degrees is at the 3 o'clock position;the returned angle is in the
* range <i>-pi</i> through <i>pi</i>.
*
* @param x
* x-coordinate of the point on the ellipse
* @param y
* y-coordinate of the point on the ellipse
* @param r
* oval external rectangle
* @return
*/
public static double getOvalAngle(double x, double y, Rectangle r) {
double cx = r.x + r.width * 0.5d;
double k = 2 * (x - cx) / r.width;
if (k > 1)
k = 1;
if (k < -1)
k = -1;
double angrad = Math.acos(k);
int quadrant = getQuadrant(x, y, r.getCenter());
if (quadrant == QUADRANT_ONE || quadrant == QUADRANT_TWO) {
angrad = -angrad;
}
return angrad;
}
/**
* Calculate point(x,y) which edge contains.
*
* @param x
* @param y
* @param r
* @return
*/
public static int getSide(double x, double y, Rectangle r) {
double cx = r.x + r.width * 0.5d;
double cy = r.y + r.height * 0.5d;
if ((x >= r.x && x <= cx) && Math.abs(y - r.y) <= MIN_DISTANCE) {
return SIDE_ONE;
} else if ((x >= cx && x <= r.x + r.width * 1d)
&& Math.abs(y - r.y) <= MIN_DISTANCE) {
return SIDE_TWO;
} else if ((y >= r.y && y <= cy)
&& Math.abs((x - (r.x + r.width * 1.0d))) <= MIN_DISTANCE) {
return SIDE_THREE;
} else if ((y >= cy && y <= r.y + r.height * 1d)
&& Math.abs((x - (r.x + r.width * 1.0d))) <= MIN_DISTANCE) {
return SIDE_FOUR;
} else if ((x >= cx && x <= r.x + r.width * 1d)
&& Math.abs((y - (r.y + r.height * 1.0d))) <= MIN_DISTANCE) {
return SIDE_FIVE;
} else if ((x >= r.x && x <= cx)
&& Math.abs((y - (r.y + r.height * 1.0d))) <= MIN_DISTANCE) {
return SIDE_SIX;
} else if ((y >= cy && y <= r.y + r.height * 1.0d)
&& (Math.abs(x - r.x) <= MIN_DISTANCE)) {
return SIDE_SEVEN;
} else {
return SIDE_EIGHT;
}
}
}