/*
* (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.Color;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.fge.geom.area.DefaultAreaProvider;
import org.openflexo.fge.geom.area.FGEArea;
import org.openflexo.fge.geom.area.FGEAreaProvider;
import org.openflexo.fge.geom.area.FGEEmptyArea;
import org.openflexo.fge.geom.area.FGEUnionArea;
import org.openflexo.fge.graphics.BackgroundStyle;
import org.openflexo.fge.graphics.BackgroundStyle.Texture.TextureType;
import org.openflexo.fge.graphics.FGEGraphics;
import org.openflexo.fge.graphics.ForegroundStyle;
import org.openflexo.fge.graphics.ForegroundStyle.DashStyle;
public class FGERectPolylin extends FGEPolylin {
static final Logger logger = Logger.getLogger(FGERectPolylin.class.getPackage().getName());
private FGEArea startArea;
private SimplifiedCardinalDirection startOrientation;
private FGEArea endArea;
private SimplifiedCardinalDirection endOrientation;
private double overlapX = 10;
private double overlapY = 10;
private boolean straightWhenPossible = false;
private FGEArea resultingStartArea;
private FGEArea resultingEndArea;
// TODO: debug only remove this
protected static ForegroundStyle focusedForegroundStyle;
protected static BackgroundStyle focusedBackgroundStyle;
private boolean respectAllConstraints = true;
// TODO: debug only remove this
static {
focusedForegroundStyle = ForegroundStyle.makeStyle(Color.RED, 0.5f, DashStyle.MEDIUM_DASHES);
focusedBackgroundStyle = BackgroundStyle.makeTexturedBackground(TextureType.TEXTURE1, Color.RED, Color.WHITE);
focusedBackgroundStyle.setUseTransparency(true);
focusedBackgroundStyle.setTransparencyLevel(0.1f);
}
public FGERectPolylin() {
super();
}
public FGERectPolylin(List<FGEPoint> points) {
super(points);
}
public FGERectPolylin(FGEPoint... points) {
super(makeList(points));
}
private static List<FGEPoint> makeList(FGEPoint... points) {
Vector<FGEPoint> returned = new Vector<FGEPoint>();
for (FGEPoint pt : points) {
returned.add(pt);
}
return returned;
}
public FGERectPolylin(List<FGEPoint> points, boolean straightWhenPossible, double overlapX, double overlapY) {
super(points);
this.straightWhenPossible = straightWhenPossible;
if (overlapX < 0) {
logger.warning("Called FGERectPolylin with negative overlapX: " + overlapX);
overlapX = 0;
}
this.overlapX = overlapX;
if (overlapY < 0) {
logger.warning("Called FGERectPolylin with negative overlapY: " + overlapY);
overlapY = 0;
}
this.overlapY = overlapY;
}
/**
* Build and return a FGERectPolylin given supplied start and end orientation, and a bunch of parameters
*
* @param startArea
* @param startOrientation
* @param endArea
* @param endOrientation
* @param straightWhenPossible
* @param overlap
*/
public FGERectPolylin(FGEArea aStartArea, SimplifiedCardinalDirection startOrientation, FGEArea anEndArea,
SimplifiedCardinalDirection endOrientation, boolean straightWhenPossible, double overlapX, double overlapY) {
super();
this.startArea = aStartArea.clone();
if (startArea instanceof FGEShape) {
((FGEShape) startArea).setIsFilled(false);
}
this.startOrientation = startOrientation;
this.endArea = anEndArea.clone();
if (endArea instanceof FGEShape) {
((FGEShape) endArea).setIsFilled(false);
}
this.endOrientation = endOrientation;
this.straightWhenPossible = straightWhenPossible;
// logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> FGERectPolylin BEGIN");
if (overlapX < 0) {
logger.warning("Called FGERectPolylin with negative overlapX: " + overlapX);
overlapX = 0;
}
this.overlapX = overlapX;
if (overlapY < 0) {
logger.warning("Called FGERectPolylin with negative overlapY: " + overlapY);
overlapY = 0;
}
this.overlapY = overlapY;
computeResultingOrthogonalPerspectiveAreas();
restoreDefaultLayout();
respectAllConstraints = true;
if (getOrientationOfSegment(0) != startOrientation && overlapX > 0 && overlapY > 0 && getFirstSegment() != null
&& getFirstSegment().getLength() > 0) {
if (getOrientationOfSegment(0) != null) {
logger.info("Inconsistant start orientation invariant... " + getOrientationOfSegment(0) + " != " + startOrientation);
}
if (logger.isLoggable(Level.FINE)) {
logger.info("startArea=" + startArea);
logger.info("startOrientation=" + startOrientation);
logger.info("endArea=" + endArea);
logger.info("endOrientation=" + endOrientation);
logger.info("polylin=" + this);
}
respectAllConstraints = false;
}
if (endOrientation != null && getOrientationOfSegment(getSegmentNb() - 1) != endOrientation.getOpposite() && overlapX > 0
&& overlapY > 0 && getLastSegment() != null && getLastSegment().getLength() > 0) {
if (getOrientationOfSegment(getSegmentNb() - 1) != null) {
logger.info("Inconsistant end orientation invariant... " + getOrientationOfSegment(getSegmentNb() - 1) + " != "
+ endOrientation.getOpposite());
}
if (logger.isLoggable(Level.FINE)) {
logger.info("startArea=" + startArea);
logger.info("startOrientation=" + startOrientation);
logger.info("endArea=" + endArea);
logger.info("endOrientation=" + endOrientation);
logger.info("polylin=" + this);
}
respectAllConstraints = false;
}
if (getPointsNb() == 0) {
logger.info("Inconsistant invariant getPointsNb() == 0");
if (logger.isLoggable(Level.FINE)) {
logger.info("startArea=" + startArea);
logger.info("startOrientation=" + startOrientation);
logger.info("endArea=" + endArea);
logger.info("endOrientation=" + endOrientation);
logger.info("resultingStartArea=" + resultingStartArea);
logger.info("resultingEndArea=" + resultingEndArea);
}
respectAllConstraints = false;
}
// logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< FGERectPolylin END");
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions are examined, and best solution is returned: -
* 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEArea startArea, FGEArea endArea, boolean straightWhenPossible, double overlapX,
double overlapY) {
return makeShortestRectPolylin(startArea, endArea, straightWhenPossible, overlapX, overlapY,
(Vector<SimplifiedCardinalDirection>) null, (Vector<SimplifiedCardinalDirection>) null);
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned: - 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEArea startArea, FGEArea endArea, boolean straightWhenPossible, double overlapX,
double overlapY, SimplifiedCardinalDirection startOrientation, SimplifiedCardinalDirection endOrientation) {
return makeShortestRectPolylin(startArea, endArea, straightWhenPossible, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(startOrientation),
SimplifiedCardinalDirection.allDirectionsExcept(endOrientation));
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned: - 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEArea startArea, FGEArea endArea, boolean straightWhenPossible, double overlapX,
double overlapY, Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
int bestNbOfPoints = Integer.MAX_VALUE;
double bestLength = Double.POSITIVE_INFINITY;
for (SimplifiedCardinalDirection startOrientation : SimplifiedCardinalDirection.values()) {
if (excludedStartOrientations == null || !excludedStartOrientations.contains(startOrientation)) {
for (SimplifiedCardinalDirection endOrientation : SimplifiedCardinalDirection.values()) {
if (excludedEndOrientations == null || !excludedEndOrientations.contains(endOrientation)) {
FGERectPolylin tried = new FGERectPolylin(startArea, startOrientation, endArea, endOrientation,
straightWhenPossible, overlapX, overlapY);
if (tried.doesRespectAllConstraints() && tried.getPointsNb() < bestNbOfPoints) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
} else if (tried.doesRespectAllConstraints() && tried.getPointsNb() == bestNbOfPoints) {
if (tried.getLength() < bestLength) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
}
}
}
}
}
}
return returned;
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned regarding distance between returned polylin and supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEArea startArea, FGEArea endArea, boolean straightWhenPossible, double overlapX,
double overlapY, FGEPoint minimizeDistanceToThisPoint, Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
double bestDistance = Double.POSITIVE_INFINITY;
for (SimplifiedCardinalDirection startOrientation : SimplifiedCardinalDirection.values()) {
if (excludedStartOrientations == null || !excludedStartOrientations.contains(startOrientation)) {
for (SimplifiedCardinalDirection endOrientation : SimplifiedCardinalDirection.values()) {
if (excludedEndOrientations == null || !excludedEndOrientations.contains(endOrientation)) {
FGERectPolylin tried = new FGERectPolylin(startArea, startOrientation, endArea, endOrientation,
straightWhenPossible, overlapX, overlapY);
double distance = FGEPoint
.distance(tried.getNearestPoint(minimizeDistanceToThisPoint), minimizeDistanceToThisPoint);
if (tried.doesRespectAllConstraints() && distance < bestDistance) {
returned = tried;
bestDistance = distance;
}
}
}
}
}
return returned;
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions are
* examined, and best solution is returned regarding distance between returned polylin and supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEArea startArea, FGEArea endArea, FGEPoint crossedPoint,
boolean straightWhenPossible, double overlapX, double overlapY) {
return makeRectPolylinCrossingPoint(startArea, endArea, crossedPoint, straightWhenPossible, overlapX, overlapY, null, null);
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point, using supplied start and end
* orientation
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEArea startArea, FGEArea endArea, FGEPoint crossedPoint,
SimplifiedCardinalDirection startOrientation, SimplifiedCardinalDirection endOrientation, boolean straightWhenPossible,
double overlapX, double overlapY) {
return makeRectPolylinCrossingPoint(startArea, endArea, crossedPoint, straightWhenPossible, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(startOrientation),
SimplifiedCardinalDirection.allDirectionsExcept(endOrientation));
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions are examined, and best solution is returned: -
* 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startAreaProvider
* @param endAreaProvider
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, boolean straightWhenPossible, double overlapX, double overlapY) {
return makeShortestRectPolylin(startAreaProvider, endAreaProvider, straightWhenPossible, overlapX, overlapY,
(Vector<SimplifiedCardinalDirection>) null, (Vector<SimplifiedCardinalDirection>) null);
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned: - 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, boolean straightWhenPossible, double overlapX, double overlapY,
SimplifiedCardinalDirection startOrientation, SimplifiedCardinalDirection endOrientation) {
return makeShortestRectPolylin(startAreaProvider, endAreaProvider, straightWhenPossible, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(startOrientation),
SimplifiedCardinalDirection.allDirectionsExcept(endOrientation));
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned: - 1st: try to minimize number of points - 2nd: try to minimize total length of path
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, boolean straightWhenPossible, double overlapX, double overlapY,
Vector<SimplifiedCardinalDirection> excludedStartOrientations, Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
int bestNbOfPoints = Integer.MAX_VALUE;
double bestLength = Double.POSITIVE_INFINITY;
for (SimplifiedCardinalDirection startOrientation : SimplifiedCardinalDirection.values()) {
if (excludedStartOrientations == null || !excludedStartOrientations.contains(startOrientation)) {
FGEArea startArea = startAreaProvider.getArea(startOrientation);
if (startArea instanceof FGEEmptyArea) {
continue;
}
for (SimplifiedCardinalDirection endOrientation : SimplifiedCardinalDirection.values()) {
if (excludedEndOrientations == null || !excludedEndOrientations.contains(endOrientation)) {
FGEArea endArea = endAreaProvider.getArea(endOrientation);
if (endArea instanceof FGEEmptyArea) {
continue;
}
FGERectPolylin tried = new FGERectPolylin(startArea, startOrientation, endArea, endOrientation,
straightWhenPossible, overlapX, overlapY);
if (tried.doesRespectAllConstraints() && tried.getPointsNb() < bestNbOfPoints) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
} else if (tried.doesRespectAllConstraints() && tried.getPointsNb() == bestNbOfPoints) {
if (tried.getLength() < bestLength) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
}
}
}
}
}
}
return returned;
}
/**
* Return and build a FGERectPolylin, given some parameters. All orientation solutions (except those supplied as to be excluded) are
* examined, and best solution is returned regarding distance between returned polylin and supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeShortestRectPolylin(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, boolean straightWhenPossible, double overlapX, double overlapY,
FGEPoint minimizeDistanceToThisPoint, Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
double bestDistance = Double.POSITIVE_INFINITY;
for (SimplifiedCardinalDirection startOrientation : SimplifiedCardinalDirection.values()) {
if (excludedStartOrientations == null || !excludedStartOrientations.contains(startOrientation)) {
FGEArea startArea = startAreaProvider.getArea(startOrientation);
for (SimplifiedCardinalDirection endOrientation : SimplifiedCardinalDirection.values()) {
if (excludedEndOrientations == null || !excludedEndOrientations.contains(endOrientation)) {
FGEArea endArea = endAreaProvider.getArea(endOrientation);
FGERectPolylin tried = new FGERectPolylin(startArea, startOrientation, endArea, endOrientation,
straightWhenPossible, overlapX, overlapY);
double distance = FGEPoint
.distance(tried.getNearestPoint(minimizeDistanceToThisPoint), minimizeDistanceToThisPoint);
if (tried.doesRespectAllConstraints() && distance < bestDistance) {
returned = tried;
bestDistance = distance;
}
}
}
}
}
return returned;
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions are
* examined, and best solution is returned regarding distance between returned polylin and supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, FGEPoint crossedPoint, boolean straightWhenPossible,
double overlapX, double overlapY) {
return makeRectPolylinCrossingPoint(startAreaProvider, endAreaProvider, crossedPoint, straightWhenPossible, overlapX, overlapY,
null, null);
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point, using supplied start and end
* orientation
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, FGEPoint crossedPoint,
SimplifiedCardinalDirection startOrientation, SimplifiedCardinalDirection endOrientation, boolean straightWhenPossible,
double overlapX, double overlapY) {
return makeRectPolylinCrossingPoint(startAreaProvider, endAreaProvider, crossedPoint, straightWhenPossible, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(startOrientation),
SimplifiedCardinalDirection.allDirectionsExcept(endOrientation));
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions (except
* those supplied as to be excluded) are examined, and best solution is returned regarding distance between returned polylin and
* supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
/*public static FGERectPolylin makeRectPolylinCrossingPoint (
FGEArea startArea,
FGEArea endArea,
FGEPoint crossedPoint,
boolean straightWhenPossible,
double overlap,
Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations)
{
System.out.println("excludedStartOrientations="+excludedStartOrientations);
System.out.println("excludedEndOrientations="+excludedEndOrientations);
FGERectPolylin polylin = makeShortestRectPolylin(
startArea,
endArea,
straightWhenPossible,
overlap,
crossedPoint,
excludedStartOrientations,
excludedEndOrientations);
FGESegment projectionSegment = polylin.getProjectionSegment(crossedPoint);
if (projectionSegment == null) projectionSegment = polylin.getNearestSegment(crossedPoint);
SimplifiedCardinalDirection orientation = projectionSegment.getApproximatedOrientation();
Vector<SimplifiedCardinalDirection> polylin1ExcludedEndOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation.getOpposite());
Vector<SimplifiedCardinalDirection> polylin2ExcludedStartOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation);
FGERectPolylin polylin1 = makeShortestRectPolylin(
startArea,
crossedPoint,
straightWhenPossible,
0,
excludedStartOrientations,
polylin1ExcludedEndOrientations);
FGERectPolylin polylin2 = makeShortestRectPolylin(
crossedPoint,
endArea,
straightWhenPossible,
0,
polylin2ExcludedStartOrientations,
excludedEndOrientations);
FGERectPolylin returned = mergePolylins(polylin1, 0, polylin1.getPointsNb()-2, polylin2, 1, polylin2.getPointsNb()-1);
if (returned.hasExtraPoints()) returned.removeExtraPoints();
return returned;
}*/
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions (except
* those supplied as to be excluded) are examined, and best solution is returned regarding distance between returned polylin and
* supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEArea startArea, FGEArea endArea, FGEPoint crossedPoint,
boolean straightWhenPossible, double overlapX, double overlapY, Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
int bestNbOfPoints = Integer.MAX_VALUE;
double bestLength = Double.POSITIVE_INFINITY;
boolean isCurrentlyChoosenPolylinWithCrossedPointAsCorner = false;
SimplifiedCardinalDirection bestStartOrientation = null;
SimplifiedCardinalDirection bestMiddleOrientation = null;
SimplifiedCardinalDirection bestEndOrientation = null;
Hashtable<SimplifiedCardinalDirection, FGERectPolylin> polylins1 = new Hashtable<SimplifiedCardinalDirection, FGERectPolylin>();
Hashtable<SimplifiedCardinalDirection, FGERectPolylin> polylins2 = new Hashtable<SimplifiedCardinalDirection, FGERectPolylin>();
// Following regards performances optimization
// Replace further commented code
for (SimplifiedCardinalDirection orientation1 : SimplifiedCardinalDirection.values()) {
FGERectPolylin polylin = makeShortestRectPolylin(startArea, crossedPoint, true, overlapX, overlapY, excludedStartOrientations,
SimplifiedCardinalDirection.allDirectionsExcept(orientation1));
if (polylin != null) {
polylins1.put(orientation1, polylin);
}
}
for (SimplifiedCardinalDirection orientation2 : SimplifiedCardinalDirection.values()) {
FGERectPolylin polylin = makeShortestRectPolylin(crossedPoint, endArea, true, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(orientation2), excludedEndOrientations);
if (polylin != null) {
polylins2.put(orientation2, polylin);
}
}
for (SimplifiedCardinalDirection orientation1 : SimplifiedCardinalDirection.values()) {
for (SimplifiedCardinalDirection orientation2 : SimplifiedCardinalDirection.values()) {
if (orientation1 != orientation2) {
/*Vector<SimplifiedCardinalDirection> polylin1ExcludedEndOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation1);
Vector<SimplifiedCardinalDirection> polylin2ExcludedStartOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation2);
FGERectPolylin polylin1 = makeShortestRectPolylin(
startArea,
crossedPoint,
true,
overlapX,
overlapY,
excludedStartOrientations,
polylin1ExcludedEndOrientations);
FGERectPolylin polylin2 = makeShortestRectPolylin(
crossedPoint,
endArea,
true,
overlapX,
overlapY,
polylin2ExcludedStartOrientations,
excludedEndOrientations);*/
// Performances, see above
FGERectPolylin polylin1 = polylins1.get(orientation1);
FGERectPolylin polylin2 = polylins2.get(orientation2);
if (polylin1 != null && polylin1.doesRespectAllConstraints() && polylin2 != null
&& polylin2.doesRespectAllConstraints()) {
FGERectPolylin tried;
boolean cornerChoosen;
if (orientation1 == orientation2.getOpposite()) {
// In this case, crossedPoint is belonging to a segment
cornerChoosen = false;
tried = mergePolylins(polylin1, 0, polylin1.getPointsNb() - 2, polylin2, 1, polylin2.getPointsNb() - 1);
} else {
// In this case, crossedPoint is a corner, take all points of polylin1 and concatenate it
// with all points of polylin2 except the first one (which is also crossedPoint)
cornerChoosen = true;
tried = mergePolylins(polylin1, 0, polylin1.getPointsNb() - 1, polylin2, 1, polylin2.getPointsNb() - 1);
}
if (tried.hasExtraPoints()) {
tried.removeExtraPoints();
}
// First of all, a polylin not crossing itself is better than any other
if (returned != null && returned.crossedItSelf() && !tried.crossedItSelf()) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// Then, try to minimize number of points
else if (tried.getPointsNb() < bestNbOfPoints) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// Then, minimise total length
else if (tried.getPointsNb() == bestNbOfPoints) {
if (tried.getLength() < bestLength - EPSILON) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// In case of same length, try to choose layout where crossed point is a corner
else if (cornerChoosen && !isCurrentlyChoosenPolylinWithCrossedPointAsCorner) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
}
}
}
}
}
// logger.info(" Choosen polylin "+bestStartOrientation+","+bestMiddleOrientation+","+bestEndOrientation);
return returned;
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions (except
* those supplied as to be excluded) are examined, and best solution is returned regarding distance between returned polylin and
* supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
public static FGERectPolylin makeRectPolylinCrossingPoint(FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider,
FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider, FGEPoint crossedPoint, boolean straightWhenPossible,
double overlapX, double overlapY, Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations) {
FGERectPolylin returned = null;
int bestNbOfPoints = Integer.MAX_VALUE;
double bestLength = Double.POSITIVE_INFINITY;
boolean isCurrentlyChoosenPolylinWithCrossedPointAsCorner = false;
SimplifiedCardinalDirection bestStartOrientation = null;
SimplifiedCardinalDirection bestMiddleOrientation = null;
SimplifiedCardinalDirection bestEndOrientation = null;
Hashtable<SimplifiedCardinalDirection, FGERectPolylin> polylins1 = new Hashtable<SimplifiedCardinalDirection, FGERectPolylin>();
Hashtable<SimplifiedCardinalDirection, FGERectPolylin> polylins2 = new Hashtable<SimplifiedCardinalDirection, FGERectPolylin>();
// Following regards performances optimization
// Replace further commented code
DefaultAreaProvider<SimplifiedCardinalDirection> crossedPointAreaProvider = new DefaultAreaProvider<SimplifiedCardinalDirection>(
crossedPoint);
for (SimplifiedCardinalDirection orientation1 : SimplifiedCardinalDirection.values()) {
FGERectPolylin polylin = makeShortestRectPolylin(startAreaProvider, crossedPointAreaProvider, true, overlapX, overlapY,
excludedStartOrientations, SimplifiedCardinalDirection.allDirectionsExcept(orientation1));
if (polylin != null) {
polylins1.put(orientation1, polylin);
}
}
for (SimplifiedCardinalDirection orientation2 : SimplifiedCardinalDirection.values()) {
FGERectPolylin polylin = makeShortestRectPolylin(crossedPointAreaProvider, endAreaProvider, true, overlapX, overlapY,
SimplifiedCardinalDirection.allDirectionsExcept(orientation2), excludedEndOrientations);
if (polylin != null) {
polylins2.put(orientation2, polylin);
}
}
for (SimplifiedCardinalDirection orientation1 : SimplifiedCardinalDirection.values()) {
for (SimplifiedCardinalDirection orientation2 : SimplifiedCardinalDirection.values()) {
if (orientation1 != orientation2) {
/*Vector<SimplifiedCardinalDirection> polylin1ExcludedEndOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation1);
Vector<SimplifiedCardinalDirection> polylin2ExcludedStartOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation2);
FGERectPolylin polylin1 = makeShortestRectPolylin(
startArea,
crossedPoint,
true,
overlapX,
overlapY,
excludedStartOrientations,
polylin1ExcludedEndOrientations);
FGERectPolylin polylin2 = makeShortestRectPolylin(
crossedPoint,
endArea,
true,
overlapX,
overlapY,
polylin2ExcludedStartOrientations,
excludedEndOrientations);*/
// Performances, see above
FGERectPolylin polylin1 = polylins1.get(orientation1);
FGERectPolylin polylin2 = polylins2.get(orientation2);
if (polylin1 != null && polylin1.doesRespectAllConstraints() && polylin2 != null
&& polylin2.doesRespectAllConstraints()) {
FGERectPolylin tried;
boolean cornerChoosen;
if (orientation1 == orientation2.getOpposite()) {
// In this case, crossedPoint is belonging to a segment
cornerChoosen = false;
tried = mergePolylins(polylin1, 0, polylin1.getPointsNb() - 2, polylin2, 1, polylin2.getPointsNb() - 1);
} else {
// In this case, crossedPoint is a corner, take all points of polylin1 and concatenate it
// with all points of polylin2 except the first one (which is also crossedPoint)
cornerChoosen = true;
tried = mergePolylins(polylin1, 0, polylin1.getPointsNb() - 1, polylin2, 1, polylin2.getPointsNb() - 1);
}
if (tried.hasExtraPoints()) {
tried.removeExtraPoints();
}
// First of all, a polylin not crossing itself is better than any other
if (returned != null && returned.crossedItSelf() && !tried.crossedItSelf()) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// Then, try to minimize number of points
else if (tried.getPointsNb() < bestNbOfPoints) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// Then, minimise total length
else if (tried.getPointsNb() == bestNbOfPoints) {
if (tried.getLength() < bestLength - EPSILON) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
// In case of same length, try to choose layout where crossed point is a corner
else if (cornerChoosen && !isCurrentlyChoosenPolylinWithCrossedPointAsCorner) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
isCurrentlyChoosenPolylinWithCrossedPointAsCorner = cornerChoosen;
bestStartOrientation = polylin1.getStartOrientation();
bestMiddleOrientation = polylin1.getEndOrientation();
bestEndOrientation = polylin2.getEndOrientation();
}
}
}
}
}
}
// logger.info(" Choosen polylin "+bestStartOrientation+","+bestMiddleOrientation+","+bestEndOrientation);
return returned;
}
/**
* Return and build a FGERectPolylin linking a start and an end area, and crossing supplied point. All orientation solutions (except
* those supplied as to be excluded) are examined, and best solution is returned regarding distance between returned polylin and
* supplied point
*
* @param startArea
* @param endArea
* @param straightWhenPossible
* @param overlap
*/
/*public static FGERectPolylin makeRectPolylinCrossingPoint (
FGEArea startArea,
FGEArea endArea,
FGEPoint crossedPoint,
boolean straightWhenPossible,
double overlapX,
double overlapY,
Vector<SimplifiedCardinalDirection> excludedStartOrientations,
Vector<SimplifiedCardinalDirection> excludedEndOrientations)
{
FGERectPolylin returned = null;
int bestNbOfPoints = Integer.MAX_VALUE;
double bestLength = Double.POSITIVE_INFINITY;
for (SimplifiedCardinalDirection orientation : SimplifiedCardinalDirection.values()) {
Vector<SimplifiedCardinalDirection> polylin1ExcludedEndOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation.getOpposite());
Vector<SimplifiedCardinalDirection> polylin2ExcludedStartOrientations
= SimplifiedCardinalDirection.allDirectionsExcept(orientation);
FGERectPolylin polylin1 = makeShortestRectPolylin(
startArea,
crossedPoint,
true,
overlapX,
overlapY,
excludedStartOrientations,
polylin1ExcludedEndOrientations);
FGERectPolylin polylin2 = makeShortestRectPolylin(
crossedPoint,
endArea,
true,
overlapX,
overlapY,
polylin2ExcludedStartOrientations,
excludedEndOrientations);
FGERectPolylin tried = mergePolylins(polylin1, 0, polylin1.getPointsNb()-2, polylin2, 1, polylin2.getPointsNb()-1);
if (tried.hasExtraPoints()) tried.removeExtraPoints();
if (returned != null && returned.crossedItSelf() && !tried.crossedItSelf()) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
}
else if (tried.getPointsNb() < bestNbOfPoints) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
}
else if (tried.getPointsNb() == bestNbOfPoints) {
if (tried.getLength() < bestLength-EPSILON) {
returned = tried;
bestNbOfPoints = tried.getPointsNb();
bestLength = tried.getLength();
}
}
}
return returned;
}
*/
public boolean crossedItSelf() {
for (FGESegment s1 : getSegments()) {
for (FGESegment s2 : getSegments()) {
if (s1 != s2
&& (s1.overlap(s2) || s1.intersectsInsideSegment(s2) && !s1.getLineIntersection(s2).equals(s1.getP1())
&& !s1.getLineIntersection(s2).equals(s1.getP2()) && !s1.getLineIntersection(s2).equals(s2.getP1())
&& !s1.getLineIntersection(s2).equals(s2.getP2()))) {
return true;
}
}
}
return false;
}
/**
* Merge polylin given supplied polylin and indexes. Ignore extra points...
*
* @param p1
* @param startIndex1
* @param endIndex1
* @param p2
* @param startIndex2
* @param endIndex2
* @return
*/
public static FGERectPolylin mergePolylins(FGERectPolylin p1, int startIndex1, int endIndex1, FGERectPolylin p2, int startIndex2,
int endIndex2) {
FGEPoint previous = null;
FGERectPolylin returned = new FGERectPolylin();
returned.setOverlapX(p1.getOverlapX());
returned.setOverlapY(p1.getOverlapY());
for (int i = startIndex1; i <= endIndex1; i++) {
if (previous != null && previous.equals(p1.getPointAt(i))) {
// System.out.println("ignore point: "+previous);
} else {
returned.addToPoints(p1.getPointAt(i));
}
previous = p1.getPointAt(i);
}
for (int i = startIndex2; i <= endIndex2; i++) {
if (previous != null && previous.equals(p2.getPointAt(i))) {
// System.out.println("ignore point: "+previous);
} else {
returned.addToPoints(p2.getPointAt(i));
}
previous = p2.getPointAt(i);
}
return returned;
}
private void computeResultingOrthogonalPerspectiveAreas() {
if (startOrientation != null) {
resultingStartArea = startArea.getOrthogonalPerspectiveArea(startOrientation);
} else {
resultingStartArea = new FGEEmptyArea();
}
if (endOrientation != null) {
resultingEndArea = endArea.getOrthogonalPerspectiveArea(endOrientation);
// FGEHalfPlane north2 = new FGEHalfPlane(FGELine.makeHorizontalLine(new FGEPoint(0,getMinYFor(endArea))),new
// FGEPoint(0,getMinYFor(endArea)-1));
/*System.out.println("resultingEndArea="+resultingEndArea);
System.out.println("north2="+north2);
System.out.println("result="+resultingEndArea.intersect(north2));*/
// resultingEndArea = resultingEndArea.intersect(north2);
} else {
resultingEndArea = new FGEEmptyArea();
}
/*if (startArea instanceof FGEPoint) {
resultingStartArea = ((FGEPoint)startArea).getOrthogonalPerspectiveArea(startOrientation);
}
else if (startArea instanceof FGESegment) {
resultingStartArea = ((FGESegment)startArea).getOrthogonalPerspectiveArea(startOrientation);
}
else if (startArea instanceof FGERectangle) {
resultingStartArea = ((FGERectangle)startArea).getOrthogonalPerspectiveArea(startOrientation);
}
else {
logger.warning("What to do with a "+startArea+" ?");
}
if (endArea instanceof FGEPoint) {
resultingEndArea = ((FGEPoint)endArea).getOrthogonalPerspectiveArea(endOrientation);
}
else if (endArea instanceof FGESegment) {
resultingEndArea = ((FGESegment)endArea).getOrthogonalPerspectiveArea(endOrientation);
}
else if (endArea instanceof FGERectangle) {
resultingEndArea = ((FGERectangle)endArea).getOrthogonalPerspectiveArea(endOrientation);
}
else {
logger.warning("What to do with a "+endArea+" ?");
}*/
}
public void debugPaint(FGEGraphics g) {
ForegroundStyle fg = g.getDefaultForeground();
BackgroundStyle bg = g.getDefaultBackground();
g.setDefaultForeground(focusedForegroundStyle);
g.setDefaultBackground(focusedBackgroundStyle);
if (resultingStartArea != null) {
resultingStartArea.paint(g);
}
if (resultingEndArea != null) {
resultingEndArea.paint(g);
}
g.setDefaultForeground(fg);
g.setDefaultBackground(bg);
super.paint(g);
getMiddle().paint(g);
}
public void paintWithRounds(FGEGraphics g, int arcSize) {
g.useDefaultForegroundStyle();
FGEPoint arcP1 = g.getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(new Point(0, 0), 1.0);
FGEPoint arcP2 = g.getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(new Point(arcSize, arcSize), 1.0);
double requestedArcWidth = arcP2.x - arcP1.x;
double requestedArcHeight = arcP2.y - arcP1.y;
FGEPoint current = null;
for (int i = 0; i < _segments.size(); i++) {
FGESegment s = _segments.get(i);
FGESegment next = i < _segments.size() - 1 ? _segments.get(i + 1) : null;
if (next == null) {
if (current == null) {
s.paint(g);
} else {
new FGESegment(current, s.getP2()).paint(g);
}
} else {
FGEPoint p = s.getP2();
SimplifiedCardinalDirection currentOrientation;
SimplifiedCardinalDirection nextOrientation;
double angleStart = 0;
boolean clockWise = false;
FGEPoint circleCenter = null;
boolean displayArc;
double arcWidth = requestedArcWidth;
double arcHeight = requestedArcHeight;
// Prevent rounded radius exceed half of segment length
double arcRatio = requestedArcWidth / requestedArcHeight;
if (s.isVertical() && next.isHorizontal()) {
if (s.getLength() < requestedArcHeight * 2) {
arcHeight = s.getLength() / 2;
arcWidth = arcHeight * arcRatio;
}
if (next.getLength() < arcWidth * 2) {
arcWidth = next.getLength() / 2;
arcHeight = arcWidth / arcRatio;
}
} else if (s.isHorizontal() && next.isVertical()) {
if (s.getLength() < requestedArcWidth * 2) {
arcWidth = s.getLength() / 2;
arcHeight = arcWidth / arcRatio;
}
if (next.getLength() < arcHeight * 2) {
arcHeight = next.getLength() / 2;
arcWidth = arcHeight * arcRatio;
}
}
if (s.isVertical() && next.isHorizontal()) {
displayArc = true;
if (next.getP1().x < next.getP2().x) {
if (s.getP1().y < s.getP2().y) {
currentOrientation = SimplifiedCardinalDirection.SOUTH;
nextOrientation = SimplifiedCardinalDirection.EAST;
circleCenter = new FGEPoint(p.x + arcWidth, p.y - arcHeight);
angleStart = -180;
clockWise = false;
} else {
currentOrientation = SimplifiedCardinalDirection.NORTH;
nextOrientation = SimplifiedCardinalDirection.EAST;
circleCenter = new FGEPoint(p.x + arcWidth, p.y + arcHeight);
angleStart = 90;
clockWise = true;
}
} else {
if (s.getP1().y < s.getP2().y) {
currentOrientation = SimplifiedCardinalDirection.SOUTH;
nextOrientation = SimplifiedCardinalDirection.WEST;
circleCenter = new FGEPoint(p.x - arcWidth, p.y - arcHeight);
angleStart = -90;
clockWise = true;
} else {
currentOrientation = SimplifiedCardinalDirection.NORTH;
nextOrientation = SimplifiedCardinalDirection.WEST;
circleCenter = new FGEPoint(p.x - arcWidth, p.y + arcHeight);
angleStart = 0;
clockWise = false;
}
}
} else if (s.isHorizontal() && next.isVertical()) {
displayArc = true;
if (next.getP1().y < next.getP2().y) {
if (s.getP1().x < s.getP2().x) {
currentOrientation = SimplifiedCardinalDirection.EAST;
nextOrientation = SimplifiedCardinalDirection.SOUTH;
circleCenter = new FGEPoint(p.x - arcWidth, p.y + arcHeight);
angleStart = 0;
clockWise = true;
} else {
currentOrientation = SimplifiedCardinalDirection.WEST;
nextOrientation = SimplifiedCardinalDirection.SOUTH;
circleCenter = new FGEPoint(p.x + arcWidth, p.y + arcHeight);
angleStart = 90;
clockWise = false;
}
} else {
if (s.getP1().x < s.getP2().x) {
currentOrientation = SimplifiedCardinalDirection.EAST;
nextOrientation = SimplifiedCardinalDirection.NORTH;
circleCenter = new FGEPoint(p.x - arcWidth, p.y - arcHeight);
angleStart = -90;
clockWise = false;
} else {
currentOrientation = SimplifiedCardinalDirection.WEST;
nextOrientation = SimplifiedCardinalDirection.NORTH;
circleCenter = new FGEPoint(p.x + arcWidth, p.y - arcHeight);
angleStart = -180;
clockWise = true;
}
}
} else {
logger.warning("Unexpected situation while drawing rounded RectPolylin connectors");
displayArc = false;
// return;
}
if (displayArc) {
FGEArc arc = new FGEArc(circleCenter, new FGEDimension(arcWidth * 2, arcHeight * 2), angleStart, 90);
FGEPoint startRound = arc.getPointAtAngle(clockWise ? angleStart + 90 : angleStart);
FGEPoint endRound = arc.getPointAtAngle(clockWise ? angleStart : angleStart + 90);
// DEBUG
/*g.setDefaultForeground(ForegroundStyle.makeStyle(Color.CYAN));
(new FGEEllips(circleCenter,new FGEDimension(arcWidth*2,arcHeight*2),Filling.NOT_FILLED)).paint(g);
startRound.paint(g);
endRound.paint(g);*/
g.useDefaultForegroundStyle();
if (current == null) {
new FGESegment(s.getP1(), startRound).paint(g);
} else {
new FGESegment(current, startRound).paint(g);
}
arc.paint(g);
current = endRound;
} else {
// For some reasons (for example 2 continuous colinear segments)
// cannot display round
g.useDefaultForegroundStyle();
s.paint(g);
current = s.getP2();
}
}
}
/*for (FGESegment s : _segments) {
s.paint(g);
}*/
}
private void restoreDefaultLayout() {
clearPoints();
/*FGEHalfPlane north1 = new FGEHalfPlane(FGELine.makeHorizontalLine(new FGEPoint(0,getMinYFor(startArea))),new FGEPoint(0,
getMinYFor(startArea)-1));
//resultingStartArea = resultingStartArea.intersect(north1);
FGEHalfPlane north2 = new FGEHalfPlane(FGELine.makeHorizontalLine(new FGEPoint(0,getMinYFor(endArea))),new FGEPoint(0,
getMinYFor(endArea)-1));
System.out.println("resultingEndArea="+resultingEndArea);
System.out.println("north2="+north2);
System.out.println("result="+resultingEndArea.intersect(north2));
resultingEndArea = resultingEndArea.intersect(north2);*/
FGEArea intersect = resultingStartArea.intersect(resultingEndArea);
/*logger.info("startOrientation="+startOrientation+" endOrientation="+endOrientation);
logger.info("resultingStartArea="+resultingStartArea);
logger.info("resultingEndArea="+resultingEndArea);
logger.info("Intersect="+intersect);*/
if (intersect instanceof FGEPoint) {
FGEPoint p = (FGEPoint) intersect;
// FGEPoint p_start = startArea.getNearestPoint(p);
// FGEPoint p_end = endArea.getNearestPoint(p);
FGEPoint p_start = startOrientation.isHorizontal() ? nearestPointOnHorizontalLine(p, startArea) : nearestPointOnVerticalLine(p,
startArea);
FGEPoint p_end = endOrientation.isHorizontal() ? nearestPointOnHorizontalLine(p, endArea) : nearestPointOnVerticalLine(p,
endArea);
addToPoints(p_start);
addToPoints(p);
addToPoints(p_end);
return;
} else if (intersect instanceof FGESegment) {
FGEPoint p = ((FGESegment) intersect).getMiddle();
// FGEPoint p_start = startArea.getNearestPoint(p);
// FGEPoint p_end = endArea.getNearestPoint(p);
FGEPoint p_start = startOrientation.isHorizontal() ? nearestPointOnHorizontalLine(p, startArea) : nearestPointOnVerticalLine(p,
startArea);
FGEPoint p_end = endOrientation.isHorizontal() ? nearestPointOnHorizontalLine(p, endArea) : nearestPointOnVerticalLine(p,
endArea);
if (FGEPoint.areAligned(p_start, p, p_end) && straightWhenPossible) {
addToPoints(p_start);
addToPoints(p_end);
return;
} else {
addToPoints(p_start);
addToPoints(p);
addToPoints(p_end);
return;
}
} else if (intersect instanceof FGEShape || intersect.isFinite() && intersect.getEmbeddingBounds() != null) {
FGEPoint center;
/*logger.info("startOrientation="+startOrientation);
logger.info("endOrientation="+endOrientation);
logger.info("startArea="+startArea);
logger.info("endArea="+endArea);
logger.info("resultingStartArea="+resultingStartArea);
logger.info("resultingEndArea="+resultingEndArea);
logger.info("Shape is "+intersect);*/
if (intersect instanceof FGEShape) {
center = ((FGEShape) intersect).getCenter();
} else { // intersect is finite with non-null bounds
center = intersect.getEmbeddingBounds().getCenter();
}
FGEPoint p_start = startOrientation.isHorizontal() ? nearestPointOnHorizontalLine(center, startArea)
: nearestPointOnVerticalLine(center, startArea);
FGEPoint p_end = endOrientation.isHorizontal() ? nearestPointOnHorizontalLine(center, endArea) : nearestPointOnVerticalLine(
center, endArea);
// FGEPoint p_start = FGEPoint.getNearestPoint(center,startArea.nearestPointFrom(center,
// startOrientation.getOpposite()),startArea.nearestPointFrom(center, startOrientation));
// FGEPoint p_end = FGEPoint.getNearestPoint(center,endArea.nearestPointFrom(center,
// endOrientation.getOpposite()),endArea.nearestPointFrom(center, endOrientation));
/*logger.info("p_start="+p_start);
logger.info("p_end="+p_end);
logger.info("startArea="+startArea);
logger.info("endArea="+endArea);
logger.info("center="+center);*/
/*if (p_start == null) {
p_start = startArea.getNearestPoint(center);
logger.warning("Cound not find nearest point on start area along axis, selecting nearest.");
}
if (p_end == null) {
p_end = endArea.getNearestPoint(center);
logger.warning("Cound not find nearest point on end area along axis, selecting nearest.");
}*/
// This test is added to handle cases where intersection is disjointed.
// In this case, center can be outside resulting areas, and causes
// orientations not to be correct
if (resultingStartArea.containsPoint(center) && resultingEndArea.containsPoint(center)) {
if (FGEPoint.areAligned(p_start, center, p_end) && straightWhenPossible) {
addToPoints(p_start);
addToPoints(p_end);
return;
} else {
addToPoints(p_start);
addToPoints(center);
addToPoints(p_end);
return;
}
}
}
// logger.info("*********** For "+startOrientation+"/"+endOrientation+" CONTINUE");
if (startOrientation == SimplifiedCardinalDirection.EAST && endOrientation == SimplifiedCardinalDirection.EAST) {
restoreDefaultLayoutForEastEast();
} else if (startOrientation == SimplifiedCardinalDirection.WEST && endOrientation == SimplifiedCardinalDirection.WEST) {
restoreDefaultLayoutForWestWest();
} else if (startOrientation == SimplifiedCardinalDirection.NORTH && endOrientation == SimplifiedCardinalDirection.NORTH) {
restoreDefaultLayoutForNorthNorth();
} else if (startOrientation == SimplifiedCardinalDirection.SOUTH && endOrientation == SimplifiedCardinalDirection.SOUTH) {
restoreDefaultLayoutForSouthSouth();
} else if (startOrientation == SimplifiedCardinalDirection.EAST && endOrientation == SimplifiedCardinalDirection.WEST) {
restoreDefaultLayoutForEastWest();
} else if (startOrientation == SimplifiedCardinalDirection.SOUTH && endOrientation == SimplifiedCardinalDirection.NORTH) {
restoreDefaultLayoutForSouthNorth();
} else if (startOrientation == SimplifiedCardinalDirection.EAST && endOrientation == SimplifiedCardinalDirection.NORTH) {
restoreDefaultLayoutForEastNorth();
} else if (startOrientation == SimplifiedCardinalDirection.EAST && endOrientation == SimplifiedCardinalDirection.SOUTH) {
restoreDefaultLayoutForEastSouth();
} else if (startOrientation == SimplifiedCardinalDirection.WEST && endOrientation == SimplifiedCardinalDirection.NORTH) {
restoreDefaultLayoutForWestNorth();
} else if (startOrientation == SimplifiedCardinalDirection.WEST && endOrientation == SimplifiedCardinalDirection.SOUTH) {
restoreDefaultLayoutForWestSouth();
} else if (startOrientation == SimplifiedCardinalDirection.WEST && endOrientation == SimplifiedCardinalDirection.EAST) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else if (startOrientation == SimplifiedCardinalDirection.NORTH && endOrientation == SimplifiedCardinalDirection.SOUTH) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else if (startOrientation == SimplifiedCardinalDirection.NORTH && endOrientation == SimplifiedCardinalDirection.EAST) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else if (startOrientation == SimplifiedCardinalDirection.SOUTH && endOrientation == SimplifiedCardinalDirection.EAST) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else if (startOrientation == SimplifiedCardinalDirection.NORTH && endOrientation == SimplifiedCardinalDirection.WEST) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else if (startOrientation == SimplifiedCardinalDirection.SOUTH && endOrientation == SimplifiedCardinalDirection.WEST) {
computeAs(new FGERectPolylin(endArea, endOrientation, startArea, startOrientation, straightWhenPossible, overlapX, overlapY));
} else {
logger.warning("Unexpected case: startOrientation=" + startOrientation + " endOrientation=" + endOrientation);
new Exception("???").printStackTrace();
}
}
private void computeAs(FGERectPolylin poly) {
for (int i = poly.getPointsNb() - 1; i >= 0; i--) {
addToPoints(poly.getPointAt(i));
}
}
private FGEPoint nearestPointOnHorizontalLine(FGEPoint p, FGEArea area) {
FGEPoint returned = FGEPoint.getNearestPoint(p, area.nearestPointFrom(p, SimplifiedCardinalDirection.EAST),
area.nearestPointFrom(p, SimplifiedCardinalDirection.WEST));
if (returned == null) {
returned = area.getNearestPoint(p);
logger.warning("Cound not find nearest point on area along horizontal axis, selecting nearest, area=" + area);
}
return returned;
}
private FGEPoint nearestPointOnVerticalLine(FGEPoint p, FGEArea area) {
FGEPoint returned = FGEPoint.getNearestPoint(p, area.nearestPointFrom(p, SimplifiedCardinalDirection.NORTH),
area.nearestPointFrom(p, SimplifiedCardinalDirection.SOUTH));
if (returned == null) {
returned = area.getNearestPoint(p);
logger.warning("Cound not find nearest point on area along horizontal axis, selecting nearest, area=" + area);
}
return returned;
}
private void restoreDefaultLayoutForEastEast() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForEastEast()");
}
FGELine line = FGELine.makeVerticalLine(new FGEPoint(Math.max(getMaxXFor(startArea), getMaxXFor(endArea)) + overlapX, 0));
FGEPoint p1 = getSignificativeAnchorAreaLocationFor(resultingStartArea.intersect(line), SimplifiedCardinalDirection.EAST);
FGEPoint p2 = getSignificativeAnchorAreaLocationFor(resultingEndArea.intersect(line), SimplifiedCardinalDirection.EAST);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnHorizontalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
private void restoreDefaultLayoutForWestWest() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForWestWest()");
}
FGELine line = FGELine.makeVerticalLine(new FGEPoint(Math.min(getMinXFor(startArea), getMinXFor(endArea)) - overlapX, 0));
FGEPoint p1 = getSignificativeAnchorAreaLocationFor(resultingStartArea.intersect(line), SimplifiedCardinalDirection.WEST);
FGEPoint p2 = getSignificativeAnchorAreaLocationFor(resultingEndArea.intersect(line), SimplifiedCardinalDirection.WEST);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnHorizontalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
private void restoreDefaultLayoutForNorthNorth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForNorthNorth()");
}
FGELine line = FGELine.makeHorizontalLine(new FGEPoint(0, Math.min(getMinYFor(startArea), getMinYFor(endArea)) - overlapY));
FGEPoint p1 = getSignificativeAnchorAreaLocationFor(resultingStartArea.intersect(line), SimplifiedCardinalDirection.NORTH);
FGEPoint p2 = getSignificativeAnchorAreaLocationFor(resultingEndArea.intersect(line), SimplifiedCardinalDirection.NORTH);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnVerticalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
private void restoreDefaultLayoutForSouthSouth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForSouthSouth()");
}
FGELine line = FGELine.makeHorizontalLine(new FGEPoint(0, Math.max(getMaxYFor(startArea), getMaxYFor(endArea)) + overlapY));
FGEPoint p1 = getSignificativeAnchorAreaLocationFor(resultingStartArea.intersect(line), SimplifiedCardinalDirection.SOUTH);
FGEPoint p2 = getSignificativeAnchorAreaLocationFor(resultingEndArea.intersect(line), SimplifiedCardinalDirection.SOUTH);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnVerticalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
private void restoreDefaultLayoutForEastWest() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForEastWest()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.EAST);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.WEST);
FGEArea startAnchorArea = startArea.getAnchorAreaFrom(SimplifiedCardinalDirection.EAST);
FGEArea endAnchorArea = endArea.getAnchorAreaFrom(SimplifiedCardinalDirection.WEST);
if (getMaxXFor(startAnchorArea) > getMinXFor(endAnchorArea)) {
// if (getMinXFor(startAnchorArea) > getMaxXFor(endAnchorArea)) { /* XXX */
double middleY = significativeStartLocation.y <= significativeEndLocation.y ? (getMaxYFor(startArea) + getMinYFor(endArea)) / 2
: (getMinYFor(startArea) + getMaxYFor(endArea)) / 2;
FGELine line = FGELine.makeHorizontalLine(new FGEPoint(0, middleY));
// FGELine line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startAnchorArea)+overlapX,0));
// FGELine line2 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(endAnchorArea)-overlapX,0));
FGELine line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startAnchorArea) + overlapX, 0));
FGELine line2 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(endAnchorArea) - overlapX, 0));
FGEPoint p1 = getLocationFor(resultingStartArea.intersect(line1));
FGEPoint p2 = line1.getLineIntersection(line);
FGEPoint p3 = line2.getLineIntersection(line);
FGEPoint p4 = getLocationFor(resultingEndArea.intersect(line2));
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p4);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnHorizontalLine(p4, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p4);
addToPoints(p_end);
} else {
FGELine line = FGELine.makeVerticalLine(new FGEPoint(
significativeStartLocation.x <= significativeEndLocation.x ? (getMaxXFor(startArea) + getMinXFor(endArea)) / 2
: (getMinXFor(startArea) + getMaxXFor(endArea)) / 2, 0));
FGEPoint p1 = getLocationFor(resultingStartArea.intersect(line));
FGEPoint p2 = getLocationFor(resultingEndArea.intersect(line));
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnHorizontalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
}
private void restoreDefaultLayoutForSouthNorth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForSouthNorth()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.SOUTH);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.NORTH);
FGEArea startAnchorArea = startArea.getAnchorAreaFrom(SimplifiedCardinalDirection.SOUTH);
FGEArea endAnchorArea = endArea.getAnchorAreaFrom(SimplifiedCardinalDirection.NORTH);
if (getMaxYFor(startAnchorArea) > getMinYFor(endAnchorArea)) {
// if (getMinYFor(startAnchorArea) > getMaxYFor(endAnchorArea)) { /* XXX */
double middleX = significativeStartLocation.x <= significativeEndLocation.x ? (getMaxXFor(startArea) + getMinXFor(endArea)) / 2
: (getMinXFor(startArea) + getMaxXFor(endArea)) / 2;
FGELine line = FGELine.makeVerticalLine(new FGEPoint(middleX, 0));
// FGELine line1 = FGELine.makeHorizontalLine(new FGEPoint(0,getMinYFor(startAnchorArea)+overlapY)); /* XXX */
// FGELine line2 = FGELine.makeHorizontalLine(new FGEPoint(0,getMaxYFor(endAnchorArea)-overlapY)); /* XXX */
FGELine line1 = FGELine.makeHorizontalLine(new FGEPoint(0, getMaxYFor(startAnchorArea) + overlapY));
FGELine line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMinYFor(endAnchorArea) - overlapY));
FGEPoint p1 = getLocationFor(resultingStartArea.intersect(line1));
FGEPoint p2 = line1.getLineIntersection(line);
FGEPoint p3 = line2.getLineIntersection(line);
FGEPoint p4 = getLocationFor(resultingEndArea.intersect(line2));
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p4);
FGEPoint p_start = nearestPointOnVerticalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p4, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p4);
addToPoints(p_end);
} else {
FGELine line = FGELine.makeHorizontalLine(new FGEPoint(0,
significativeStartLocation.y <= significativeEndLocation.y ? (getMaxYFor(startArea) + getMinYFor(endArea)) / 2
: (getMinYFor(startArea) + getMaxYFor(endArea)) / 2));
FGELine alternativeLine = FGELine.makeHorizontalLine(new FGEPoint(0,
(significativeStartLocation.y + significativeEndLocation.y) / 2));
FGEArea i1 = resultingStartArea.intersect(line);
FGEArea i2 = resultingEndArea.intersect(line);
if (i1 instanceof FGEEmptyArea) {
i1 = resultingStartArea.intersect(alternativeLine);
}
if (i2 instanceof FGEEmptyArea) {
i2 = resultingEndArea.intersect(alternativeLine);
}
if (i1 instanceof FGEEmptyArea) {
logger.warning("Unexpected empty area for intersection: " + resultingStartArea + " and " + alternativeLine);
}
if (i2 instanceof FGEEmptyArea) {
logger.warning("Unexpected empty area for intersection: " + resultingEndArea + " and " + alternativeLine);
}
FGEPoint p1 = getLocationFor(i1);
FGEPoint p2 = getLocationFor(i2);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p2);
FGEPoint p_start = nearestPointOnVerticalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p2, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p_end);
}
}
private void restoreDefaultLayoutForEastNorth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForEastNorth()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.EAST);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.NORTH);
/*System.out.println("startArea="+startArea);
System.out.println("endArea"+endArea);
System.out.println("significativeStartLocation="+significativeStartLocation);
System.out.println("significativeEndLocation"+significativeEndLocation);*/
CardinalQuadrant quadrant = FGEPoint.getCardinalQuadrant(significativeStartLocation, significativeEndLocation);
FGELine line1 = null;
FGELine line2 = null;
FGELine alternativeLine1 = null;
FGELine alternativeLine2 = null;
boolean useAlternativeLine1 = false;
boolean useAlternativeLine2 = false;
if (quadrant == CardinalQuadrant.SOUTH_EAST) {
logger.warning("Unexpected call to restoreDefaultForEastNorth() while quadrant is SOUTH_EAST");
return;
} else if (quadrant == CardinalQuadrant.NORTH_EAST) {
useAlternativeLine1 = getMaxXFor(startArea) > getMinXFor(endArea);
line1 = FGELine.makeVerticalLine(new FGEPoint((getMaxXFor(startArea) + getMinXFor(endArea)) / 2, 0));
alternativeLine1 = FGELine.makeVerticalLine(new FGEPoint((significativeStartLocation.x + significativeEndLocation.x) / 2, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMinYFor(endArea) - overlapY));
} else if (quadrant == CardinalQuadrant.SOUTH_WEST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startArea) + overlapX, 0));
useAlternativeLine2 = getMaxYFor(startArea) > getMinYFor(endArea);
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, (getMaxYFor(startArea) + getMinYFor(endArea)) / 2));
alternativeLine2 = FGELine.makeHorizontalLine(new FGEPoint(0, (significativeStartLocation.y + significativeEndLocation.y) / 2));
} else if (quadrant == CardinalQuadrant.NORTH_WEST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startArea) + overlapX, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMinYFor(endArea) - overlapY));
}
if (useAlternativeLine1) {
line1 = alternativeLine1;
}
if (useAlternativeLine2) {
line2 = alternativeLine2;
}
FGEArea i1 = resultingStartArea.intersect(line1);
FGEArea i2 = resultingEndArea.intersect(line2);
if (i1 instanceof FGEEmptyArea && alternativeLine1 != null) {
// Special case, use alternative line 1
line1 = alternativeLine1;
i1 = resultingStartArea.intersect(line1);
}
if (i2 instanceof FGEEmptyArea && alternativeLine2 != null) {
// Special case, use alternative line 1
line2 = alternativeLine2;
i2 = resultingEndArea.intersect(line2);
}
FGEPoint p1 = getLocationFor(i1);
FGEPoint p2 = line1.getLineIntersection(line2);
FGEPoint p3 = getLocationFor(i2);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p3);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p3, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p_end);
}
private void restoreDefaultLayoutForEastSouth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForEastSouth()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.EAST);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.SOUTH);
CardinalQuadrant quadrant = FGEPoint.getCardinalQuadrant(significativeStartLocation, significativeEndLocation);
FGELine line1 = null;
FGELine line2 = null;
FGELine alternativeLine1 = null;
FGELine alternativeLine2 = null;
boolean useAlternativeLine1 = false;
boolean useAlternativeLine2 = false;
if (quadrant == CardinalQuadrant.NORTH_EAST) {
logger.warning("Unexpected call to restoreDefaultForEastSouth() while quadrant is NORTH_EAST");
return;
} else if (quadrant == CardinalQuadrant.SOUTH_EAST) {
useAlternativeLine1 = getMaxXFor(startArea) > getMinXFor(endArea);
line1 = FGELine.makeVerticalLine(new FGEPoint((getMaxXFor(startArea) + getMinXFor(endArea)) / 2, 0));
alternativeLine1 = FGELine.makeVerticalLine(new FGEPoint((significativeStartLocation.x + significativeEndLocation.x) / 2, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMaxYFor(endArea) + overlapY));
} else if (quadrant == CardinalQuadrant.NORTH_WEST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startArea) + overlapX, 0));
useAlternativeLine2 = getMinYFor(startArea) < getMaxYFor(endArea);
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, (getMinYFor(startArea) + getMaxYFor(endArea)) / 2));
alternativeLine2 = FGELine.makeHorizontalLine(new FGEPoint(0, (significativeStartLocation.y + significativeEndLocation.y) / 2));
} else if (quadrant == CardinalQuadrant.SOUTH_WEST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMaxXFor(startArea) + overlapX, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMaxYFor(endArea) + overlapY));
}
if (useAlternativeLine1) {
line1 = alternativeLine1;
}
if (useAlternativeLine2) {
line2 = alternativeLine2;
}
FGEArea i1 = resultingStartArea.intersect(line1);
FGEArea i2 = resultingEndArea.intersect(line2);
if (i1 instanceof FGEEmptyArea && alternativeLine1 != null) {
// Special case, use alternative line 1
line1 = alternativeLine1;
i1 = resultingStartArea.intersect(line1);
}
if (i2 instanceof FGEEmptyArea && alternativeLine2 != null) {
// Special case, use alternative line 1
line2 = alternativeLine2;
i2 = resultingEndArea.intersect(line2);
}
FGEPoint p1 = getLocationFor(i1);
FGEPoint p2 = line1.getLineIntersection(line2);
FGEPoint p3 = getLocationFor(i2);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p3);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p3, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p_end);
}
private void restoreDefaultLayoutForWestNorth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForWestNorth()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.WEST);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.NORTH);
CardinalQuadrant quadrant = FGEPoint.getCardinalQuadrant(significativeStartLocation, significativeEndLocation);
FGELine line1 = null;
FGELine line2 = null;
FGELine alternativeLine1 = null;
FGELine alternativeLine2 = null;
boolean useAlternativeLine1 = false;
boolean useAlternativeLine2 = false;
if (quadrant == CardinalQuadrant.SOUTH_WEST) {
logger.warning("Unexpected call to restoreDefaultForWestNorth() while quadrant is SOUTH_WEST");
return;
} else if (quadrant == CardinalQuadrant.NORTH_WEST) {
useAlternativeLine1 = getMinXFor(startArea) < getMaxXFor(endArea);
line1 = FGELine.makeVerticalLine(new FGEPoint((getMinXFor(startArea) + getMaxXFor(endArea)) / 2, 0));
alternativeLine1 = FGELine.makeVerticalLine(new FGEPoint((significativeStartLocation.x + significativeEndLocation.x) / 2, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMinYFor(endArea) - overlapY));
} else if (quadrant == CardinalQuadrant.SOUTH_EAST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(startArea) - overlapX, 0));
useAlternativeLine2 = getMaxYFor(startArea) > getMinYFor(endArea);
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, (getMaxYFor(startArea) + getMinYFor(endArea)) / 2));
alternativeLine2 = FGELine.makeHorizontalLine(new FGEPoint(0, (significativeStartLocation.y + significativeEndLocation.y) / 2));
} else if (quadrant == CardinalQuadrant.NORTH_EAST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(startArea) - overlapX, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMinYFor(endArea) - overlapY));
}
if (useAlternativeLine1) {
line1 = alternativeLine1;
}
if (useAlternativeLine2) {
line2 = alternativeLine2;
}
FGEArea i1 = resultingStartArea.intersect(line1);
FGEArea i2 = resultingEndArea.intersect(line2);
if (i1 instanceof FGEEmptyArea && alternativeLine1 != null) {
// Special case, use alternative line 1
line1 = alternativeLine1;
i1 = resultingStartArea.intersect(line1);
}
if (i2 instanceof FGEEmptyArea && alternativeLine2 != null) {
// Special case, use alternative line 1
line2 = alternativeLine2;
i2 = resultingEndArea.intersect(line2);
}
FGEPoint p1 = getLocationFor(i1);
FGEPoint p2 = line1.getLineIntersection(line2);
FGEPoint p3 = getLocationFor(i2);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p3);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p3, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p_end);
}
private void restoreDefaultLayoutForWestSouth() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("restoreDefaultForWestSouth()");
}
FGEPoint significativeStartLocation = getSignificativeAnchorAreaLocationFor(startArea, SimplifiedCardinalDirection.WEST);
FGEPoint significativeEndLocation = getSignificativeAnchorAreaLocationFor(endArea, SimplifiedCardinalDirection.SOUTH);
CardinalQuadrant quadrant = FGEPoint.getCardinalQuadrant(significativeStartLocation, significativeEndLocation);
FGELine line1 = null;
FGELine line2 = null;
FGELine alternativeLine1 = null;
FGELine alternativeLine2 = null;
boolean useAlternativeLine1 = false;
boolean useAlternativeLine2 = false;
if (quadrant == CardinalQuadrant.NORTH_WEST) {
logger.warning("Unexpected call to restoreDefaultForWestSouth() while quadrant is NORTH_WEST");
return;
} else if (quadrant == CardinalQuadrant.SOUTH_WEST) {
useAlternativeLine1 = getMinXFor(startArea) < getMaxXFor(endArea);
line1 = FGELine.makeVerticalLine(new FGEPoint((getMinXFor(startArea) + getMaxXFor(endArea)) / 2, 0));
alternativeLine1 = FGELine.makeVerticalLine(new FGEPoint((significativeStartLocation.x + significativeEndLocation.x) / 2, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMaxYFor(endArea) + overlapY));
} else if (quadrant == CardinalQuadrant.NORTH_EAST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(startArea) - overlapX, 0));
useAlternativeLine2 = getMinYFor(startArea) < getMaxYFor(endArea);
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, (getMinYFor(startArea) + getMaxYFor(endArea)) / 2));
alternativeLine2 = FGELine.makeHorizontalLine(new FGEPoint(0, (significativeStartLocation.y + significativeEndLocation.y) / 2));
} else if (quadrant == CardinalQuadrant.SOUTH_EAST) {
line1 = FGELine.makeVerticalLine(new FGEPoint(getMinXFor(startArea) - overlapX, 0));
line2 = FGELine.makeHorizontalLine(new FGEPoint(0, getMaxYFor(endArea) + overlapY));
}
if (useAlternativeLine1) {
line1 = alternativeLine1;
}
if (useAlternativeLine2) {
line2 = alternativeLine2;
}
FGEArea i1 = resultingStartArea.intersect(line1);
FGEArea i2 = resultingEndArea.intersect(line2);
if (i1 instanceof FGEEmptyArea && alternativeLine1 != null) {
// Special case, use alternative line 1
line1 = alternativeLine1;
i1 = resultingStartArea.intersect(line1);
}
if (i2 instanceof FGEEmptyArea && alternativeLine2 != null) {
// Special case, use alternative line 1
line2 = alternativeLine2;
i2 = resultingEndArea.intersect(line2);
}
FGEPoint p1 = getLocationFor(i1);
FGEPoint p2 = line1.getLineIntersection(line2);
FGEPoint p3 = getLocationFor(i2);
// FGEPoint p_start = startArea.getNearestPoint(p1);
// FGEPoint p_end = endArea.getNearestPoint(p3);
FGEPoint p_start = nearestPointOnHorizontalLine(p1, startArea);
FGEPoint p_end = nearestPointOnVerticalLine(p3, endArea);
addToPoints(p_start);
addToPoints(p1);
addToPoints(p2);
addToPoints(p3);
addToPoints(p_end);
}
private FGEPoint getLocationFor(FGEArea a) {
if (a instanceof FGEEmptyArea) {
logger.warning("Unexpected empty area while computing getLocationFor(FGEArea)");
}
if (a instanceof FGEPoint) {
return ((FGEPoint) a).clone();
}
if (a instanceof FGESegment) {
return ((FGESegment) a).getMiddle();
}
if (a instanceof FGEPolylin) {
return ((FGEPolylin) a).getMiddle();
}
if (a instanceof FGEArc) {
return ((FGEArc) a).getMiddle();
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfPoints()) {
return (FGEPoint) ((FGEUnionArea) a).getObjects().firstElement();
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfFiniteGeometricObjects()) {
return a.getNearestPoint(a.getEmbeddingBounds().getCenter());
}
if (a.isFinite() && !(a instanceof FGEEmptyArea)) {
FGERectangle r = a.getEmbeddingBounds();
if (r != null) {
return r.getCenter();
}
}
logger.warning("Unexpected " + a);
if (logger.isLoggable(Level.INFO)) {
logger.info("Infos:" + "\nstartArea=" + startArea + "\nstartOrientation=" + startOrientation + "\nendArea=" + endArea
+ "\nendOrientation=" + endOrientation + "\noverlapX=" + overlapX + "\noverlapY=" + overlapY);
}
return new FGEPoint(0, 0);
}
private FGEPoint getSignificativeAnchorAreaLocationFor(FGEArea a, SimplifiedCardinalDirection direction) {
FGEArea anchorArea = a.getAnchorAreaFrom(direction);
/*if (anchorArea instanceof FGEUnionArea && ((FGEUnionArea)anchorArea).isUnionOfSegments()) {
anchorArea = ((FGEUnionArea)anchorArea).getObjects().firstElement();
}
logger.info("Anchor area for "+a+" is "+anchorArea);*/
if (anchorArea instanceof FGEPoint) {
return ((FGEPoint) anchorArea).clone();
}
if (anchorArea instanceof FGESegment) {
return ((FGESegment) anchorArea).getMiddle();
}
if (anchorArea instanceof FGEPolylin) {
return ((FGEPolylin) anchorArea).getMiddle();
}
if (anchorArea instanceof FGEArc) {
return ((FGEArc) anchorArea).getMiddle();
}
if (anchorArea instanceof FGEShape) {
return getSignificativeAnchorAreaLocationFor(((FGEShape) anchorArea).getBoundingBox(), direction);
}
if (anchorArea instanceof FGEUnionArea && ((FGEUnionArea) anchorArea).isUnionOfPoints()) {
return getSignificativeAnchorAreaLocationFor(((FGEUnionArea) anchorArea).getObjects().firstElement(), direction);
}
if (anchorArea instanceof FGEUnionArea && ((FGEUnionArea) anchorArea).isUnionOfFiniteGeometricObjects()) {
return anchorArea.getNearestPoint(anchorArea.getEmbeddingBounds().getCenter());
}
logger.warning("Unexpected " + anchorArea + " for " + a);
return new FGEPoint(0, 0);
}
private double getMaxXFor(FGEArea a) {
if (a instanceof FGEPoint) {
return ((FGEPoint) a).x;
}
if (a instanceof FGESegment) {
return Math.max(((FGESegment) a).getP1().x, ((FGESegment) a).getP2().x);
}
if (a instanceof FGEShape) {
return ((FGEShape) a).getBoundingBox().x + ((FGEShape) a).getBoundingBox().width;
}
if (a instanceof FGEPolylin) {
return ((FGEPolylin) a).getBoundingBox().x + ((FGEPolylin) a).getBoundingBox().width;
}
if (a instanceof FGEArc) {
return ((FGEArc) a).getBoundingBox().x + ((FGEArc) a).getBoundingBox().width;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfPoints()) {
double returned = Double.NEGATIVE_INFINITY;
for (FGEArea p : ((FGEUnionArea) a).getObjects()) {
FGEPoint pt = (FGEPoint) p;
if (pt.x > returned) {
returned = pt.x;
}
}
return returned;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfFiniteGeometricObjects()) {
return a.getEmbeddingBounds().getMaxX();
}
logger.warning("Unexpected " + a);
return 0;
}
private double getMinXFor(FGEArea a) {
if (a instanceof FGEPoint) {
return ((FGEPoint) a).x;
}
if (a instanceof FGESegment) {
return Math.min(((FGESegment) a).getP1().x, ((FGESegment) a).getP2().x);
}
if (a instanceof FGEShape) {
return ((FGEShape) a).getBoundingBox().x;
}
if (a instanceof FGEPolylin) {
return ((FGEPolylin) a).getBoundingBox().x;
}
if (a instanceof FGEArc) {
return ((FGEArc) a).getBoundingBox().x;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfPoints()) {
double returned = Double.POSITIVE_INFINITY;
for (FGEArea p : ((FGEUnionArea) a).getObjects()) {
FGEPoint pt = (FGEPoint) p;
if (pt.x < returned) {
returned = pt.x;
}
}
return returned;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfFiniteGeometricObjects()) {
return a.getEmbeddingBounds().getMinX();
}
logger.warning("Unexpected " + a);
return 0;
}
private double getMaxYFor(FGEArea a) {
if (a instanceof FGEPoint) {
return ((FGEPoint) a).y;
}
if (a instanceof FGESegment) {
return Math.max(((FGESegment) a).getP1().y, ((FGESegment) a).getP2().y);
}
if (a instanceof FGEShape) {
return ((FGEShape) a).getBoundingBox().y + ((FGEShape) a).getBoundingBox().height;
}
if (a instanceof FGEPolylin) {
return ((FGEPolylin) a).getBoundingBox().y + ((FGEPolylin) a).getBoundingBox().height;
}
if (a instanceof FGEArc) {
return ((FGEArc) a).getBoundingBox().y + ((FGEArc) a).getBoundingBox().height;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfPoints()) {
double returned = Double.NEGATIVE_INFINITY;
for (FGEArea p : ((FGEUnionArea) a).getObjects()) {
FGEPoint pt = (FGEPoint) p;
if (pt.y > returned) {
returned = pt.y;
}
}
return returned;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfFiniteGeometricObjects()) {
return a.getEmbeddingBounds().getMaxY();
}
logger.warning("Unexpected " + a);
return 0;
}
private double getMinYFor(FGEArea a) {
if (a instanceof FGEPoint) {
return ((FGEPoint) a).y;
}
if (a instanceof FGESegment) {
return Math.min(((FGESegment) a).getP1().y, ((FGESegment) a).getP2().y);
}
if (a instanceof FGEShape) {
return ((FGEShape) a).getBoundingBox().y;
}
if (a instanceof FGEPolylin) {
return ((FGEPolylin) a).getBoundingBox().y;
}
if (a instanceof FGEArc) {
return ((FGEArc) a).getBoundingBox().y + ((FGEArc) a).getBoundingBox().height;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfPoints()) {
double returned = Double.POSITIVE_INFINITY;
for (FGEArea p : ((FGEUnionArea) a).getObjects()) {
FGEPoint pt = (FGEPoint) p;
if (pt.y < returned) {
returned = pt.y;
}
}
return returned;
}
if (a instanceof FGEUnionArea && ((FGEUnionArea) a).isUnionOfFiniteGeometricObjects()) {
return a.getEmbeddingBounds().getMinY();
}
logger.warning("Unexpected " + a);
return 0;
}
public SimplifiedCardinalDirection getEndOrientation() {
if (endOrientation == null && getSegmentNb() > 0) {
endOrientation = getApproximatedOrientationOfSegment(getSegments().size() - 1);
}
return endOrientation;
}
public SimplifiedCardinalDirection getStartOrientation() {
if (startOrientation == null && getSegmentNb() > 0) {
startOrientation = getApproximatedOrientationOfSegment(0);
}
return startOrientation;
}
/**
* Return the segment on which middle is located
*
* @return
*/
public FGESegment getMiddleSegment() {
double middleDistancePath = getLength() / 2;
double distance = 0;
for (FGESegment s : getSegments()) {
if (distance <= middleDistancePath && distance + s.getLength() >= middleDistancePath) {
return s;
}
distance += s.getLength();
}
logger.warning("Unexpected situation while computing middle segment of rect polylin");
return null;
}
public FGEPoint getNearestPointLocatedOnRoundedRepresentation(FGEPoint p, double arcWidth, double arcHeight) {
FGEArc arc = getArcForNearestPointLocatedOnRoundedRepresentation(p, arcWidth, arcHeight);
if (arc == null) {
return getNearestPoint(p); // Point located outside arc
} else {
return arc.getNearestPoint(p);
}
}
public FGEArc getArcForNearestPointLocatedOnRoundedRepresentation(FGEPoint p, double arcWidth, double arcHeight) {
FGEPoint point = getNearestPoint(p);
FGESegment segment = getProjectionSegment(point);
// FGEPoint point = segment.getNearestPointOnSegment(p);
int segmentId = getSegmentIndex(segment);
SimplifiedCardinalDirection orientation = getApproximatedOrientationOfSegment(segmentId);
FGEArc arc = null;
FGEArc startArc = null;
FGEArc endArc = null;
if (segmentId > 0) {
FGESegment previousSegment = getSegmentAt(segmentId - 1);
startArc = getRoundedArc(previousSegment, segment, arcWidth, arcHeight);
}
if (segmentId < getSegmentNb() - 1) {
FGESegment nextSegment = getSegmentAt(segmentId + 1);
endArc = getRoundedArc(segment, nextSegment, arcWidth, arcHeight);
}
if (orientation == SimplifiedCardinalDirection.NORTH) {
if (segment.y1 - point.y < arcHeight) {
arc = startArc;
}
if (point.y - segment.y2 < arcHeight) {
arc = endArc;
}
}
if (orientation == SimplifiedCardinalDirection.SOUTH) {
if (point.y - segment.y1 < arcHeight) {
arc = startArc;
}
if (segment.y2 - point.y < arcHeight) {
arc = endArc;
}
}
if (orientation == SimplifiedCardinalDirection.WEST) {
if (segment.x1 - point.x < arcWidth) {
arc = startArc;
}
if (point.x - segment.x2 < arcWidth) {
arc = endArc;
}
}
if (orientation == SimplifiedCardinalDirection.EAST) {
if (point.x - segment.x1 < arcWidth) {
arc = startArc;
}
if (segment.x2 - point.x < arcWidth) {
arc = endArc;
}
}
return arc;
}
protected FGEArc getRoundedArc(FGESegment s1, FGESegment s2, double arcWidth, double arcHeight) {
if (s1 == null || s2 == null) {
return null;
}
SimplifiedCardinalDirection orientation1 = s1.getApproximatedOrientation();
SimplifiedCardinalDirection orientation2 = s2.getApproximatedOrientation();
// Prevent rounded radius exceed half of segment length
double arcRatio = arcWidth / arcHeight;
if (s1.isVertical() && s2.isHorizontal()) {
if (s1.getLength() < arcHeight * 2) {
arcHeight = s1.getLength() / 2;
arcWidth = arcHeight * arcRatio;
}
if (s2.getLength() < arcWidth * 2) {
arcWidth = s2.getLength() / 2;
arcHeight = arcWidth / arcRatio;
}
} else if (s1.isHorizontal() && s2.isVertical()) {
if (s1.getLength() < arcWidth * 2) {
arcWidth = s1.getLength() / 2;
arcHeight = arcWidth / arcRatio;
}
if (s2.getLength() < arcHeight * 2) {
arcHeight = s2.getLength() / 2;
arcWidth = arcHeight * arcRatio;
}
}
if (orientation1 == SimplifiedCardinalDirection.NORTH) {
if (orientation2 == SimplifiedCardinalDirection.EAST) {
return new FGEArc(new FGEPoint(s1.x2 + arcWidth, s1.y2 + arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), 180,
-90);
}
if (orientation2 == SimplifiedCardinalDirection.WEST) {
return new FGEArc(new FGEPoint(s1.x2 - arcWidth, s1.y2 + arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), 0, 90);
}
return null;
}
if (orientation1 == SimplifiedCardinalDirection.SOUTH) {
if (orientation2 == SimplifiedCardinalDirection.EAST) {
return new FGEArc(new FGEPoint(s1.x2 + arcWidth, s1.y2 - arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), -180,
90);
}
if (orientation2 == SimplifiedCardinalDirection.WEST) {
return new FGEArc(new FGEPoint(s1.x2 - arcWidth, s1.y2 - arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), 0, -90);
}
return null;
}
if (orientation1 == SimplifiedCardinalDirection.EAST) {
if (orientation2 == SimplifiedCardinalDirection.NORTH) {
return new FGEArc(new FGEPoint(s1.x2 - arcWidth, s1.y2 - arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), -90, 90);
}
if (orientation2 == SimplifiedCardinalDirection.SOUTH) {
return new FGEArc(new FGEPoint(s1.x2 - arcWidth, s1.y2 + arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), 90, -90);
}
return null;
}
if (orientation1 == SimplifiedCardinalDirection.WEST) {
if (orientation2 == SimplifiedCardinalDirection.NORTH) {
return new FGEArc(new FGEPoint(s1.x2 + arcWidth, s1.y2 - arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), -90,
-90);
}
if (orientation2 == SimplifiedCardinalDirection.SOUTH) {
return new FGEArc(new FGEPoint(s1.x2 + arcWidth, s1.y2 + arcHeight), new FGEDimension(arcWidth * 2, arcHeight * 2), 90, 90);
}
return null;
}
return null;
}
/**
* Return the nearest segment (if any) on which supplied point p might be projected
*
* @return the related segment, or null if no such segment has been found
*/
public FGESegment getProjectionSegment(FGEPoint p) {
double shortestDistance = Double.POSITIVE_INFINITY;
FGESegment returned = null;
for (FGESegment s : getSegments()) {
if (s.projectionIntersectsInsideSegment(p)) {
double distance = FGEPoint.distance(p, s.getProjection(p));
if (distance <= shortestDistance) {
returned = s;
shortestDistance = distance;
}
}
}
return returned;
}
public SimplifiedCardinalDirection getOrientationOfSegment(int index) {
FGESegment segment = getSegmentAt(index);
if (segment != null) {
return segment.getOrientation();
}
return null;
/*double angle = segment.getAngle();
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) return SimplifiedCardinalDirection.NORTH;
else return null;*/
}
public SimplifiedCardinalDirection getApproximatedOrientationOfSegment(int index) {
FGESegment segment = getSegmentAt(index);
if (segment != null) {
return segment.getApproximatedOrientation();
}
return null;
/*SimplifiedCardinalDirection returned = getOrientationOfSegment(index);
if (returned != null) return returned;
FGESegment segment = getSegmentAt(index);
return FGEPoint.getSimplifiedOrientation(segment.getP1(), segment.getP2());*/
}
@Override
public FGERectPolylin transform(AffineTransform t) {
Vector<FGEPoint> points = new Vector<FGEPoint>();
for (FGEPoint p : _points) {
points.add(p.transform(t));
}
FGERectPolylin returned = new FGERectPolylin(points);
return returned;
}
@Override
public FGERectPolylin clone() {
// return (FGERectPolylin)super.clone();
FGERectPolylin returned = new FGERectPolylin();
returned.overlapX = overlapX;
returned.overlapY = overlapY;
returned.straightWhenPossible = straightWhenPossible;
for (FGEPoint p : getPoints()) {
returned.addToPoints(p.clone());
}
return returned;
}
/**
* Return a flag indicating wether this polylin is normalized or not A rect polylin is normalized when: - all segments are rect (their
* orientations matches cardinal orientation NORTH, EAST, SOUTH, WEST) - no extra points are found (no point located inside a segment,
* which means that no colinear adjascent segment were found)
*
* @return true if this polylin is normalized, false otherwise
*/
public boolean isNormalized() {
return !hasNonRectSegments() && !hasExtraPoints();
}
/**
* Return a flag indicating wether this polylin has non-rect segment (their orientations doesn't matches cardinal orientation NORTH,
* EAST, SOUTH, WEST)
*
* @return
*/
public boolean hasNonRectSegments() {
for (int i = 0; i < getSegmentNb(); i++) {
if (getOrientationOfSegment(i) == null) {
return true;
}
}
return false;
}
/**
* Return a flag indicating wether this polylin has extra point (no point located inside a segment, which means that no colinear
* adjascent segment were found)
*
* @return true if this polylin has extra points, false otherwise
*/
public boolean hasExtraPoints() {
return hasEqualsAdjascentPoints() || hasColinearAdjascentSegments();
}
// Check for 2 equal adjascent point
private boolean hasEqualsAdjascentPoints() {
FGEPoint previous = null;
for (FGEPoint p : getPoints()) {
if (previous != null && previous.equals(p)) {
return true;
}
previous = p;
}
return false;
}
// Remove point, if any
private void removeFirstEqualsAdjascentPoints() {
FGEPoint previous = null;
for (int i = 0; i < getPointsNb(); i++) {
if (previous != null && previous.equals(getPointAt(i))) {
removePointAtIndex(i);
return;
}
previous = getPointAt(i);
}
}
// Check for 2 colinear adjascent segments
private boolean hasColinearAdjascentSegments() {
FGESegment previous_s = null;
for (FGESegment s : getSegments()) {
if (previous_s != null && previous_s.getApproximatedOrientation().equals(s.getApproximatedOrientation())) {
return true;
}
previous_s = s;
}
return false;
}
// Remove point, if any
private void removeFirstColinearAdjascentSegments() {
FGESegment previous_s = null;
for (int i = 0; i < getSegmentNb(); i++) {
FGESegment s = getSegmentAt(i);
if (previous_s != null && previous_s.getApproximatedOrientation().equals(s.getApproximatedOrientation())) {
removePointAtIndex(i);
return;
}
previous_s = s;
}
}
public void removeExtraPoints() {
/*logger.info("removeExtraPoints()");
for (FGEPoint p : getPoints()) {
System.out.println("WAS:"+p);
}
for (FGESegment s : getSegments()) {
System.out.println("WAS:"+s);
}*/
while (hasEqualsAdjascentPoints()) {
removeFirstEqualsAdjascentPoints();
}
while (hasColinearAdjascentSegments()) {
removeFirstColinearAdjascentSegments();
}
/*for (FGEPoint p : getPoints()) {
System.out.println("NOW:"+p);
}
for (FGESegment s : getSegments()) {
System.out.println("NOW:"+s);
}*/
}
public boolean isNormalizable() {
if (isNormalized()) {
return true;
}
FGERectPolylin normalizedRect = makeNormalizedRectPolylin();
return normalizedRect.getPointsNb() == getPointsNb();
}
public void normalize() {
if (isNormalized()) {
return;
}
FGERectPolylin normalizedRect = makeNormalizedRectPolylin();
if (normalizedRect.getPointsNb() != getPointsNb()) {
throw new IllegalArgumentException("Cannot normalize a non-normalizable rect");
}
for (int i = 0; i < getPointsNb(); i++) {
updatePointAt(i, normalizedRect.getPointAt(i));
}
}
/**
* Build and return a normalized rect polylin given this polylin
*
* @return
*/
public FGERectPolylin makeNormalizedRectPolylin() {
FGERectPolylin updatedPolylin = clone();
// Now iterate both sides
/*FGEPoint*/
currentPointStartingSide = updatedPolylin.getPointAt(0);
/*FGEPoint*/
currentPointEndingSide = updatedPolylin.getPointAt(updatedPolylin.getPointsNb() - 1);
int currentStartIndex = 0;
int currentEndIndex = updatedPolylin.getPointsNb() - 1;
for (int i = 1; i < getPointsNb() / 2; i++) {
// Starting side
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Checking point at " + i);
}
currentStartIndex = i;
FGEPoint previousPointStartingSide = updatedPolylin.getPointAt(i - 1);
currentPointStartingSide = updatedPolylin.getPointAt(i);
SimplifiedCardinalDirection orientation = updatedPolylin.getApproximatedOrientationOfSegment(i - 1);
if (orientation.isVertical()) {
if (currentPointStartingSide.x != previousPointStartingSide.x) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Updating point at " + i + " for vertical orientation");
}
currentPointStartingSide.x = previousPointStartingSide.x;
updatedPolylin.updatePointAt(i, currentPointStartingSide);
}
} else if (orientation.isHorizontal()) {
if (currentPointStartingSide.y != previousPointStartingSide.y) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Updating point at " + i + " for horizontal orientation");
}
currentPointStartingSide.y = previousPointStartingSide.y;
updatedPolylin.updatePointAt(i, currentPointStartingSide);
}
}
// Ending side
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Checking point at " + (updatedPolylin.getPointsNb() - i - 1));
}
currentEndIndex = updatedPolylin.getPointsNb() - i - 1;
FGEPoint previousPointEndingSide = updatedPolylin.getPointAt(updatedPolylin.getPointsNb() - i);
currentPointEndingSide = updatedPolylin.getPointAt(updatedPolylin.getPointsNb() - i - 1);
SimplifiedCardinalDirection orientation2 = updatedPolylin.getApproximatedOrientationOfSegment(updatedPolylin.getPointsNb() - i
- 1);
if (orientation2.isVertical()) {
if (currentPointEndingSide.x != previousPointEndingSide.x) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Updating point at " + (updatedPolylin.getPointsNb() - i - 1) + " for vertical orientation");
}
currentPointEndingSide.x = previousPointEndingSide.x;
updatedPolylin.updatePointAt(updatedPolylin.getPointsNb() - i - 1, currentPointEndingSide);
}
} else if (orientation2.isHorizontal()) {
if (currentPointEndingSide.y != previousPointEndingSide.y) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Updating point at " + (updatedPolylin.getPointsNb() - i - 1) + " for horizontal orientation");
}
currentPointEndingSide.y = previousPointEndingSide.y;
updatedPolylin.updatePointAt(updatedPolylin.getPointsNb() - i - 1, currentPointEndingSide);
}
}
}
// Finish updating
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Still have to finish updating, currentStartIndex=" + currentStartIndex + " currentEndIndex=" + currentEndIndex);
}
if (currentPointEndingSide.equals(currentPointStartingSide)
|| Math.abs(currentPointEndingSide.x - currentPointStartingSide.x) < EPSILON
|| Math.abs(currentPointEndingSide.y - currentPointStartingSide.y) < EPSILON) {
// No need to append extra path
} else {
Vector<SimplifiedCardinalDirection> excludedStartOrientations = new Vector<SimplifiedCardinalDirection>();
Vector<SimplifiedCardinalDirection> excludedEndOrientations = new Vector<SimplifiedCardinalDirection>();
if (currentStartIndex - 1 >= 0 && currentStartIndex - 1 < updatedPolylin.getSegmentNb()) {
excludedStartOrientations.add(updatedPolylin.getApproximatedOrientationOfSegment(currentStartIndex - 1).getOpposite());
// System.out.println("excludedStartOrientations="+excludedStartOrientations);
}
if (currentEndIndex >= 0 && currentEndIndex < updatedPolylin.getSegmentNb()) {
excludedEndOrientations.add(updatedPolylin.getApproximatedOrientationOfSegment(currentEndIndex));
// System.out.println("excludedEndOrientations="+excludedEndOrientations);
}
// System.out.println("overlap="+overlap);
if (currentEndIndex - currentStartIndex == 1) {
/*FGERectPolylin*/
missingPath = makeShortestRectPolylin(currentPointStartingSide, currentPointEndingSide, true, overlapX, overlapY,
excludedStartOrientations, excludedEndOrientations);
for (int i = 1; i < missingPath.getPointsNb() - 1; i++) {
updatedPolylin.insertPointAtIndex(missingPath.getPointAt(i), currentStartIndex + i);
}
} else if (currentEndIndex - currentStartIndex == 2) {
/*FGERectPolylin*/// missingPath = makeShortestRectPolylin(currentPointStartingSide, currentPointEndingSide, true, overlap,
// updatedPolylin.getPointAt(currentStartIndex+1), excludedStartOrientations, excludedEndOrientations);
missingPath = makeRectPolylinCrossingPoint(currentPointStartingSide, currentPointEndingSide,
updatedPolylin.getPointAt(currentStartIndex + 1), true, overlapX, overlapY, excludedStartOrientations,
excludedEndOrientations);
updatedPolylin.removePointAtIndex(currentStartIndex + 1);
for (int i = 1; i < missingPath.getPointsNb() - 1; i++) {
updatedPolylin.insertPointAtIndex(missingPath.getPointAt(i), currentStartIndex + i);
}
}
}
return updatedPolylin;
}
// TODO debug: remove this
public FGERectPolylin missingPath;
public FGEPoint currentPointStartingSide;
public FGEPoint currentPointEndingSide;
public double getOverlapX() {
return overlapX;
}
public void setOverlapX(double overlap) {
this.overlapX = overlap;
}
public double getOverlapY() {
return overlapY;
}
public void setOverlapY(double overlap) {
this.overlapY = overlap;
}
public FGEArea getEndArea() {
// if no start area defined, return start point
if (endArea == null && getPointsNb() > 0) {
return getPointAt(getPointsNb() - 1);
}
return endArea;
}
public FGEArea getStartArea() {
// if no start area defined, return start point
if (startArea == null && getPointsNb() > 0) {
return getPointAt(0);
}
return startArea;
}
public boolean doesRespectAllConstraints() {
return respectAllConstraints && getPointsNb() > 0;
}
}