/** * */ package cz.cuni.mff.peckam.java.origamist.math; import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON; import javax.vecmath.Point2d; import javax.vecmath.Vector2d; /** * A line segment in 2D. * * @author Martin Pecka */ public class Segment2d extends Line2d { /** */ private static final long serialVersionUID = -118307867048129986L; protected Point2d p2; public Segment2d(Point2d p1, Point2d p2) { super(p1, p2); this.p2 = p2; } /** * @param line */ public Segment2d(Line2d line) { super(line); p2 = new Point2d(p); p2.add(v); } /** * Set the points of this segment. * * @param p1 * @param p2 */ public void set(Point2d p1, Point2d p2) { this.p = p1; this.p2 = p2; this.v = new Vector2d(p2); this.v.sub(p1); } public Point2d getP1() { return this.p; } public Point2d getP2() { return this.p2; } /** * @return The both endpoints of the segment. */ public Point2d[] getPoints() { return new Point2d[] { p, p2 }; } /** * @return The intersection point (as a segment with zero direction vector); <code>null</code> if no intersection * point was found; <code>this</code> if this lies on the given line and the line is not a segment; or a * (new instance of) segment of intersection of this segment and the given segment. */ @Override public Segment2d getIntersection(Line2d line) { if (line instanceof Segment2d) { return getIntersection((Segment2d) line); } // line is a pure line, not a segment Line2d intersection = super.getIntersection(line); if (intersection == null || intersection == this) // no intersection with this as a pure line, or this lies on line return (Segment2d) intersection; if (contains(intersection.p)) { return new Segment2d(intersection.p, intersection.p); } return null; } /** * Return the intersection with another segment. * * @param segment The other segment to find intersection with. * @return The intersection point (as a segment with zero direction vector); <code>null</code> if no intersection * point was found; or a (new instance of) segment of intersection of this segment and the given segment. */ public Segment2d getIntersection(Segment2d segment) { // creating a new instance of line is necessary to avoid infinite call loop Segment2d intersection = getIntersection(new Line2d(segment.p, segment.v)); if (intersection == null) { return null; } else if (intersection.v.epsilonEquals(new Vector2d(), EPSILON)) { // single point intersection if (!segment.contains(intersection.p)) return null; return new Segment2d(intersection.p, intersection.p); } else { // the segments lie on the same line Segment2d big = this; Segment2d small = segment; if (!this.contains(segment.p) && !this.contains(segment.p2)) { big = segment; small = this; } // if one segment is whole inside the other, big is the "containing" segment... otherwise it doesn't matter if (big.contains(small.p)) { if (big.contains(small.p2)) { return small.clone(); } else { // small has to contain either big.p or big.p2 if (small.contains(big.p)) { return new Segment2d(big.p, small.p); } else { return new Segment2d(big.p2, small.p); } } } else if (big.contains(small.p2)) { // small has to contain either big.p or big.p2 if (small.contains(big.p)) { return new Segment2d(big.p, small.p2); } else { return new Segment2d(big.p2, small.p2); } } else { // big is the potentionally "containing" segment, and if it contains no points of small, the segments // have no intersection return null; } } } /** * @return The length of the segment. */ public double getLength() { return v.length(); } @Override public boolean contains(Point2d point) { // handling border points by quotient is quite inaccurate, so handle them this way if (p.epsilonEquals(point, EPSILON) || p2.epsilonEquals(point, EPSILON)) return true; Double quotient = getParameterForPoint(point); // if the quotient is between 0 and 1, the point lies inside the segment return (quotient != null && quotient >= -EPSILON && quotient <= 1.0 + EPSILON); } /** * Return the point on this segment that is the nearest to the given point. * * @param p The point to find the nearest one for. * @return The point on this segment that is the nearest to the given point. */ @Override public Point2d getNearestPoint(Point2d p) { Point2d point = super.getNearestPoint(p); if (point != null && contains(point)) { return point; } else { double p1dist = this.p.distance(p); double p2dist = this.p2.distance(p); if (p1dist < p2dist) return this.p; else return p2; } } @Override public boolean epsilonEquals(Line2d other) { return epsilonEquals(other, false); } /** * @param other The line to compare. * @param allowInverseDirection If true, even two segments with inverted direction will be considered equal. * @return see {@link Line2d#epsilonEquals(Line3d)}. */ public boolean epsilonEquals(Line2d other, boolean allowInverseDirection) { if (this == other) return true; if (other == null) return false; if (!getClass().equals(other.getClass())) return false; Point2d op = other.p; Point2d op2 = ((Segment2d) other).p2; boolean p_op = p.epsilonEquals(op, EPSILON); boolean p_op2 = p.epsilonEquals(op2, EPSILON); boolean p2_op = p2.epsilonEquals(op, EPSILON); boolean p2_op2 = p2.epsilonEquals(op2, EPSILON); if (allowInverseDirection) { return (p_op && p2_op2) || (p_op2 && p2_op); } else { return p_op && p2_op2; } } /** * Return true if the segments have a common part (at least a point) and are parallel. * * @param segment The other segment to check. * @return true if the segments have a common part (at least a point) and are parallel. */ public boolean overlaps(Segment2d segment) { return contains(segment.p) || contains(segment.p2) || segment.contains(p) || segment.contains(p2); } /** * Return true if the given point is the beginning or end of this segment. * * @param point The point to check. Cannot be <code>null</code>. * @return true if the given point is the beginning or end of this segment. */ public boolean isBorderPoint(Point2d point) { return p.epsilonEquals(point, EPSILON) || p2.epsilonEquals(point, EPSILON); } @Override public Segment2d clone() { return new Segment2d(new Point2d(p), new Point2d(p2)); } @Override public String toString() { Point2d pt = new Point2d(v); pt.add(p); return "Segment2d [" + p + " + t*" + v + "] [" + p + ", " + pt + "]"; } }