/* File StraightLine2D.java 
 *
 * Project : Java Geometry Library
 *
 * ===========================================
 * 
 * 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.
 *
 * 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., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */

// package

package math.geom2d.line;

//Imports

import java.awt.Graphics2D;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.Collection;

import math.geom2d.*;
import math.geom2d.circulinear.CircleLine2D;
import math.geom2d.circulinear.CirculinearDomain2D;
import math.geom2d.circulinear.GenericCirculinearDomain2D;
import math.geom2d.conic.Circle2D;
import math.geom2d.domain.SmoothContour2D;
import math.geom2d.polygon.Polyline2D;
import math.geom2d.transform.CircleInversion2D;
import math.utils.EqualUtils;

/**
 * Implementation of a straight line. Such a line can be constructed using two
 * points, a point and a parallel line or straight object, or with coefficient
 * of the Cartesian equation.
 */
public class StraightLine2D extends AbstractLine2D implements
        SmoothContour2D, Cloneable, CircleLine2D {

    // ===================================================================
    // constants

    // ===================================================================
    // class variables

    // ===================================================================
    // static methods

    /**
     * Creates a straight line going through a point and with a given angle.
	 * @deprecated since 0.11.1
	 */
	@Deprecated
    public static StraightLine2D create(Point2D point, double angle) {
        return new StraightLine2D(point.x(), point.y(), Math.cos(angle),
                Math.sin(angle));
    }

    /**
     * Creates a straight line through 2 points.
	 * @deprecated since 0.11.1
	 */
	@Deprecated
    public static StraightLine2D create(Point2D p1, Point2D p2) {
        return new StraightLine2D(p1, p2);
    }

    /**
     * Creates a straight line through a point and with a given direction
     * vector.
	 * @deprecated since 0.11.1
	 */
	@Deprecated
    public static StraightLine2D create(Point2D origin, Vector2D direction) {
        return new StraightLine2D(origin, direction);
    }

    /**
     * Creates a vertical straight line through the given point.
     * @since 0.10.3
     */
    public static StraightLine2D createHorizontal(Point2D origin) {
        return new StraightLine2D(origin, new Vector2D(1, 0));
    }

    /**
     * Creates a vertical straight line through the given point.
     * @since 0.10.3
     */
    public static StraightLine2D createVertical(Point2D origin) {
        return new StraightLine2D(origin, new Vector2D(0, 1));
    }

    /**
     * Creates a median between 2 points.
     * 
     * @param p1 one point
     * @param p2 another point
     * @return the median of points p1 and p2
     * @since 0.6.3
     */
    public static StraightLine2D createMedian(Point2D p1, Point2D p2) {
        Point2D mid = Point2D.midPoint(p1, p2);
        StraightLine2D line = StraightLine2D.create(p1, p2);
        return StraightLine2D.createPerpendicular(line, mid);
    }


    /**
     * Returns a new Straight line, parallel to another straight object (ray,
     * straight line or edge), and going through the given point.
     * 
     * @since 0.6.3
     */
    public static StraightLine2D createParallel(LinearShape2D line,
            Point2D point) {
        return new StraightLine2D(line, point);
    }


    /**
     * Returns a new Straight line, parallel to another straight object (ray,
     * straight line or edge), and going through the given point.
     * 
     * @since 0.6.3
     */
    public static StraightLine2D createParallel(LinearShape2D linear,
            double d) {
        StraightLine2D line = linear.supportingLine();
        double d2 = d / Math.hypot(line.dx, line.dy);
		return new StraightLine2D(
				line.x0 + line.dy * d2, line.y0 - line.dx * d2, 
				line.dx, line.dy);
	}

    /**
     * Returns a new Straight line, perpendicular to a straight object (ray,
     * straight line or edge), and going through the given point.
     * 
     * @since 0.6.3
     */
    public static StraightLine2D createPerpendicular(
            LinearShape2D linear, Point2D point) {
        StraightLine2D line = linear.supportingLine();
        return new StraightLine2D(point, -line.dy, line.dx);
    }


    /**
     * Returns a new Straight line, with the given coefficient of the cartesian
     * equation (a*x + b*y + c = 0).
     */
    public static StraightLine2D createCartesian(double a, double b,
            double c) {
        return new StraightLine2D(a, b, c);
    }

    /**
     * Computes the intersection point of the two (infinite) lines going through
     * p1 and p2 for the first one, and p3 and p4 for the second one. Returns
     * null if two lines are parallel.
     */
    public static Point2D getIntersection(Point2D p1, Point2D p2, Point2D p3,
            Point2D p4) {
        StraightLine2D line1 = new StraightLine2D(p1, p2);
        StraightLine2D line2 = new StraightLine2D(p3, p4);
        return line1.intersection(line2);
    }

    // ===================================================================
    // constructors

    /** Empty constructor: a straight line corresponding to horizontal axis. */
    public StraightLine2D() {
        this(0, 0, 1, 0);
    }

    /** Defines a new Straight line going through the two given points. */
    public StraightLine2D(Point2D point1, Point2D point2) {
        this(point1, new Vector2D(point1, point2));
    }

    /**
     * Defines a new Straight line going through the given point, and with the
     * specified direction vector.
     */
    public StraightLine2D(Point2D point, Vector2D direction) {
        this(point.x(), point.y(), direction.x(), direction.y());
    }

    /**
     * Defines a new Straight line going through the given point, and with the
     * specified direction vector.
     */
    public StraightLine2D(Point2D point, double dx, double dy) {
        this(point.x(), point.y(), dx, dy);
    }

    /**
     * Defines a new Straight line going through the given point, and with the
     * specified direction given by angle.
     */
    public StraightLine2D(Point2D point, double angle) {
        this(point.x(), point.y(), Math.cos(angle), Math.sin(angle));
    }

    /*
     * Defines a new Straight line going through the point (xp, yp) and with
     * the direction dx, dy.
     */
    public StraightLine2D(double xp, double yp, double dx, double dy) {
        super(xp, yp, dx, dy);
    }

    /**
     * Copy constructor:
     * Defines a new Straight line at the same position and with the same
     * direction than an other straight object (line, edge or ray).
     */
    public StraightLine2D(LinearShape2D line) {
        this(line.origin(), line.direction());
    }
    
    /**
     * Defines a new Straight line, parallel to another straigth object (ray,
     * straight line or edge), and going through the given point.
     */
    public StraightLine2D(LinearShape2D line, Point2D point) {
        this(point, line.direction());
    }

    /**
     * Defines a new straight line, from the coefficients of the cartesian
     * equation. The starting point of the line is then the point of the line
     * closest to the origin, and the direction vector has unit norm.
     */
    public StraightLine2D(double a, double b, double c) {
        this(0, 0, 1, 0);
		double d = a * a + b * b;
		x0 = -a * c / d;
		y0 = -b * c / d;
        double theta = Math.atan2(-a, b);
        dx = Math.cos(theta);
        dy = Math.sin(theta);
    }

    // ===================================================================
    // methods specific to StraightLine2D

    /**
     * Returns a new Straight line, parallel to another straight object (ray,
     * straight line or edge), and going through the given point.
     */
    public StraightLine2D parallel(Point2D point) {
        return new StraightLine2D(point, dx, dy);
    }

    // ===================================================================
    // methods implementing the CirculinearCurve2D interface

	/**
	 * Returns the parallel line located at a distance d from the line. 
	 * Distance is positive in the 'right' side of the line (outside of the
	 * limiting half-plane), and negative in the 'left' of the line.
	 * 
	 * @throws DegeneratedLine2DException
	 *             if line direction vector is null
	 */
    public StraightLine2D parallel(double d) {
		double d2 = Math.hypot(this.dx, this.dy);
		if (Math.abs(d2) < Shape2D.ACCURACY)
			throw new DegeneratedLine2DException(
					"Can not compute parallel of degenerated line", this);
		d2 = d / d2;
		return new StraightLine2D(x0 + dy * d2, y0 - dx * d2, dx, dy);
    }

    /**
     * Returns a new Straight line, parallel to another straight object (ray,
     * straight line or edge), and going through the given point.
     */
    @Override
    public StraightLine2D perpendicular(Point2D point) {
        return new StraightLine2D(point, -dy, dx);
    }
    
	/* (non-Javadoc)
	 * @see math.geom2d.circulinear.CirculinearCurve2D#transform(math.geom2d.transform.CircleInversion2D)
	 */
	@Override
	public CircleLine2D transform(CircleInversion2D inv) {
		// Extract inversion parameters
        Point2D center 	= inv.center();
        double r 		= inv.radius();
        
        // projection of inversion center on the line
        Point2D po 	= this.projectedPoint(center);
        double d 	= this.distance(center);

        // Degenerate case of a point belonging to the line:
		// the transform is the line itself.
		if (Math.abs(d) < Shape2D.ACCURACY) {
			return new StraightLine2D(this);
        }
        
        // angle from center to line
        double angle = Angle2D.horizontalAngle(center, po);

		// center of transformed circle
		double r2 = r * r / d / 2;
		Point2D c2 = Point2D.createPolar(center, r2, angle);

		// choose direction of circle arc
		boolean direct = this.isInside(center);

		// return the created circle
        return new Circle2D(c2, r2, direct);
    }
	

    // ===================================================================
    // methods specific to Boundary2D interface

	/* (non-Javadoc)
	 * @see math.geom2d.domain.Boundary2D#domain()
	 */
    public CirculinearDomain2D domain() {
        return new GenericCirculinearDomain2D(this);
    }

	/* (non-Javadoc)
	 * @see math.geom2d.domain.Boundary2D#fill()
	 */
    public void fill(Graphics2D g2) {
        g2.fill(this.getGeneralPath());
    }

    
    // ===================================================================
    // methods specific to OrientedCurve2D interface

	/* (non-Javadoc)
	 * @see math.geom2d.domain.OrientedCurve2D#windingAngle(Point2D)
	 */
    @Override
    public double windingAngle(Point2D point) {

        double angle1 = Angle2D.horizontalAngle(-dx, -dy);
        double angle2 = Angle2D.horizontalAngle(dx, dy);

		if (this.isInside(point)) {
			if (angle2 > angle1)
				return angle2 - angle1;
			else
				return 2 * Math.PI - angle1 + angle2;
		} else {
			if (angle2 > angle1)
				return angle2 - angle1 - 2 * Math.PI;
			else
				return angle2 - angle1;
        }
    }

    
    // ===================================================================
    // methods implementing the ContinuousCurve2D interface

    /**
     * Throws an exception when called.
     */
	@Override
    public Polyline2D asPolyline(int n) {
        throw new UnboundedShape2DException(this);
    }

    
    // ===================================================================
    // methods implementing the Curve2D interface

    /** Throws an infiniteShapeException */
	@Override
    public Point2D firstPoint() {
        throw new UnboundedShape2DException(this);
    }

	/** Throws an infiniteShapeException */
	@Override
	public Point2D lastPoint() {
		throw new UnboundedShape2DException(this);
	}

    /** Returns an empty list of points. */
	@Override
    public Collection singularPoints() {
        return new ArrayList(0);
    }

    /** Returns false, whatever the position. */
	@Override
    public boolean isSingular(double pos) {
        return false;
    }

    /**
     * Returns the parameter of the first point of the line, which is always
     * Double.NEGATIVE_INFINITY.
     */
    public double t0() {
        return Double.NEGATIVE_INFINITY;
    }

    /**
     * @deprecated replaced by t0() (since 0.11.1).
     */
    @Deprecated
    public double getT0() {
    	return t0();
    }

    /**
     * Returns the parameter of the last point of the line, which is always
     * Double.POSITIVE_INFINITY.
     */
    public double t1() {
        return Double.POSITIVE_INFINITY;
    }

    /**
     * @deprecated replaced by t1() (since 0.11.1).
     */
    @Deprecated
    public double getT1() {
    	return this.t1();
    }
    
    /**
     * Returns the point specified with the parametric representation of the
     * line.
     */
    public Point2D point(double t) {
		return new Point2D(x0 + dx * t, y0 + dy * t);
    }

    /**
     * Need to override to cast the type.
     */
    @Override
    public Collection continuousCurves() {
        ArrayList list = 
        	new ArrayList(1);
        list.add(this);
        return list;
    }

    /**
     * Returns the straight line with same origin but with opposite direction
     * vector.
     */
    public StraightLine2D reverse() {
        return new StraightLine2D(this.x0, this.y0, -this.dx, -this.dy);
    }

    public GeneralPath appendPath(GeneralPath path) {
        throw new UnboundedShape2DException(this);
    }
    

    // ===================================================================
    // methods implementing the Shape2D interface

    /** Always returns false, because a line is not bounded. */
    public boolean isBounded() {
        return false;
    }

    /**
     * Returns the distance of the point (x, y) to this straight line.
     */
    @Override
    public double distance(double x, double y) {
        Point2D proj = super.projectedPoint(x, y);
        return proj.distance(x, y);
    }

    public Box2D boundingBox() {
        if (Math.abs(dx) < Shape2D.ACCURACY)
            return new Box2D(
                    x0, x0, 
                    Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        if (Math.abs(dy) < Shape2D.ACCURACY)
            return new Box2D(
                    Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 
                    x0, y0);

        return new Box2D(
                Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
                Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
    }

    /**
     * Returns the transformed line. The result is still a StraightLine2D.
     */
    @Override
    public StraightLine2D transform(AffineTransform2D trans) {
        double[] tab = trans.coefficients();
		return new StraightLine2D(
				x0 * tab[0] + y0 * tab[1] + tab[2], 
				x0 * tab[3] + y0 * tab[4] + tab[5], 
				dx * tab[0] + dy * tab[1], 
				dx * tab[3] + dy * tab[4]);
    }

    
    // ===================================================================
    // methods implementing the Shape interface

    /**
     * Returns true if the point (x, y) lies on the line, with precision given
     * by Shape2D.ACCURACY.
     */
    public boolean contains(double x, double y) {
        return super.supportContains(x, y);
    }

    /**
     * Returns true if the point p lies on the line, with precision given by
     * Shape2D.ACCURACY.
     */
    @Override
    public boolean contains(Point2D p) {
        return super.supportContains(p.x(), p.y());
    }

    /** Throws an infiniteShapeException */
    public java.awt.geom.GeneralPath getGeneralPath() {
        throw new UnboundedShape2DException(this);
    }

	// ===================================================================
	// methods implementing the GeometricObject2D interface

	/* (non-Javadoc)
	 * @see math.geom2d.GeometricObject2D#almostEquals(math.geom2d.GeometricObject2D, double)
	 */
    public boolean almostEquals(GeometricObject2D obj, double eps) {
		if (this == obj)
			return true;

		if (!(obj instanceof StraightLine2D))
			return false;
		StraightLine2D line = (StraightLine2D) obj;

		if (Math.abs(x0 - line.x0) > eps)
			return false;
		if (Math.abs(y0 - line.y0) > eps)
			return false;
		if (Math.abs(dx - line.dx) > eps)
			return false;
		if (Math.abs(dy - line.dy) > eps)
			return false;
        
        return true;
    }

   
    // ===================================================================
    // methods overriding the Object class

    @Override
    public String toString() {
        return new String("StraightLine2D(" + x0 + "," + y0 + "," + 
        		dx + "," + dy + ")");
    }
    
    @Override
    public boolean equals(Object obj) {
		if (this == obj)
			return true;
        if (!(obj instanceof StraightLine2D))
            return false;
        StraightLine2D that = (StraightLine2D) obj;
        
        // Compare each field
		if (!EqualUtils.areEqual(this.x0, that.x0)) 
			return false;
		if (!EqualUtils.areEqual(this.y0, that.y0)) 
			return false;
		if (!EqualUtils.areEqual(this.dx, that.dx)) 
			return false;
		if (!EqualUtils.areEqual(this.dy, that.dy)) 
			return false;

        return true;
    }
    
	/**
	 * @deprecated use copy constructor instead (0.11.2)
	 */
	@Deprecated
    @Override
    public StraightLine2D clone() {
        return new StraightLine2D(x0, y0, dx, dy);
    }
}
 

Ads help maintain this website.