/* * Copyright (c) 2010, Frederik Vanhoutte This library is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * http://creativecommons.org/licenses/LGPL/2.1/ This library is distributed in * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, * Fifth Floor, Boston, MA 02110-1301 USA */ package wblut.geom; import java.util.ArrayList; import wblut.WB_Epsilon; import wblut.math.WB_Fast; // TODO: Auto-generated Javadoc /** * 2D line. */ public class WB_Line2D extends WB_Linear2D { /** * Instantiates a new WB_Line. */ public WB_Line2D() { super(); } /** * Instantiates a new WB_Line. * * @param o origin * @param d direction */ public WB_Line2D(final WB_Point2d o, final WB_Point2d d) { super(o, d, true); } /** * Instantiates a new WB_Line. * * * @param p1x first point on line * @param p1y first point on line * @param p2x second point on line * @param p2y second point on line */ public WB_Line2D(final double p1x, final double p1y, final double p2x, final double p2y) { super(p1x, p1y, p2x, p2y); } /** * Sets new line parameters. * * @param p1 first point on line * @param p2 second point on line */ public void setFromPoints(final WB_Point2d p1, final WB_Point2d p2) { super.set(p1, p2); } /** * Gets the t. * * @param p the p * @return the t */ public double getT(final WB_Point2d p) { double t = Double.NaN; final WB_Point2d proj = WB_Intersection2D.closestPoint2D(p, this); final double x = WB_Fast.abs(direction.x); final double y = WB_Fast.abs(direction.y); if (x >= y) { t = (proj.x - origin.x) / (direction.x); } else { t = (proj.y - origin.y) / (direction.y); } return t; } /** * Classify point to line. * * @param p the p * @return the w b_ classify point to line2 d */ public WB_ClassifyPointToLine2D classifyPointToLine2D(final WB_Point2d p) { final double dist = -direction.y * p.x + direction.x * p.y + origin.x * direction.y - origin.y * direction.x; if (dist > WB_Epsilon.EPSILON) { return WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE; } if (dist < -WB_Epsilon.EPSILON) { return WB_ClassifyPointToLine2D.POINT_BEHIND_LINE; } return WB_ClassifyPointToLine2D.POINT_ON_LINE; } /** * Classify point to line. * * @param p the p * @param L the l * @return the w b_ classify point to line2 d */ public static WB_ClassifyPointToLine2D classifyPointToLine2D( final WB_Point2d p, final WB_Line2D L) { final double dist = L.a() * p.x + L.b() * p.y + L.c(); if (dist > WB_Epsilon.EPSILON) { return WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE; } if (dist < -WB_Epsilon.EPSILON) { return WB_ClassifyPointToLine2D.POINT_BEHIND_LINE; } return WB_ClassifyPointToLine2D.POINT_ON_LINE; } /** * Classify segment to line2 d. * * @param seg the seg * @return the w b_ classify segment to line2 d */ public WB_ClassifySegmentToLine2D classifySegmentToLine2D( final WB_Segment2D seg) { final WB_ClassifyPointToLine2D a = classifyPointToLine2D(seg .getOrigin()); final WB_ClassifyPointToLine2D b = classifyPointToLine2D(seg.getEnd()); if (a == WB_ClassifyPointToLine2D.POINT_ON_LINE) { if (b == WB_ClassifyPointToLine2D.POINT_ON_LINE) { return WB_ClassifySegmentToLine2D.SEGMENT_ON_LINE; } else if (b == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) { return WB_ClassifySegmentToLine2D.SEGMENT_IN_FRONT_OF_LINE; } else { return WB_ClassifySegmentToLine2D.SEGMENT_BEHIND_LINE; } } if (b == WB_ClassifyPointToLine2D.POINT_ON_LINE) { if (a == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) { return WB_ClassifySegmentToLine2D.SEGMENT_IN_FRONT_OF_LINE; } else { return WB_ClassifySegmentToLine2D.SEGMENT_BEHIND_LINE; } } if ((a == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) && (b == WB_ClassifyPointToLine2D.POINT_BEHIND_LINE)) { return WB_ClassifySegmentToLine2D.SEGMENT_SPANNING_LINE; } if ((a == WB_ClassifyPointToLine2D.POINT_BEHIND_LINE) && (b == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE)) { return WB_ClassifySegmentToLine2D.SEGMENT_SPANNING_LINE; } if (a == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) { return WB_ClassifySegmentToLine2D.SEGMENT_IN_FRONT_OF_LINE; } return WB_ClassifySegmentToLine2D.SEGMENT_BEHIND_LINE; } /** * Classify polygon to line2 d. * * @param P the p * @return the w b_ classify polygon to line2 d */ public WB_ClassifyPolygonToLine2D classifyPolygonToLine2D( final WB_Polygon2D P) { int numFront = 0; int numBack = 0; for (int i = 0; i < P.n; i++) { if (classifyPointToLine2D(P.points[i]) == WB_ClassifyPointToLine2D.POINT_IN_FRONT_OF_LINE) { numFront++; } else if (classifyPointToLine2D(P.points[i]) == WB_ClassifyPointToLine2D.POINT_BEHIND_LINE) { numBack++; } if (numFront > 0 && numBack > 0) { return WB_ClassifyPolygonToLine2D.POLYGON_SPANNING_LINE; } } if (numFront > 0) { return WB_ClassifyPolygonToLine2D.POLYGON_IN_FRONT_OF_LINE; } if (numBack > 0) { return WB_ClassifyPolygonToLine2D.POLYGON_BEHIND_LINE; } return null; } /** * Gets the line tangent to circle at point. * * @param C the c * @param p the p * @return the line tangent to circle at point */ public static WB_Line2D getLineTangentToCircleAtPoint(final WB_Circle C, final WB_Point2d p) { final WB_Point2d v = p.subAndCopy(C.getCenter()); return new WB_Line2D(p, new WB_Point2d(-v.y, v.x)); } /** * Gets the lines tangent to circle through point. * * @param C the c * @param p the p * @return the lines tangent to circle through point */ public static ArrayList<WB_Line2D> getLinesTangentToCircleThroughPoint( final WB_Circle C, final WB_Point2d p) { final ArrayList<WB_Line2D> result = new ArrayList<WB_Line2D>(2); final double dcp = WB_Distance2D.distance(C.getCenter(), p); if (WB_Epsilon.isZero(dcp - C.getRadius())) { final WB_Point2d u = p.subAndCopy(C.getCenter()); result.add(new WB_Line2D(p, new WB_Point2d(-u.y, u.x))); } else if (dcp < C.getRadius()) { return result; } else { final WB_Point2d u = p.subAndCopy(C.getCenter()); final double ux2 = u.x * u.x; final double ux4 = ux2 * ux2; final double uy2 = u.y * u.y; final double r2 = C.getRadius() * C.getRadius(); final double r4 = r2 * r2; final double num = r2 * uy2; final double denom = ux2 + uy2; final double rad = Math.sqrt(-r4 * ux2 + r2 * ux4 + r2 * ux2 * uy2); result.add(new WB_Line2D(p, new WB_Point2d(-((r2 * u.y) + rad) / denom, (r2 - (num + u.y * rad) / denom) / u.x))); result.add(new WB_Line2D(p, new WB_Point2d(-((r2 * u.y) - rad) / denom, (r2 - (num - u.y * rad) / denom) / u.x))); } return result; } /** * Gets the lines tangent to2 circles. * * @param C0 the c0 * @param C1 the c1 * @return the lines tangent to2 circles */ public static ArrayList<WB_Line2D> getLinesTangentTo2Circles( final WB_Circle C0, final WB_Circle C1) { final ArrayList<WB_Line2D> result = new ArrayList<WB_Line2D>(4); final WB_Point2d w = C1.getCenter().subAndCopy(C0.getCenter()); final double wlensqr = w.mag2(); final double rsum = C0.getRadius() + C1.getRadius(); if (wlensqr <= rsum * rsum + WB_Epsilon.SQEPSILON) { return result; } final double rdiff = C1.getRadius() - C0.getRadius(); if (!WB_Epsilon.isZero(rdiff)) { final double r0sqr = C0.getRadius() * C0.getRadius(); final double r1sqr = C1.getRadius() * C1.getRadius(); final double c0 = -r0sqr; final double c1 = 2 * r0sqr; final double c2 = C1.getRadius() * C1.getRadius() - r0sqr; final double invc2 = 1.0 / c2; final double discr = Math.sqrt(WB_Fast.abs(c1 * c1 - 4 * c0 * c2)); double s, oms, a; s = -0.5 * (c1 + discr) * invc2; if (s >= 0.5) { a = Math.sqrt(WB_Fast.abs(wlensqr - r0sqr / (s * s))); } else { oms = 1.0 - s; a = Math.sqrt(WB_Fast.abs(wlensqr - r1sqr / (oms * oms))); } WB_Point2d[] dir = getDirections(w, a); WB_Point2d org = new WB_Point2d(C0.getCenter().x + s * w.x, C0.getCenter().y + s * w.y); result.add(new WB_Line2D(org, dir[0])); result.add(new WB_Line2D(org, dir[1])); s = -0.5 * (c1 - discr) * invc2; if (s >= 0.5) { a = Math.sqrt(WB_Fast.abs(wlensqr - r0sqr / (s * s))); } else { oms = 1.0 - s; a = Math.sqrt(WB_Fast.abs(wlensqr - r1sqr / (oms * oms))); } dir = getDirections(w, a); org = new WB_Point2d(C0.getCenter().x + s * w.x, C0.getCenter().y + s * w.y); result.add(new WB_Line2D(org, dir[0])); result.add(new WB_Line2D(org, dir[1])); } else { final WB_Point2d mid = (C0.getCenter().addAndCopy(C1.getCenter())) .mult(0.5); final double a = Math.sqrt(WB_Fast.abs(wlensqr - 4 * C0.getRadius() * C0.getRadius())); final WB_Point2d[] dir = getDirections(w, a); result.add(new WB_Line2D(mid, dir[0])); result.add(new WB_Line2D(mid, dir[1])); final double invwlen = 1.0 / Math.sqrt(wlensqr); w.x *= invwlen; w.y *= invwlen; result.add(new WB_Line2D(new WB_Point2d(mid.x + C0.getRadius() * w.y, mid.y - C0.getRadius() * w.x), w)); result.add(new WB_Line2D(new WB_Point2d(mid.x - C0.getRadius() * w.y, mid.y + C0.getRadius() * w.x), w)); } return result; } /** * Gets the directions. * * @param w the w * @param a the a * @return the directions */ private static WB_Point2d[] getDirections(final WB_Point2d w, final double a) { final WB_Point2d[] dir = new WB_Point2d[2]; final double asqr = a * a; final double wxsqr = w.x * w.x; final double wysqr = w.y * w.y; final double c2 = wxsqr + wysqr; final double invc2 = 1.0 / c2; double c0, c1, discr, invwx; final double invwy; if (WB_Fast.abs(w.x) >= WB_Fast.abs(w.y)) { c0 = asqr - wxsqr; c1 = -2 * a * w.y; discr = Math.sqrt(WB_Fast.abs(c1 * c1 - 4 * c0 * c2)); invwx = 1.0 / w.x; final double dir0y = -0.5 * (c1 + discr) * invc2; dir[0] = new WB_Point2d((a - w.y * dir0y) * invwx, dir0y); final double dir1y = -0.5 * (c1 - discr) * invc2; dir[1] = new WB_Point2d((a - w.y * dir1y) * invwx, dir1y); } else { c0 = asqr - wysqr; c1 = -2 * a * w.x; discr = Math.sqrt(WB_Fast.abs(c1 * c1 - 4 * c0 * c2)); invwy = 1.0 / w.y; final double dir0x = -0.5 * (c1 + discr) * invc2; dir[0] = new WB_Point2d(dir0x, (a - w.x * dir0x) * invwy); final double dir1x = -0.5 * (c1 - discr) * invc2; dir[1] = new WB_Point2d(dir1x, (a - w.x * dir1x) * invwy); } return dir; } /** * Gets the perpendicular line through point. * * @param L the l * @param p the p * @return the perpendicular line through point */ public static WB_Line2D getPerpendicularLineThroughPoint(final WB_Line2D L, final WB_Point2d p) { return new WB_Line2D(p, new WB_Point2d(-L.getDirection().y, L.getDirection().x)); } /** * Gets the parallel line through point. * * @param L the l * @param p the p * @return the parallel line through point */ public static WB_Line2D getParallelLineThroughPoint(final WB_Line2D L, final WB_Point2d p) { return new WB_Line2D(p, L.getDirection()); } /** * Gets the bisector. * * @param p the p * @param q the q * @return the bisector */ public static WB_Line2D getBisector(final WB_Point2d p, final WB_Point2d q) { return new WB_Line2D(WB_Point2d.interpolate(p, q, 0.5), new WB_Point2d( p.y - q.y, q.x - p.x)); } /** * Gets the parallel lines. * * @param L the l * @param d the d * @return the parallel lines */ public static WB_Line2D[] getParallelLines(final WB_Line2D L, final double d) { final WB_Line2D[] result = new WB_Line2D[2]; result[0] = new WB_Line2D( new WB_Point2d(L.getOrigin().x - d * L.getDirection().y, L.getOrigin().y + d * L.getDirection().x), L.getDirection()); result[1] = new WB_Line2D( new WB_Point2d(L.getOrigin().x + d * L.getDirection().y, L.getOrigin().y - d * L.getDirection().x), L.getDirection()); return result; } /** * Gets the perpendicular lines tangent to circle. * * @param L the l * @param C the c * @return the perpendicular lines tangent to circle */ public static WB_Line2D[] getPerpendicularLinesTangentToCircle( final WB_Line2D L, final WB_Circle C) { final WB_Line2D[] result = new WB_Line2D[2]; result[0] = new WB_Line2D(new WB_Point2d(C.getCenter().x + C.getRadius() * L.getDirection().x, C.getCenter().y + C.getRadius() * L.getDirection().y), new WB_Point2d( -L.getDirection().y, L.getDirection().x)); result[1] = new WB_Line2D(new WB_Point2d(C.getCenter().x - C.getRadius() * L.getDirection().x, C.getCenter().y - C.getRadius() * L.getDirection().y), new WB_Point2d( -L.getDirection().y, L.getDirection().x)); return result; } }