/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fge.geom;
import java.awt.geom.AffineTransform;
import java.util.logging.Logger;
import org.openflexo.fge.geom.area.FGEArea;
import org.openflexo.fge.geom.area.FGEEmptyArea;
import org.openflexo.fge.geom.area.FGEHalfBand;
import org.openflexo.fge.geom.area.FGEHalfLine;
import org.openflexo.fge.graphics.FGEGraphics;
public class FGESegment extends FGEAbstractLine<FGESegment> implements FGEGeneralShape.GeneralShapePathElement<FGESegment> {
private static final Logger logger = Logger.getLogger(FGESegment.class.getPackage().getName());
public FGESegment(double X1, double Y1, double X2, double Y2) {
super(X1, Y1, X2, Y2);
}
public FGESegment(FGEPoint p1, FGEPoint p2) {
super(p1, p2);
}
public FGESegment() {
super();
}
public FGEPoint getMiddle() {
return new FGEPoint((getP1().x + getP2().x) / 2, (getP1().y + getP2().y) / 2);
}
@Override
public boolean containsLine(FGEAbstractLine l) {
if (!overlap(l)) {
return false;
}
if (!(containsPoint(l.getP1()) && containsPoint(l.getP2()))) {
return false;
}
if (l instanceof FGEHalfLine) {
return false;
}
if (l instanceof FGESegment) {
return true;
}
// If this is a line this is false
return false;
}
@Override
public boolean contains(FGEPoint p) {
// First see if located on line
if (!_containsPointIgnoreBounds(p)) {
return false;
}
// Now check bounds
if (getB() != 0) {
FGEPoint pp1 = getP1();
FGEPoint pp2 = getP2();
if (pp1.x > pp2.x) {
pp1 = getP2();
pp2 = getP1();
}
if (p.x >= pp1.x - EPSILON) {
if (p.x > pp2.x + EPSILON) {
return false;
} else {
return true;
}
} else {
return false;
}
} else {
FGEPoint pp1 = getP1();
FGEPoint pp2 = getP2();
if (pp1.y > pp2.y) {
pp1 = getP2();
pp2 = getP1();
}
if (p.y >= pp1.y - EPSILON) {
if (p.y > pp2.y + EPSILON) {
return false;
} else {
return true;
}
} else {
return false;
}
}
}
public boolean projectionIntersectsInsideSegment(FGEPoint p) {
FGEPoint projection = getProjection(p);
return contains(projection);
}
/**
* Return flag indicating if intersection of this segment and supplied line occurs somewhere INSIDE this segment
*
* @param line
* @return
*/
public boolean intersectsInsideSegment(FGEAbstractLine line) {
FGEPoint intersection;
try {
intersection = getLineIntersection(line);
} catch (ParallelLinesException e) {
return false;
}
return contains(intersection) && line.contains(intersection);
}
/**
* Return flag indicating if intersection of this segment and supplied line occurs somewhere INSIDE this segment. If insideOnly set to
* true and intersection is one of extremities return false
*
* @param line
* @return
*/
public boolean intersectsInsideSegment(FGEAbstractLine line, boolean insideOnly) {
FGEPoint intersection;
try {
intersection = getLineIntersection(line);
} catch (ParallelLinesException e) {
return false;
}
if (!(contains(intersection) && line.contains(intersection))) {
return false;
}
if (insideOnly) {
if (intersection.equals(getP1()) || intersection.equals(getP2()) || line instanceof FGESegment
&& (intersection.equals(line.getP1()) || intersection.equals(line.getP2()))) {
return false;
}
}
return true;
}
public static FGEPoint getClosestPointOnSegment(FGEPoint p, FGESegment segment) {
return segment.getNearestPointOnSegment(p);
}
public static boolean intersectsInsideSegment(FGEPoint p, FGESegment segment) {
return segment.projectionIntersectsInsideSegment(p);
}
/**
* Return flag indicating if intersection of supplied segment and supplied line occurs somewhere INSIDE supplied segment
*
* @param line
* @return
*/
public static boolean intersectsInsideSegment(FGESegment segment, FGEAbstractLine line) {
return segment.intersectsInsideSegment(line);
}
@Override
public FGEPoint getNearestPoint(FGEPoint p) {
return getNearestPointOnSegment(p);
}
/**
* Return nearest point on segment
*
* If orthogonal projection of supplied point on segment is inside the segment, return this projection. Otherwise, return adequate
* segment extremity
*
* @param p
* @return
*/
public FGEPoint getNearestPointOnSegment(FGEPoint p) {
FGEPoint projection = getProjection(p);
if (getB() != 0) {
FGEPoint pp1 = getP1();
FGEPoint pp2 = getP2();
if (pp1.x > pp2.x) {
pp1 = getP2();
pp2 = getP1();
}
if (projection.x >= pp1.x) {
if (projection.x >= pp2.x) {
return pp2;
} else {
return projection;
}
} else {
return pp1;
}
} else {
FGEPoint pp1 = getP1();
FGEPoint pp2 = getP2();
if (pp1.y > pp2.y) {
pp1 = getP2();
pp2 = getP1();
}
if (projection.y >= pp1.y) {
if (projection.y >= pp2.y) {
return pp2;
} else {
return projection;
}
} else {
return pp1;
}
}
}
/**
* Creates a new object of the same class and with the same contents as this object.
*
* @return a clone of this instance.
* @exception OutOfMemoryError
* if there is not enough memory.
* @see java.lang.Cloneable
* @since 1.2
*/
@Override
public FGESegment clone() {
return (FGESegment) super.clone();
}
@Override
public FGESegment transform(AffineTransform t) {
return new FGESegment(getP1().transform(t), getP2().transform(t));
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FGESegment) {
FGESegment s = (FGESegment) obj;
if (!overlap(s)) {
return false;
}
return (getP1().equals(s.getP1()) || getP1().equals(s.getP2())) && (getP2().equals(s.getP1()) || getP2().equals(s.getP2()));
}
return false;
}
@Override
public String toString() {
return "FGESegment: [" + getP1() + "," + getP2() + "]";
}
@Override
public void paint(FGEGraphics g) {
g.useDefaultForegroundStyle();
g.drawLine(getP1(), getP2());
}
public double getLength() {
return Math.sqrt(getSqLength());
}
public double getSqLength() {
return (getP1().x - getP2().x) * (getP1().x - getP2().x) + (getP1().y - getP2().y) * (getP1().y - getP2().y);
}
public static double getLength(FGEPoint p1, FGEPoint p2) {
return new FGESegment(p1, p2).getLength();
}
@Override
protected FGEArea computeLineIntersection(FGEAbstractLine line) {
// logger.info("computeIntersection() between "+this+"\n and "+line+" overlap="+overlap(line));
if (overlap(line)) {
if (line instanceof FGEHalfLine) {
return _compute_hl_segment_Intersection((FGEHalfLine) line, this);
} else if (line instanceof FGESegment) {
return _compute_segment_segment_Intersection(this, (FGESegment) line);
} else {
return clone();
}
} else if (isParallelTo(line)) {
return new FGEEmptyArea();
} else {
FGEPoint returned;
try {
returned = getLineIntersection(line);
if (containsPoint(returned) && line.containsPoint(returned)) {
return returned;
}
} catch (ParallelLinesException e) {
// cannot happen
}
return new FGEEmptyArea();
}
}
private static FGEArea _compute_segment_segment_Intersection(FGESegment s1, FGESegment s2) {
if (s1.containsPoint(s2.getP1())) {
if (s1.containsPoint(s2.getP2())) {
return s2.clone();
} else {
if (s2.containsPoint(s1.getP1())) {
if (s1.getP1().equals(s2.getP1())) {
return s1.getP1().clone();
}
return new FGESegment(s1.getP1(), s2.getP1());
} else {
if (s2.getP1().equals(s1.getP2())) {
return s2.getP1().clone();
}
return new FGESegment(s2.getP1(), s1.getP2());
}
}
} else {
if (s1.containsPoint(s2.getP2())) {
if (s2.containsPoint(s1.getP1())) {
if (s1.getP1().equals(s2.getP2())) {
return s1.getP1().clone();
}
return new FGESegment(s1.getP1(), s2.getP2());
} else {
if (s2.getP2().equals(s1.getP2())) {
return s2.getP2().clone();
}
return new FGESegment(s2.getP2(), s1.getP2());
}
} else {
if (s2.containsPoint(s1.getP1()) && s2.containsPoint(s1.getP2())) {
return s1.clone();
} else {
return new FGEEmptyArea();
}
}
}
}
@Override
public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) {
FGEHalfLine hl1 = null;
FGEHalfLine hl2 = null;
FGEPoint ps1 = getP1();
FGEPoint ps2 = getP2();
if (orientation == SimplifiedCardinalDirection.NORTH) {
hl1 = new FGEHalfLine(ps1, new FGEPoint(ps1.x, ps1.y - 1));
hl2 = new FGEHalfLine(ps2, new FGEPoint(ps2.x, ps2.y - 1));
if (Math.abs(ps1.x - ps2.x) < EPSILON) {
return hl1;
}
} else if (orientation == SimplifiedCardinalDirection.SOUTH) {
hl1 = new FGEHalfLine(ps1, new FGEPoint(ps1.x, ps1.y + 1));
hl2 = new FGEHalfLine(ps2, new FGEPoint(ps2.x, ps2.y + 1));
if (Math.abs(ps1.x - ps2.x) < EPSILON) {
return hl1;
}
} else if (orientation == SimplifiedCardinalDirection.EAST) {
hl1 = new FGEHalfLine(ps1, new FGEPoint(ps1.x + 1, ps1.y));
hl2 = new FGEHalfLine(ps2, new FGEPoint(ps2.x + 1, ps2.y));
if (Math.abs(ps1.y - ps2.y) < EPSILON) {
return hl1;
}
} else if (orientation == SimplifiedCardinalDirection.WEST) {
hl1 = new FGEHalfLine(ps1, new FGEPoint(ps1.x - 1, ps1.y));
hl2 = new FGEHalfLine(ps2, new FGEPoint(ps2.x - 1, ps2.y));
if (Math.abs(ps1.y - ps2.y) < EPSILON) {
return hl1;
}
}
// System.out.println("Segment: "+this+" orientation="+orientation);
try {
return new FGEHalfBand(hl1, hl2);
} catch (IllegalArgumentException e) {
logger.warning("Could not obtain resulting half-band, hl1=" + hl1 + " hl2=" + hl2 + " orientation=" + orientation + " ps1="
+ ps1 + " ps2=" + ps2);
return new FGEEmptyArea();
}
}
@Override
public FGESegment getAnchorAreaFrom(SimplifiedCardinalDirection direction) {
return clone();
}
public FGEPoint getScaledPoint(double scale) {
FGEPoint p = new FGEPoint();
p.x = getP1().x + (getP2().x - getP1().x) * scale;
p.y = getP1().y + (getP2().y - getP1().y) * scale;
return p;
}
public double getRelativeLocation(FGEPoint p) {
FGEPoint proj = getNearestPointOnSegment(p);
if (Math.abs(getP2().x - getP1().x) < EPSILON) {
return (proj.y - getP1().y) / (getP2().y - getP1().y);
} else {
return (proj.x - getP1().x) / (getP2().x - getP1().x);
}
}
public static FGERectangle getBoundingBox(FGESegment... segments) {
FGEPolylin polylin = new FGEPolylin();
for (FGESegment s : segments) {
polylin.addToPoints(s.getP1());
polylin.addToPoints(s.getP2());
}
return polylin.getBoundingBox();
}
public SimplifiedCardinalDirection getOrientation() {
double angle = getAngle();
/**
* GPO: Angle will be between -PI/2 and 3PI/2 0-->WEST PI/2--> SOUTH -PI or PI-->EAST (Almost sure that -PI will never come but it
* does not cost too much so let's do it) -PI/2 or 3PI/2-->NORTH
*/
if (Math.abs(angle) < EPSILON) {
return SimplifiedCardinalDirection.WEST;
} else if (Math.abs(angle - Math.PI) < EPSILON || Math.abs(angle + Math.PI) < EPSILON) {
return SimplifiedCardinalDirection.EAST;
} else if (Math.abs(angle - Math.PI / 2) < EPSILON) {
return SimplifiedCardinalDirection.SOUTH;
} else if (Math.abs(angle - 3 * Math.PI / 2) < EPSILON || Math.abs(angle + Math.PI / 2) < EPSILON) {
return SimplifiedCardinalDirection.NORTH;
} else {
return null;
}
}
public SimplifiedCardinalDirection getApproximatedOrientation() {
SimplifiedCardinalDirection returned = getOrientation();
if (returned != null) {
return returned;
}
return FGEPoint.getSimplifiedOrientation(getP1(), getP2());
}
@Override
public final FGEArea union(FGEArea area) {
if (containsArea(area)) {
return clone();
}
if (area.containsArea(this)) {
return area.clone();
}
if (area instanceof FGEPolylin) {
return ((FGEPolylin) area).union(this);
}
if (area instanceof FGESegment) {
FGESegment s = (FGESegment) area;
if (s.containsLine(this)) {
return s;
}
if (containsLine(s)) {
return clone();
}
if (overlap(s)) {
if (containsPoint(s.getP1())) {
// Doesn't contains P2, otherwise all contained, see above
if (s.containsPoint(getP1())) {
return new FGESegment(getP2(), s.getP2());
}
if (s.containsPoint(getP2())) {
return new FGESegment(getP1(), s.getP2());
}
}
if (containsPoint(s.getP2())) {
// Doesn't contains P2, otherwise all contained, see above
if (s.containsPoint(getP1())) {
return new FGESegment(getP2(), s.getP1());
}
if (s.containsPoint(getP2())) {
return new FGESegment(getP1(), s.getP1());
}
}
}
if (getP1().equals(s.getP2())) {
return new FGEPolylin(getP2(), getP1(), s.getP1());
}
if (getP1().equals(s.getP1())) {
return new FGEPolylin(getP2(), getP1(), s.getP2());
}
if (getP2().equals(s.getP2())) {
return new FGEPolylin(getP1(), getP2(), s.getP1());
}
if (getP2().equals(s.getP1())) {
return new FGEPolylin(getP1(), getP2(), s.getP2());
}
return super.union(area);
}
return super.union(area);
}
/**
* This area is finite, so always return true
*/
@Override
public final boolean isFinite() {
return true;
}
/**
* This area is finite, so always return null
*/
@Override
public final FGERectangle getEmbeddingBounds() {
return new FGERectangle(getP1(), getP2(), Filling.FILLED);
}
}