/**
*
*/
package cz.cuni.mff.peckam.java.origamist.modelstate;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector2d;
import javax.vecmath.Vector3d;
import cz.cuni.mff.peckam.java.origamist.math.IntersectionWithTriangle;
import cz.cuni.mff.peckam.java.origamist.math.Segment2d;
import cz.cuni.mff.peckam.java.origamist.math.Segment3d;
/**
* A segment in the model containing additional model information.
*
* @author Martin Pecka
*/
public class ModelSegment extends Segment3d
{
/** */
private static final long serialVersionUID = 5783162571585033772L;
/** The image of this segment on the paper in 2D. */
protected Segment2d original;
/**
* The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper, <code>null</code> means
* that this is an older segment that doesn't need to signalize if it is MOUNTAIN or VALLEY.
*/
protected Direction direction;
/** Id of the step "this segment was last touched". */
protected int originatingStepId;
/**
* @param segment The segment in 3D.
* @param original The image of this segment on the paper in 2D.
* @param direction The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper,
* <code>null</code> means that this is an older segment that doesn't need to signalize if it is MOUNTAIN
* or VALLEY.
* @param originatingStepId Id of the step "this segment was last touched".
*/
public ModelSegment(Segment3d segment, Segment2d original, Direction direction, int originatingStepId)
{
super(segment.getP1(), segment.getP2());
this.original = new Segment2d(original);
this.direction = direction;
this.originatingStepId = originatingStepId;
}
/**
* Create a model segment with <code>null</code> direction and originatingStepId 0.
*
* @param segment The segment in 3D.
* @param original The image of this segment on the paper in 2D.
*/
public ModelSegment(Segment3d segment, Segment2d original)
{
this(segment, original, null, 0);
}
/**
* @param line The foldLine to fetch data from.
*/
public ModelSegment(FoldLine line)
{
this(line.getSegment3d(), line.getSegment2d(), line.getDirection(), line.getFold().getOriginatingStepId());
}
/**
* Create a model segment with <code>null</code> direction and originatingStepId 0.
*
* @param p1 The start point.
* @param p2 The end point.
*/
public ModelSegment(ModelPoint p1, ModelPoint p2)
{
this(p1, p2, null, 0);
}
/**
* @param p1 The start point.
* @param p2 The end point.
* @param direction The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper,
* <code>null</code> means that this is an older segment that doesn't need to signalize if it is MOUNTAIN
* or VALLEY.
* @param originatingStepId Id of the step "this segment was last touched".
*/
public ModelSegment(ModelPoint p1, ModelPoint p2, Direction direction, int originatingStepId)
{
this(new Segment3d(p1, p2), new Segment2d(p1.getOriginal(), p2.getOriginal()), direction, originatingStepId);
}
/**
* @param inter Intersection with model triangle to create this segment from.
* @param direction The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper,
* <code>null</code> means that this is an older segment that doesn't need to signalize if it is MOUNTAIN
* or VALLEY.
* @param originatingStepId Id of the step "this segment was last touched".
*/
public ModelSegment(IntersectionWithTriangle<ModelTriangle> inter, Direction direction, int originatingStepId)
{
this(inter, new Segment2d(((ModelPoint) inter.getPoint(0)).getOriginal(),
((ModelPoint) inter.getPoint(1)).getOriginal()), direction, originatingStepId);
}
@Override
public boolean isSinglePoint()
{
return super.isSinglePoint() && original.isSinglePoint();
}
@Override
protected boolean isNonTrivial()
{
return !super.isSinglePoint() && !original.isSinglePoint();
}
/**
* @return The image of this segment on the paper in 2D.
*/
public Segment2d getOriginal()
{
return original;
}
/**
* @param original The image of this segment on the paper in 2D.
*/
public void setOriginal(Segment2d original)
{
this.original = original;
}
/**
* @return The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper,
* <code>null</code> means that this is an older segment that doesn't need to signalize if it is MOUNTAIN
* or VALLEY.
*/
public Direction getDirection()
{
return direction;
}
/**
* @param direction The direction of the segment. MOUNTAIN/VALLEY is related to the front side of the paper,
* <code>null</code> means that this is an older segment that doesn't need to signalize if it is MOUNTAIN
* or VALLEY.
*/
public void setDirection(Direction direction)
{
this.direction = direction;
}
/**
* @return Id of the step "this segment was last touched".
*/
public int getOriginatingStepId()
{
return originatingStepId;
}
/**
* @param originatingStepId Id of the step "this segment was last touched".
*/
public void setOriginatingStepId(int originatingStepId)
{
this.originatingStepId = originatingStepId;
}
/**
* If the segments are parallel and overlap, return true and change this segment to the union of the two segments.
* Otherwise return false and leave this segment unchanged.
*
* @param other The segment to merge with.
* @return True if the segments were merged.
*/
public boolean merge(ModelSegment other)
{
if (this.original.contains(other.original.getP1()) && this.original.contains(other.original.getP2()))
// other is either equal or is a subsegment
return true;
if (!this.original.isParallelTo(other.original) || !this.isParallelTo(other))
return false;
if (!this.original.overlaps(other.original) || !this.overlaps(other))
return false;
if ((this.original.containsAll(other.original.getPoints()) != this.containsAll(other.getPoints()))
|| (other.original.containsAll(this.original.getPoints()) != other.containsAll(this.getPoints())))
return false;
Segment3d trial;
if ((trial = new Segment3d(this.getP1(), other.getP1())) != null && trial.contains(this.getP2())
&& trial.contains(other.getP2())) {
this.p2 = other.getP1();
this.original = new Segment2d(this.original.getP1(), other.original.getP1());
} else if ((trial = new Segment3d(this.getP1(), other.getP2())) != null && trial.contains(this.getP2())
&& trial.contains(other.getP1())) {
this.p2 = other.getP2();
this.original = new Segment2d(this.original.getP1(), other.original.getP2());
} else if ((trial = new Segment3d(this.getP2(), other.getP1())) != null && trial.contains(this.getP1())
&& trial.contains(other.getP2())) {
this.p = other.getP1();
this.original = new Segment2d(other.original.getP1(), this.original.getP2());
} else if ((trial = new Segment3d(this.getP2(), other.getP2())) != null && trial.contains(this.getP1())
&& trial.contains(other.getP1())) {
this.p = other.getP2();
this.original = new Segment2d(other.original.getP2(), this.original.getP2());
} else { // other contains this
this.p = other.getP1();
this.p2 = other.getP2();
this.original = new Segment2d(other.original);
}
this.v = new Vector3d(this.p2);
this.v.sub(this.p);
return true;
}
/**
* If this segment contains the given point (and it isn't its border point), split it at that point, set this
* segment to be one part and return the other part.
*
* @param point The split point.
* @return The second segment, or <code>null</code> if this segment couldn't be split at the given point.
*/
public ModelSegment split(Point3d point)
{
if (!contains(point) || isBorderPoint(point))
return null;
double param = getParameterForPoint(point);
Point2d point2 = new Point2d(this.original.getP1());
Vector2d vec2 = new Vector2d(this.original.getVector());
vec2.scale(param);
point2.add(vec2);
ModelSegment result = new ModelSegment(new Segment3d(point, this.getP2()), new Segment2d(point2,
this.original.getP2()), direction, originatingStepId);
this.p2 = point;
this.v = new Vector3d(this.p2);
this.v.sub(this.p);
this.original = new Segment2d(this.original.getP1(), point2);
return result;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((direction == null) ? 0 : direction.hashCode());
result = prime * result + ((original == null) ? 0 : original.hashCode());
result = prime * result + originatingStepId;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
ModelSegment other = (ModelSegment) obj;
if (direction != other.direction)
return false;
if (original == null) {
if (other.original != null)
return false;
} else if (!original.equals(other.original))
return false;
if (originatingStepId != other.originatingStepId)
return false;
return true;
}
/**
* @param other The line to compare.
* @param allowInverseDirection If true, even two segments with inverted direction will be considered equal.
* @return If the segments are epsilon-equal in both 3D and 2D.
*/
public boolean epsilonEquals(ModelSegment other, boolean allowInverseDirection)
{
return this == other
|| (super.epsilonEquals(other, allowInverseDirection) && other.getOriginal().epsilonEquals(original,
allowInverseDirection));
}
@Override
public ModelPoint getPointForParameter(double param)
{
return new ModelPoint(super.getPointForParameter(param), original.getPointForParameter(param));
}
@Override
public ModelSegment clone()
{
return new ModelSegment(super.clone(), original.clone(), direction, originatingStepId);
}
@Override
public String toString()
{
return "ModelSegment [segment=" + super.toString() + ", original=" + original + ", direction=" + direction
+ ", originatingStepId=" + originatingStepId + "]";
}
}