/******************************************************************************* * <copyright> * * Copyright (c) 2005, 2011 SAP AG. * 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: * SAP AG - initial API, implementation and documentation * mwenz - Bug 352440 - Fixed deprecation warnings - contributed by Felix Velasco * * </copyright> * *******************************************************************************/ package org.eclipse.graphiti.ui.internal.util.draw2d; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.PrecisionPoint; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.geometry.Translatable; import org.eclipse.draw2d.geometry.Vector; /** * @noinstantiate This class is not intended to be instantiated by clients. * @noextend This class is not intended to be subclassed by clients. */ public class LineSeg implements java.io.Serializable, Translatable { /** * Enumeration class for defining the keypoint along a line segment. Can be * one of <code>ORIGIN</code>, <code>MIDPOINT</code> or * <code>TERMINUS</code>. */ static public class KeyPoint { /** * Constant designating the origin point on the line segment. */ public static final KeyPoint ORIGIN = new KeyPoint("origin"); //$NON-NLS-1$ /** * Constant designating the mid point on the line segment. */ public static final KeyPoint MIDPOINT = new KeyPoint("midpoint");//$NON-NLS-1$ /** * Constant designating the terminal point on the line segment. */ public static final KeyPoint TERMINUS = new KeyPoint("terminus");//$NON-NLS-1$ private final String name; private KeyPoint(String name) { this.name = name; } @Override public String toString() { return name; } } /** * Enumeration class for defining the orientations of a point relative to * the line segment. The orientations can be one of <code>POSITIVE</code> or * <code>NEGATIVE</code>. */ static public class Sign { /** * Constant designating an orientation that is position relative to the * lineseg vector. */ public static final Sign POSITIVE = new Sign("positive");//$NON-NLS-1$ /** * Constant designating an orientation that is negative relative to the * lineseg vector. */ public static final Sign NEGATIVE = new Sign("negative");//$NON-NLS-1$ private final String name; private Sign(String name) { this.name = name; } @Override public String toString() { return name; } } /** * Structure to hold onto trig values that represent an angle * * @author sshaw */ static public class TrigValues { /** * Sin theta value */ public double sinTheta; /** * Cos theta value. */ public double cosTheta; } static final long serialVersionUID = 1; final private static int DEFAULT_INTERSECTION_TOLERANCE = 1; /** * Constant to avoid divide by zero errors. */ private static final float BIGSLOPE = 9999; /** * Returns the coefficients of the generalized equation of the line passing * through points (x1,y1) and (x2,y2) Generalized line equation: ax+by=c => * a==result[0], b==result[1], c==result[2] * * @param x1 * - x coordinate of the 1st point * @param y1 * - y coordinate of the 1st point * @param x2 * - x coordinate of the 2nd point * @param y2 * - y coordinate of the 2nd point * @return the coefficients of the generalized equation of the line passing * through points (x1,y1) and (x2,y2) */ public static double[] getLineEquation(double x1, double y1, double x2, double y2) { double equation[] = new double[3]; for (int i = 0; i < 3; i++) equation[i] = 0; if (x1 == x2 && y1 == y2) return equation; if (x1 == x2) { equation[0] = 1; equation[1] = 0; equation[2] = x1; return equation; } equation[0] = (y1 - y2) / (x2 - x1); equation[1] = 1.0; equation[2] = y2 + equation[0] * x2; return equation; } private Point origin; private Point terminus; /** * Creates a segment using (fromX, fromY) as either the first point of the * segment (start == Origin) or the midpoint of the segment (start == * Midpoint), and using slope as its new slope and len as the new length. * xdir indicates which direction the segment should go in the x-axis. * * @param start * <code>KeyPoint</code> from which the other parameters are * relative to * @param fromX * int x value of start <code>KeyPoint</code> * @param fromY * int y value of start <code>KeyPoint</code> * @param slope * <code>float</code> slope of the line * @param len * <code>long</code> length of the line * @param xdir * direction */ public LineSeg(final KeyPoint start, final int fromX, final int fromY, final float slope, final long len, final int xdir) { super(); origin = new Point(); terminus = new Point(); int dx, dy; float dx_float; double len_squared; // Find the delta y and x needed to get to the end points. See // pointOn() for explanation of these equations if (start == KeyPoint.ORIGIN) { len_squared = (float) len * (float) len; } else // start == DirectedLine::Midpoint { len_squared = len / 2.0 * len / 2.0; } double slope_squared = slope * slope; dx_float = (float) Math.sqrt(len_squared / (slope_squared + 1.0)); // Set which direction the segment should go in the x direction. // The y direction will get set automatically based on slope // and the dx. dx_float *= xdir; dx = (int) (dx_float + 0.5); dy = (int) ((slope * dx_float) + 0.5); if (start == KeyPoint.ORIGIN) { origin.x = fromX; origin.y = fromY; } else // start == DirectedLine::Midpoint { origin.x = fromX - dx; origin.y = fromY - dy; } terminus.x = fromX + dx; terminus.y = fromY + dy; } /** * Constructor * * @param ptStart * Point indicating the start of the line segment * @param ptEnd * Point indicating the end of the line segment */ public LineSeg(Point ptStart, Point ptEnd) { origin = new Point(ptStart); terminus = new Point(ptEnd); } /** * Checks if this line segment contains the given point within a tolerance * value. * * @param aPoint * <code>Point</code> to test if contained in this line. * @param tolerance * int tolerance value for detecting the intersection. * @return <code>boolean</code> <code>true</code> if the given point lies on * this segment, <code>false</code> otherwise. */ public final boolean containsPoint(final Point aPoint, final int tolerance) { Point theOrigin = getOrigin(); Point theTerminus = getTerminus(); return (theOrigin.getDistance(aPoint) + aPoint.getDistance(theTerminus) <= length() + tolerance); } /** * Finds the percentage distance along this line segement where the given * point resides. * * @param coord * <code>Point</code> to determine how far along the line segment * it resides. * @return <code>float</code> the distance along the line segment where the * ptCoord is in a percentage from. */ public final float distanceAlong(Point coord) { int xCoord = coord.x; int yCoord = coord.y; /* * Use parametric form for equation of a line segment: p + td, where 0 < * t < 1 and d = p2 - p (direction vector) To find out if point lies * "inside" line segment (i.e. can draw perpendicular line from segment * to point), use projection of point (q) to line (p + td): t = * (q-p).d/length(d)^2 (. is dot product) */ /* get the direction vector */ long dirx = (long) terminus.x - (long) origin.x; long diry = (long) terminus.y - (long) origin.y; /* get q - p */ long qpx = (long) xCoord - (long) origin.x; long qpy = (long) yCoord - (long) origin.y; /* dot product of (q-p) and d */ long dotprod = qpx * dirx + qpy * diry; /* * avoid divide by 0 - check if point1 equals point2. If so, there is no * segment - return a value which indicates projection falls outside the * segment. */ if (dirx == 0 && diry == 0) return -1; /* * length (magnitude) of d is sqrt(dirx^2 + diry^2). Don't bother taking * square root since we want the length squared. */ return ((float) dotprod / (float) (dirx * dirx + diry * diry)); } /** * Finds the perpendicular distance from a point coordinates to this line * segment. If point is "inside" line segment, then use distance from point * to the line, otherwise use distance to nearest endpoint of segment * * @param xCoord * the x coordinate of the point. * @param yCoord * the y coordinate of the point. * @return <code>long</code> the distance from the line segment to the given * point. */ public final long distanceToPoint(final int xCoord, final int yCoord) { double proj = projection(xCoord, yCoord); if (proj > 0 && proj < 1) { Point pt = perpIntersect(xCoord, yCoord); return Math.round(pt.getDistance(new Point(xCoord, yCoord))); } long d1 = Math.round(getOrigin().getDistance(new Point(xCoord, yCoord))); long d2 = Math.round(getTerminus().getDistance(new Point(xCoord, yCoord))); return (d1 < d2 ? d1 : d2); /* * There are 2 general forms to equations of a line: 1. y = mx + c, * where c = y1 - m(x1), and 2. ax + by + c = 0 We know m and c, so * putting first version in the form of the second version, we get: mx - * y + c = 0 So, for form 2, a = m, b = -1, and c = y1 - m(x1). Distance * from point (x, y) to line (using form 2) is: |ax + by + c| / sqrt(a^2 * + b^2) or |mx - y + y1 - m(x1)| / sqrt(m^2 + 1) */ } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object seg) { if (!(seg instanceof LineSeg)) return false; LineSeg ls = (LineSeg) seg; return getOrigin().equals(ls.getOrigin()) && getTerminus().equals(ls.getTerminus()); } /** * Returns array with 3 numbers in it, which are the coefficients of the * generalized line equation of the line corresponding to this line segment * a*x+b*y=c is the equation => result[0]=a, result[1]=b, result[2]=c * * @return an array with 3 numbers in it, which are the coefficients of the * generalized line equation */ public double[] getEquation() { return getLineEquation(origin.x, origin.y, terminus.x, terminus.y); } /** * Get a <code>Point</code> representing the lowest point value for this * line segment. * * @return <code>Point</code> Representing the lowest point value. */ public final Point getInfimum() { return new Point(Math.min(origin.x, terminus.x), Math.min(origin.y, terminus.y)); } /** * Calculates intersection points of the line of the line segment and * ellipse * * @param ellipseBounds * - width and height of the ellipse * @return - <Code>PointList</Code> containing all intersection points */ public PointList getLineIntersectionsWithEllipse(Rectangle ellipseBounds) { PointList intersections = new PointList(); if (ellipseBounds.width == 0 || ellipseBounds.height == 0) return intersections; double xl1 = getOrigin().x - ellipseBounds.getCenter().x; double xl2 = getTerminus().x - ellipseBounds.getCenter().x; double yl1 = getOrigin().y - ellipseBounds.getCenter().y; double yl2 = getTerminus().y - ellipseBounds.getCenter().y; double[] equation = getLineEquation(xl1, yl1, xl2, yl2); if (equation.length < 3 || (equation[0] == 0 && equation[1] == 0)) return intersections; double a = equation[0]; double b = equation[1]; double c = equation[2]; double w = ellipseBounds.width; double h = ellipseBounds.height; // Ellipse with a cneter at the origin has an equation: // (h*x)^2+(w*y)^2=(h*w/2)^2 // Line equation: a*x+b*y=c if (b == 0) { // b==0 is a special case since in general case we will express // y in terms of x, i.e. we need to divide by b, which should not // be 0 // b==0 => a*x=c +> x=c/a; double x = c / a; // y^2 = (h/2)^2-((h*c)/(a*w))^2 double y = Math.pow(h / 2, 2) - Math.pow((h * c) / (a * w), 2); if (y < 0) return intersections; intersections.addPoint(new PrecisionPoint(x + ellipseBounds.getCenter().x, Math.sqrt(y) + ellipseBounds.getCenter().y)); intersections.addPoint(new PrecisionPoint(x + ellipseBounds.getCenter().x, -Math.sqrt(y) + ellipseBounds.getCenter().y)); } else { // y = (c-a*x)/b => we get quadratic equation for x // x^2*(h^2+(w*a/b)^2)-x*(2*w^2*a*c)/(b^2)+((w*c/b)^2-(h*w/2)^2)=0 // or // x^2*xA+x*xB+xC=0 double xA = Math.pow(h, 2) + Math.pow((w * a) / b, 2); double xB = (-2) * Math.pow(w, 2) * a * c / Math.pow(b, 2); double xC = Math.pow(w * c / b, 2) - Math.pow(h * w / 2, 2); double xD = Math.pow(xB, 2) - 4 * xA * xC; if (xD < 0) return intersections; double x1 = (-xB + Math.sqrt(xD)) / (2 * xA); double x2 = (-xB - Math.sqrt(xD)) / (2 * xA); intersections.addPoint(new PrecisionPoint(x1 + ellipseBounds.getCenter().x, (c - a * x1) / b + ellipseBounds.getCenter().y)); intersections.addPoint(new PrecisionPoint(x2 + ellipseBounds.getCenter().x, (c - a * x2) / b + ellipseBounds.getCenter().y)); } return intersections; } /** * Calculates intersection points of the line that contains this line * segment with a list of other line segments. If the list of points (line * segments) form a closed <Code>PolyLine</Code>, i.e form a closed polygon * figure, then the method will claculate intersections of a line and a * figure * * @param points * - list of points that form linesegments, i.e the * <Code>PolyLine</Code> * @return the intersection points of the line that contains this line * segment with a list of other line segments. */ public PointList getLineIntersectionsWithLineSegs(final PointList points) { double temp[] = getEquation(); double a = temp[0]; double b = temp[1]; double c = temp[2]; PointList intersections = new PointList(); if (points.size() < 2) { if (a * points.getPoint(0).x + b * points.getPoint(0).y == c) { intersections.addPoint(points.getPoint(0).getCopy()); } return intersections; } for (int i = 0; i < points.size() - 1; i++) { LineSeg line = new LineSeg(points.getPoint(i).getCopy(), points.getPoint(i + 1).getCopy()); PointList currentIntersections = getLinesIntersections(line); for (int j = 0; j < currentIntersections.size(); j++) { Point intersection = currentIntersections.getPoint(j); if (line.containsPoint(intersection, DEFAULT_INTERSECTION_TOLERANCE)) intersections.addPoint(currentIntersections.getPoint(j)); } } return intersections; } /** * Returns intersection points of two lines that contain this line segment * and the argumet line segment. The list of intersection points may contain * at most two points and will contain 2 points if and only if the lines are * equal. The 2 points will be the end points of the parameter line segment * * @param line * - the line segment * @return intersection points of two lines that contain this line segment * and the argumet line segment. */ public PointList getLinesIntersections(LineSeg line) { PointList intersections = new PointList(); double temp[] = getEquation(); double a1 = temp[0]; double b1 = temp[1]; double c1 = temp[2]; temp = line.getEquation(); double a2 = temp[0]; double b2 = temp[1]; double c2 = temp[2]; // Cramer's rule for the system of linear equations double det = a1 * b2 - b1 * a2; if (det == 0) { if (a1 == a2 && b1 == b2 && c1 == c2) { // if lines are the same, then instead of infinite number of // intersections // we will put the end points of the line segment passed as an // argument intersections.addPoint(new Point(line.getOrigin().getCopy())); intersections.addPoint(new Point(line.getTerminus().getCopy())); } } else { intersections.addPoint(new PrecisionPoint((c1 * b2 - b1 * c2) / det, (a1 * c2 - c1 * a2) / det)); } return intersections; } /** * Accesssor to retrieve the origin point of the line segement. * * @return <code>Point</code> the origin of the line segment. */ public Point getOrigin() { return new Point(origin); } /** * Returns a new <code>LineSeg</code> that is parallel to this by the given * distance. Orientation is relative to the start and end. Negative implies * to the left and Position implies to the right. * * @param ptLoc * <code>Point</code> value to constrain the line to. * @return <code>LineSeg</code> line that was calculated going through the * given point */ public final LineSeg getParallelLineSegThroughPoint(Point ptLoc) { if (isHorizontal()) { return new LineSeg(new Point(getOrigin().x, ptLoc.y), new Point(getTerminus().x, ptLoc.y)); } else if (isVertical()) { return new LineSeg(new Point(ptLoc.x, getOrigin().y), new Point(ptLoc.x, getTerminus().y)); } else { Point ptProj = perpIntersect(ptLoc.x, ptLoc.y); long nHeight = Math.round(ptProj.getDistance(ptLoc)); Sign position = positionRelativeTo(ptLoc); return new LineSeg(locatePoint(0.0, nHeight, position), locatePoint(1.0, nHeight, position)); } } /** * Get points representing the highest point value for this line segment. * * @return <code>Point</code> Representing the highest point value. */ public final Point getSupremum() { return new Point(Math.max(origin.x, terminus.x), Math.max(origin.y, terminus.y)); } /** * Accesssor to retrieve the terminal point of the line segement. * * @return <code>Point</code> the terminating point of the line segment */ public Point getTerminus() { return new Point(terminus); } /** * Gets the trig values associated with the angle from this line segment to * the given vector. * * @param ptToVector * <code>Ray</code> value to calculate trig values of. * @return <code>TrigValues</code> object representing the trigonometry * values for the angle of the passed in <code>Ray</code> relative * to <code>this</code> or null if calculation is not possible, */ public TrigValues getTrigValues(final Vector ptToVector) { double dFromLength = length(); double dToLength = ptToVector.getLength(); Vector ptFromVector = new Vector(new PrecisionPoint(getOrigin()), new PrecisionPoint(getTerminus())); if (dFromLength <= 0 || dToLength <= 0) { return null; } // 1. find angle for ptToVector relative to the origin. double dAlpha; double dCosAlpha, dSinAlpha; dCosAlpha = ptFromVector.x / dFromLength; dSinAlpha = ptFromVector.y / dFromLength; dAlpha = Math.atan2(dSinAlpha, dCosAlpha); // 2. inverse the angle to get the rotation dCosAlpha = Math.cos(-dAlpha); dSinAlpha = Math.sin(-dAlpha); // 3. rotate vector 2 by angle above so that it's angle relative to // vector 1 can // be calculated double dRotateX = (ptToVector.x * dCosAlpha) - (ptToVector.y * dSinAlpha); double dRotateY = (ptToVector.x * dSinAlpha) + (ptToVector.y * dCosAlpha); // 4. Now calculate the Theta trig values TrigValues val = new TrigValues(); val.cosTheta = dRotateX / dToLength; val.sinTheta = dRotateY / dToLength; return val; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return getOrigin().hashCode() ^ getTerminus().hashCode(); } /** * Determines the intersect point between this line and the line passed in * as a parameter. If they intersect, then true is returned and the point * reference passed in will be set to the intersect point. If they don't * intersect, then the method returns <code>false</code>. * * @param line * <code>LineSeg</code> to test the intersection against. * @param nTolerance * int tolerance value for detecting the intersection. * @return <code>Point</code> that represents the intersection with this * line, or <code>null</code> if the calculation is not possible. */ public Point intersect(final LineSeg line, final int nTolerance) { PointList intersections = getLinesIntersections(line); if (intersections.size() > 1) { intersections.addPoint(getOrigin().getCopy()); intersections.addPoint(getTerminus().getCopy()); } for (int i = 0; i < intersections.size(); i++) { Point result = intersections.getPoint(i).getCopy(); if (containsPoint(result, nTolerance) && line.containsPoint(result, nTolerance)) { return result; } } return null; } /** * Determines if this a horizontal segment * * @return <code>boolean</code> <code>true</code> if horizontal, * <code>false</code> otherwise. */ public final boolean isHorizontal() { return (origin.y == terminus.y); } /** * Determines if this a vertical segment * * @return <code>boolean</code> <code>true</code> if vertical, * <code>false</code> otherwise. */ public final boolean isVertical() { return (origin.x == terminus.x); } /** * Calculate the length of the line segment. * * @return the <code>double</code> length of the line segment. */ public final double length() { return getOrigin().getDistance(getTerminus()); } /** * Locates a point at a given height and distance along the line segment. B * (the point we are looking for) + | dist |h this segment * P1-----------+-------------------> A get point A (on picture above) * * @param pctDist * <code>double</code> distance along the line * @param theHeight * <code>long</code> height above the line * @param asOriented * <code>Sign</code> indicating relative position of the point to * be located * @return <code>Point</code> value that was located on the line. */ public final Point locatePoint(final double pctDist, final long theHeight, final Sign asOriented) { int xdir; int dist = (int) (pctDist * length()); Point pt = new Point(); pointOn(dist, KeyPoint.ORIGIN, pt); // (x,y) now = A // get linesegment AB // first determine the direction AB should go in the x axis. Don't ask- // just have faith. if (getOrigin().y > getTerminus().y || (getOrigin().y == getTerminus().y && getOrigin().x < getTerminus().x)) { xdir = (asOriented == Sign.POSITIVE ? -1 : 1); } else { xdir = (asOriented == Sign.POSITIVE ? 1 : -1); } LineSeg linesegAB = new LineSeg(KeyPoint.ORIGIN, pt.x, pt.y, perpSlope(), theHeight, xdir); return (new Point(linesegAB.getTerminus().x, linesegAB.getTerminus().y)); } /* * (non-Javadoc) * * @see org.eclipse.draw2d.geometry.Translatable#performScale(double) */ public void performScale(double factor) { setOrigin(getOrigin().scale(factor)); setTerminus(getTerminus().scale(factor)); } /* * (non-Javadoc) * * @see org.eclipse.draw2d.geometry.Translatable#performTranslate(int, int) */ public void performTranslate(int dx, int dy) { setOrigin(getOrigin().translate(dx, dy)); setTerminus(getTerminus().translate(dx, dy)); } /** * Calculates the perpendicular intersection point on the line segment from * the given point. * * @param startX * the x coordinate of the point * @param startY * the y coordinate of the point * @return <code>Point</code> value containment the perpendicular * intersection point. */ public final Point perpIntersect(final int startX, final int startY) { float fx; // The following equations are based on solving 2 equations with // 2 unknowns (x and y). The 2 equations are equations for the // slope of each line segment where the slope and 1 point in the // segment are known: // (y1 - y) / (x1 - x) = m // (sy - y) / (sx - x) = -1/m (-1/m is slope of perp. line) // Point ptResult = new Point(); float m = slope(); fx = (m * startY - m * getOrigin().y + m * m * getOrigin().x + startX) / (float) (m * m + 1.0); if (m == 0) { ptResult.y = getOrigin().y; // segment is horizontal - avoid divide // by 0 } else { ptResult.y = (int) (startY + ((startX - fx) / m) + 0.5); } ptResult.x = Math.round(fx); // add .5 for rounding return ptResult; } /** * Calculates the perpendicular slope of this line segment. This calculates * the slope and then inverts it. Again, to avoid divide by zero errors, the * constant <code>BIGSLOPE</code> is returned if the calculated slope before * inverting it was zero. * * @return <code>float</code> the perpendicular slope value of the line * segment. */ public final float perpSlope() { float m = slope(); if (m == 0.0) return BIGSLOPE; else return -(1.0F / m); } /** * Gets the point on the line segment at the given distance away from the * key point. * * @param theDistance * <code>long</code> distance along the line * @param fromKeyPoint * <code>KeyPoint</code> to calculate the distance from * @param ptResult * <code>Point</code> where the resulting calculating value is * stored. * @return <code>boolean</code> <code>true</code> if point can be * calculated, <code>false</code> otherwise. */ public final boolean pointOn(final long theDistance, final KeyPoint fromKeyPoint, Point ptResult) { float m, dx_float; int dx, dy, startX = 0, startY = 0, otherX = 0, otherY = 0; // Set the point to offset from and the other point used to determine // which direction dx and dy should be applied to get a point on the // line. if (fromKeyPoint == KeyPoint.ORIGIN) { startX = getOrigin().x; startY = getOrigin().y; otherX = getTerminus().x; otherY = getTerminus().y; } else if (fromKeyPoint == KeyPoint.TERMINUS) { startX = getTerminus().x; startY = getTerminus().y; otherX = getOrigin().x; otherY = getOrigin().y; } else if (fromKeyPoint == KeyPoint.MIDPOINT) { startX = (getOrigin().x + getTerminus().x) / 2; startY = (getOrigin().y + getTerminus().y) / 2; otherX = getTerminus().x; otherY = getTerminus().y; } else { return false; } m = slope(); // get the slope of this line // Find dx and dy - the delta x and y to get from the endpoint to the // point on the line at the specified distance away. // The following is based on solving 2 equations with 2 unknowns: // dy/dx = m (m is slope of line) // dy^2 + dx^2 = dist^2 // double d_squared = (float) theDistance * (float) theDistance; double m_squared = m * m; // Add .5 so result is rounded to nearest integer when cast dx_float = (float) Math.sqrt(d_squared / (m_squared + 1.0)); dx = (int) (dx_float + 0.5); dy = (int) (Math.sqrt(d_squared * m_squared / (m_squared + 1.0)) + 0.5); /* negative distance means we want point off the line */ if (theDistance < 0) { dx = -dx; dy = -dy; } ptResult.x = ((startX > otherX) ? startX - dx : startX + dx); ptResult.y = ((startY > otherY) ? startY - dy : startY + dy); boolean in_line; if (startX > otherX) in_line = ptResult.x >= otherX; else in_line = ptResult.x <= otherX; if (in_line) { if (startY > otherY) in_line = ptResult.y >= otherY; else in_line = ptResult.y <= otherY; } return in_line; } /** * Returns out a positive or negative value (Positive / Negative) depending * on the orientation of the given point to the line. Point on this side: * Positive. P1------------------------------> this segment Point on this * side: Negative. * * @param rel * <code>Point</code> to test the relative position against this * line. * @return <code>Sign</code> value indicating the relative position of the * given point. */ public final Sign positionRelativeTo(Point rel) { Vector ptRelRay = new Vector(new PrecisionPoint(getOrigin()), new PrecisionPoint(rel)); TrigValues val = getTrigValues(ptRelRay); double dNewAngle = Math.atan2(-val.sinTheta, -val.cosTheta); if (dNewAngle > 0) return Sign.POSITIVE; return Sign.NEGATIVE; } /** * Calculates the projection of the given point onto the line segment. * * @param xCoord * the x coordinate of the point. * @param yCoord * the y coordinate of the point. * @return <code>double</code> value of the calculated projection. */ public final double projection(final int xCoord, final int yCoord) { /* * Use parametric form for equation of a line segment: p + td, where 0 < * t < 1 and d = p2 - p (direction vector) To find out if point lies * "inside" line segment (i.e. can draw perpendicular line from segment * to point), use projection of point (q) to line (p + td): t = * (q-p).d/length(d)^2 (. is dot product) */ /* get the direction vector */ long dirx = (long) getTerminus().x - (long) getOrigin().x; long diry = (long) getTerminus().y - (long) getOrigin().y; /* get q - p */ long qpx = (long) xCoord - (long) getOrigin().x; long qpy = (long) yCoord - (long) getOrigin().y; /* dot product of (q-p) and d */ long dotprod = qpx * dirx + qpy * diry; /* * avoid divide by 0 - check if point1 equals point2. If so, there is no * segment - return a value which indicates projection falls outside the * segment. */ if (dirx == 0 && diry == 0) return -1.0F; /* * length (magnitude) of d is sqrt(dirx^2 + diry^2). Don't bother taking * square root since we want the length squared. */ return ((double) dotprod / (double) (dirx * dirx + diry * diry)); } /** * Sets the origin point of the line segment * * @param origin * Point to set as origin */ public void setOrigin(Point origin) { this.origin = new Point(origin); } /** * Sets the terminating point of the line segment. * * @param terminus * Point to set as terminus */ public void setTerminus(Point terminus) { this.terminus = new Point(terminus); } /** * Calculates the slope of this line segment (y=mx+b) * * @return <code>float</code> the slope of this segment. If the slope is not * defined such as when the line segment is vertical, then the * constant <code>BIGSLOPE</code> is returned to avoid divide by * zero errors. */ public final float slope() { if (isVertical()) return BIGSLOPE; return (float) (terminus.y - origin.y) / (float) (terminus.x - origin.x); } }