/**
*
*/
package cz.cuni.mff.peckam.java.origamist.math;
import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
/**
* A line segment in 3D.
*
* @author Martin Pecka
*/
public class Segment3d extends Line3d
{
/** */
private static final long serialVersionUID = -7131577422249930817L;
protected Point3d p2;
public Segment3d(Point3d p1, Point3d p2)
{
super(p1, p2);
this.p2 = p2;
}
public Segment3d(Segment3d seg)
{
this(seg.getP1(), seg.getP2());
}
public Point3d getP1()
{
return this.p;
}
public Point3d getP2()
{
return this.p2;
}
/**
* @return The both endpoints of the segment.
*/
public Point3d[] getPoints()
{
return new Point3d[] { 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 Segment3d getIntersection(Line3d line)
{
if (line instanceof Segment3d) {
return getIntersection((Segment3d) line);
}
// line is a pure line, not a segment
Line3d intersection = super.getIntersection(line);
if (intersection == null || intersection == this)
// no intersection with this as a pure line, or this lies on line
return (Segment3d) intersection;
if (contains(intersection.p)) {
return new Segment3d(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 Segment3d getIntersection(Segment3d segment)
{
// creating a new instance of line is necessary to avoid infinite call loop
Segment3d intersection = getIntersection(new Line3d(segment.p, segment.v));
if (intersection == null) {
return null;
} else if (intersection.v.epsilonEquals(new Vector3d(), EPSILON)) { // single point intersection
if (!segment.contains(intersection.p))
return null;
return new Segment3d(intersection.p, intersection.p);
} else { // the segments lie on the same line
Segment3d big = this;
Segment3d 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 Segment3d(big.p, small.p);
} else {
return new Segment3d(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 Segment3d(big.p, small.p2);
} else {
return new Segment3d(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(Point3d point)
{
// handling border points by the quotient is quite inaccurate, so do it 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);
}
@Override
public boolean epsilonEquals(Line3d 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 Line3d#epsilonEquals(Line3d)}.
*/
public boolean epsilonEquals(Line3d other, boolean allowInverseDirection)
{
if (this == other)
return true;
if (!getClass().equals(other.getClass()))
return false;
Point3d op = other.p;
Point3d op2 = ((Segment3d) 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 these segments have a common part (at least a point).
*
* @param segment The other segment to check.
* @return true if these segments have a common part (at least a point).
*/
public boolean overlaps(Segment3d 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(Point3d point)
{
return p.epsilonEquals(point, EPSILON) || p2.epsilonEquals(point, EPSILON);
}
@Override
public Segment3d clone()
{
return new Segment3d(new Point3d(p), new Point3d(p2));
}
@Override
public String toString()
{
Point3d pt = new Point3d(v);
pt.add(p);
return "Segment3d [" + p + " + t*" + v + "] [" + p + ", " + pt + "]";
}
}