/* * (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.connectors.rpc; import java.awt.Color; import java.awt.Cursor; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.NoninvertibleTransformException; import java.util.List; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.fge.ConnectorGraphicalRepresentation; import org.openflexo.fge.FGEConstants; import org.openflexo.fge.GraphicalRepresentation; import org.openflexo.fge.connectors.Connector; import org.openflexo.fge.connectors.ConnectorSymbol.EndSymbolType; import org.openflexo.fge.connectors.ConnectorSymbol.MiddleSymbolType; import org.openflexo.fge.connectors.ConnectorSymbol.StartSymbolType; import org.openflexo.fge.cp.ConnectorAdjustingControlPoint; import org.openflexo.fge.cp.ConnectorNonAdjustableControlPoint; import org.openflexo.fge.cp.ControlArea; import org.openflexo.fge.cp.ControlPoint; import org.openflexo.fge.geom.FGEArc; import org.openflexo.fge.geom.FGEGeometricObject; import org.openflexo.fge.geom.FGEGeometricObject.CardinalQuadrant; import org.openflexo.fge.geom.FGEGeometricObject.Filling; import org.openflexo.fge.geom.FGEGeometricObject.SimplifiedCardinalDirection; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGERectPolylin; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.geom.FGESegment; import org.openflexo.fge.geom.FGEShape; 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.FGEUnionArea; import org.openflexo.fge.graphics.FGEConnectorGraphics; import org.openflexo.fge.graphics.ForegroundStyle; import org.openflexo.fge.graphics.ForegroundStyle.DashStyle; import org.openflexo.toolbox.ConcatenedList; /** * A RectPolylinConnector is a connector joining 2 shapes with a path of orthogonal segments (this connector is encoded as a * {@link FGERectPolylin} instance). * * This connector has many configuration parameters. * * Mainly, there are three principal modes, regarding user control * * - automatic layout: the layout is continuously recomputed and updated, given location and orientation constraints (adjustability = * AUTO_LAYOUT) - semi-adjustable layout: a user is editing the layout by moving a single control point (any point located on connector). * Locations and orientation constraints are respected (adjustability = BASICALLY_ADJUSTABLE) - adjustable layout: layout is fully * controlled by user: layout is editable by moving, adding and removing control points, and segments are translatable. Locations and * orientation constraints are respected * * Layout mode is configurable by using {@link #setAdjustability(RectPolylinAdjustability)} method (default is automatic layout). * * This connector encodes many control points: - start control point is the starting control point, located on the outline of starting shape * - first control point is the (eventual) first control point located outside both shapes (just after start control point) - end control * point is the ending control point, located on the outline of ending shape - last control point is the (eventual) last control point * located outside both shapes (just before end control point) - an intermediate control point is an other control point, which is not the * start, first, last or end control point * * @author sylvain * */ public class RectPolylinConnector extends Connector { static final Logger logger = Logger.getLogger(RectPolylinConnector.class.getPackage().getName()); private FGERectPolylin polylin; private Vector<FGERectPolylin> potentialPolylin; // public FGERectPolylin debugPolylin; private ControlPoint p_start; private ControlPoint p_end; // private ControlPoint crossedControlPoint; private Vector<ControlPoint> controlPoints; private Vector<ControlArea<?>> controlAreas; private boolean firstUpdated = false; private boolean straightLineWhenPossible = true; private RectPolylinAdjustability adjustability = RectPolylinAdjustability.AUTO_LAYOUT; private boolean wasManuallyAdjusted = false; private SimplifiedCardinalDirection startOrientation; private SimplifiedCardinalDirection endOrientation; private int pixelOverlap = FGEConstants.DEFAULT_RECT_POLYLIN_PIXEL_OVERLAP; // overlap expressed in pixels relative to 1.0 scale private int arcSize = FGEConstants.DEFAULT_ROUNDED_RECT_POLYLIN_ARC_SIZE; private boolean isRounded = false; private boolean isStartingLocationFixed = false; private boolean isEndingLocationFixed = false; private boolean isStartingLocationDraggable = false; private boolean isEndingLocationDraggable = false; // private FGEPoint startCPRelativeToStartObject; // private FGEPoint endCPRelativeToEndObject; private FGERectPolylin polylinRelativeToStartObject; private FGERectPolylin polylinRelativeToEndObject; private FGERectPolylin lastKnownCleanPolylinBeforeConnectorRestructuration; private boolean isCleaningPolylin = false; private FGEPoint fixedStartLocationRelativeToStartObject; private FGEPoint fixedEndLocationRelativeToEndObject; private FGEPoint _crossedPoint; // ******************************************************************************* // * Constructor * // ******************************************************************************* // Used for deserialization public RectPolylinConnector() { this(null); } public RectPolylinConnector(ConnectorGraphicalRepresentation graphicalRepresentation) { super(graphicalRepresentation); controlPoints = new Vector<ControlPoint>(); controlAreas = new Vector<ControlArea<?>>(); potentialPolylin = new Vector<FGERectPolylin>(); } @Override public ConnectorType getConnectorType() { return ConnectorType.RECT_POLYLIN; } Vector<ControlPoint> _getControlPoints() { return controlPoints; } @Override public List<? extends ControlArea> getControlAreas() { if (getGraphicalRepresentation().getMiddleSymbol() == MiddleSymbolType.NONE && controlAreas.size() == 0) { return controlPoints; } // Otherwise, we have to manage a concatenation if (allControlAreas == null) { allControlAreas = new ConcatenedList<ControlArea>(); allControlAreas.addElementList(controlPoints); if (getGraphicalRepresentation().getMiddleSymbol() != MiddleSymbolType.NONE && middleSymbolLocationControlPoint != null) { allControlAreas.add(0, middleSymbolLocationControlPoint); } allControlAreas.addElementList(controlAreas); } return allControlAreas; } private ConcatenedList<ControlArea> allControlAreas; @Override public void drawConnector(FGEConnectorGraphics g) { if (!firstUpdated) { refreshConnector(); } /* * if (debugPolylin != null) { g.setDefaultForeground(ForegroundStyle.makeStyle(Color.PINK, 1.0f, DashStyle.SMALL_DASHES)); * debugPolylin.paint(g); } */ if (getDebug()) { g.setDefaultForeground(ForegroundStyle.makeStyle(Color.GRAY, 1.0f, DashStyle.SMALL_DASHES)); for (FGERectPolylin p : potentialPolylin) { p.paint(g); } g.setDefaultForeground(ForegroundStyle.makeStyle(Color.BLACK, 3.0f, DashStyle.PLAIN_STROKE)); if (polylin != null) { polylin.debugPaint(g); } } else { g.setDefaultForeground(getGraphicalRepresentation().getForeground()); if (polylin != null) { if (getIsRounded()) { polylin.paintWithRounds(g, getArcSize()); } else { polylin.paint(g); } } } /* * if (debugPolylin != null) { g.setDefaultForeground(ForegroundStyle.makeStyle(Color.RED, 1.0f, DashStyle.PLAIN_STROKE)); * debugPolylin.paint(g); } */ // Draw eventual symbols if (polylin != null && polylin.getSegments() != null && polylin.getSegments().size() > 0) { // Segments are here all orthogonal, we can can then rely on getAngle() computation performed on geom layer // (we dont need to convert to view first) if (getGraphicalRepresentation().getStartSymbol() != StartSymbolType.NONE) { FGESegment firstSegment = polylin.getSegments().firstElement(); if (firstSegment != null) { g.drawSymbol(firstSegment.getP1(), getGraphicalRepresentation().getStartSymbol(), getGraphicalRepresentation() .getStartSymbolSize(), firstSegment.getAngle()); } } if (getGraphicalRepresentation().getEndSymbol() != EndSymbolType.NONE) { FGESegment lastSegment = polylin.getSegments().lastElement(); if (lastSegment != null) { g.drawSymbol(lastSegment.getP2(), getGraphicalRepresentation().getEndSymbol(), getGraphicalRepresentation() .getEndSymbolSize(), lastSegment.getAngle() + Math.PI); } } if (getGraphicalRepresentation().getMiddleSymbol() != MiddleSymbolType.NONE) { g.drawSymbol(getMiddleSymbolLocation(), getGraphicalRepresentation().getMiddleSymbol(), getGraphicalRepresentation() .getMiddleSymbolSize(), getMiddleSymbolAngle()); } } } @Override public FGEPoint getMiddleSymbolLocation() { if (polylin == null) { return new FGEPoint(0, 0); } AffineTransform at = getGraphicalRepresentation().convertNormalizedPointToViewCoordinatesAT(1.0); FGERectPolylin transformedPolylin = polylin.transform(at); FGEPoint point = transformedPolylin.getPointAtRelativePosition(getGraphicalRepresentation().getRelativeMiddleSymbolLocation()); try { point = point.transform(at.createInverse()); } catch (NoninvertibleTransformException e) { e.printStackTrace(); } if (!getIsRounded()) { return point; } else { UnnormalizedArcSize arcSize = computeUnnormalizedArcSize(); FGEPoint returned = polylin.getNearestPointLocatedOnRoundedRepresentation(point, arcSize.arcWidth, arcSize.arcHeight); if (returned == null) { return new FGEPoint(0, 0); } else { return returned; } } } /** * * @return angle expressed in radians */ public double getMiddleSymbolAngle() { if (polylin == null) { return 0; } FGEPoint middleSymbolLocation = getMiddleSymbolLocation(); FGESegment relatedSegment = polylin.getNearestSegment(middleSymbolLocation); if (relatedSegment == null) { return 0; } if (!getIsRounded()) { return relatedSegment.getAngle() + Math.PI; } else { UnnormalizedArcSize arcSize = computeUnnormalizedArcSize(); FGEArc arc = polylin.getArcForNearestPointLocatedOnRoundedRepresentation(middleSymbolLocation, arcSize.arcWidth, arcSize.arcHeight); if (arc != null) { double angle = arc.angleForPoint(middleSymbolLocation) + Math.PI / 2; if (arc.isClockWise()) { angle += Math.PI; } return angle; } else { return relatedSegment.getAngle() + Math.PI; } } } @Override public void refreshConnector(boolean force) { if (!force && !needsRefresh()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Skipping refreshConnector() for " + getGraphicalRepresentation().getDrawable()); } return; } else { if (logger.isLoggable(Level.FINE)) { logger.fine("Perform refreshConnector() for " + getGraphicalRepresentation().getDrawable()); } } updateLayout(); if (getGraphicalRepresentation().getMiddleSymbol() != MiddleSymbolType.NONE) { updateMiddleSymbolLocationControlPoint(); } super.refreshConnector(force); firstUpdated = true; } @Override public boolean needsRefresh() { if (!firstUpdated) { return true; } if (polylin == null) { return true; } return super.needsRefresh(); } @Override public double distanceToConnector(FGEPoint aPoint, double scale) { double returned = Double.POSITIVE_INFINITY; if (polylin == null) { return Double.POSITIVE_INFINITY; } Point testPoint = getGraphicalRepresentation().convertNormalizedPointToViewCoordinates(aPoint, scale); for (FGESegment s : polylin.getSegments()) { Point point1 = getGraphicalRepresentation().convertNormalizedPointToViewCoordinates(s.getP1(), scale); Point point2 = getGraphicalRepresentation().convertNormalizedPointToViewCoordinates(s.getP2(), scale); double distanceToCurrentSegment = Line2D.ptSegDist(point1.x, point1.y, point2.x, point2.y, testPoint.x, testPoint.y); if (distanceToCurrentSegment < returned) { returned = distanceToCurrentSegment; } } return returned; } public static enum RectPolylinAdjustability { AUTO_LAYOUT, BASICALLY_ADJUSTABLE, FULLY_ADJUSTABLE } public static enum RectPolylinConstraints { NONE, ORTHOGONAL_LAYOUT, ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST, ORTHOGONAL_LAYOUT_VERTICAL_FIRST, HORIZONTAL_OR_VERTICAL_LAYOUT, HORIZONTAL_LAYOUT, VERTICAL_LAYOUT, ORIENTATIONS_FIXED, START_ORIENTATION_FIXED, END_ORIENTATION_FIXED } private RectPolylinConstraints rectPolylinConstraints = RectPolylinConstraints.NONE; public RectPolylinConstraints getRectPolylinConstraints() { return rectPolylinConstraints; } public void setRectPolylinConstraints(RectPolylinConstraints aRectPolylinConstraints) { if (aRectPolylinConstraints != rectPolylinConstraints) { rectPolylinConstraints = aRectPolylinConstraints; p_start = null; p_end = null; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public void setRectPolylinConstraints(RectPolylinConstraints someRectPolylinConstraints, SimplifiedCardinalDirection aStartOrientation, SimplifiedCardinalDirection aEndOrientation) { if (someRectPolylinConstraints != rectPolylinConstraints || startOrientation != aStartOrientation || endOrientation != aEndOrientation) { rectPolylinConstraints = someRectPolylinConstraints; startOrientation = aStartOrientation; endOrientation = aEndOrientation; // p_start = null; // p_end = null; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public boolean getStraightLineWhenPossible() { return straightLineWhenPossible; } public void setStraightLineWhenPossible(boolean aFlag) { straightLineWhenPossible = aFlag; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } public boolean getIsAdjustable() { return getAdjustability() == RectPolylinAdjustability.FULLY_ADJUSTABLE; } public void setIsAdjustable(boolean aFlag) { setAdjustability(aFlag ? RectPolylinAdjustability.FULLY_ADJUSTABLE : RectPolylinAdjustability.AUTO_LAYOUT); } public RectPolylinAdjustability getAdjustability() { return adjustability; } public void setAdjustability(RectPolylinAdjustability anAdjustability) { if (adjustability != anAdjustability) { adjustability = anAdjustability; // logger.info("Switching to setIsAdjustable("+aFlag+")"); if (polylin != null) { updateWithNewPolylin(polylin); } // if (isAdjustable) polylin = null; if (getGraphicalRepresentation() != null) { updateLayout(); if (polylin != null) { updateWithNewPolylin(polylin); } getGraphicalRepresentation().notifyConnectorChanged(); } if (adjustability != RectPolylinAdjustability.FULLY_ADJUSTABLE) { setWasManuallyAdjusted(false); } } } public boolean getWasManuallyAdjusted() { return wasManuallyAdjusted; } public void setWasManuallyAdjusted(boolean aFlag) { wasManuallyAdjusted = aFlag; if (!wasManuallyAdjusted) { if (polylin != null) { updateWithNewPolylin(polylin); } // if (isAdjustable) polylin = null; if (getGraphicalRepresentation() != null) { updateLayout(); if (polylin != null) { updateWithNewPolylin(polylin); } getGraphicalRepresentation().notifyConnectorChanged(); } } } public SimplifiedCardinalDirection getEndOrientation() { return endOrientation; } public void setEndOrientation(SimplifiedCardinalDirection anOrientation) { // logger.info("setEndOrientation="+anOrientation); if (anOrientation != endOrientation) { endOrientation = anOrientation; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public SimplifiedCardinalDirection getStartOrientation() { return startOrientation; } public void setStartOrientation(SimplifiedCardinalDirection anOrientation) { if (anOrientation != startOrientation) { startOrientation = anOrientation; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public int getPixelOverlap() { return pixelOverlap; } public void setPixelOverlap(int aPixelOverlap) { if (aPixelOverlap != pixelOverlap) { pixelOverlap = aPixelOverlap; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } double getOverlapXResultingFromPixelOverlap() { // Compute relative overlap along X-axis Point overlap_p1 = new Point(0, 0); Point overlap_p2 = new Point(getPixelOverlap(), 0); FGEPoint overlap_pp1 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(overlap_p1, 1); FGEPoint overlap_pp2 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(overlap_p2, 1); return Math.abs(overlap_pp1.x - overlap_pp2.x); } double getOverlapYResultingFromPixelOverlap() { // Compute relative overlap along Y-axis Point overlap_p1 = new Point(0, 0); Point overlap_p2 = new Point(0, getPixelOverlap()); FGEPoint overlap_pp1 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(overlap_p1, 1); FGEPoint overlap_pp2 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(overlap_p2, 1); return Math.abs(overlap_pp1.y - overlap_pp2.y); } public boolean getIsRounded() { return isRounded; } public void setIsRounded(boolean aFlag) { if (isRounded != aFlag) { isRounded = aFlag; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public int getArcSize() { return arcSize; } public void setArcSize(int anArcSize) { if (anArcSize != arcSize) { arcSize = anArcSize; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } } } @Override public FGERectangle getConnectorUsedBounds() { // logger.info("Called getConnectorUsedBounds()"); if (polylin != null) { FGERectangle minimalBounds = polylin.getBoundingBox(); FGERectangle returned = new FGERectangle(Filling.FILLED); // Compute required space to draw symbols, eg arrows double maxSymbolSize = Math.max(getGraphicalRepresentation().getStartSymbolSize(), Math.max(getGraphicalRepresentation().getMiddleSymbolSize(), getGraphicalRepresentation().getEndSymbolSize())); double relativeWidthToAdd = maxSymbolSize * 2 / getGraphicalRepresentation().getViewWidth(1.0); double relativeHeightToAdd = maxSymbolSize * 2 / getGraphicalRepresentation().getViewHeight(1.0); // Add space to draw symbols, eg arrows returned.x = minimalBounds.x - relativeWidthToAdd * minimalBounds.width; returned.y = minimalBounds.y - relativeHeightToAdd * minimalBounds.height; returned.width = (1 + 2 * relativeWidthToAdd) * minimalBounds.width; returned.height = (1 + 2 * relativeHeightToAdd) * minimalBounds.height; // logger.info("Called getConnectorUsedBounds() return "+returned); return returned; } return NORMALIZED_BOUNDS; } @Override public void connectorWillBeModified() { super.connectorWillBeModified(); if (polylin != null) { lastKnownCleanPolylinBeforeConnectorRestructuration = polylin.clone(); } } @Override public void connectorHasBeenModified() { super.connectorHasBeenModified(); lastKnownCleanPolylinBeforeConnectorRestructuration = null; _connectorChanged(false); } public FGERectPolylin getCurrentPolylin() { return polylin; } private FGERectPolylin _deserializedPolylin; // Used for serialization only public FGERectPolylin _getPolylin() { if (getAdjustability() != RectPolylinAdjustability.FULLY_ADJUSTABLE) { return null; } return polylin; } // Used for serialization only public void _setPolylin(FGERectPolylin aPolylin) { if (aPolylin != null && aPolylin.getPointsNb() > 0) { _deserializedPolylin = aPolylin; wasManuallyAdjusted = true; } } public void manuallySetPolylin(FGERectPolylin aPolylin) { updateWithNewPolylin(aPolylin); } private ConnectorAdjustingControlPoint updateMiddleSymbolLocationControlPoint() { if (middleSymbolLocationControlPoint == null) { middleSymbolLocationControlPoint = new ConnectorAdjustingControlPoint(getGraphicalRepresentation(), getMiddleSymbolLocation()) { @Override public Cursor getDraggingCursor() { /* * SimplifiedCardinalDirection orientation = * polylin.getNearestSegment(getMiddleSymbolLocation()).getApproximatedOrientation(); if (orientation != null) { if * (orientation.isHorizontal()) { return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); } if * (orientation.isVertical()) { return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR); } } */ // return FGEConstants.MOVE_CURSOR; return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); } @Override public FGEArea getDraggingAuthorizedArea() { return polylin; } @Override public boolean dragToPoint(FGEPoint newRelativePoint, FGEPoint pointRelativeToInitialConfiguration, FGEPoint newAbsolutePoint, FGEPoint initialPoint, MouseEvent event) { if (polylin == null) { logger.warning("polylin is null"); return false; } // logger.info("OK, moving to "+point);dds UnnormalizedArcSize arcSize = computeUnnormalizedArcSize(); FGEPoint pt = polylin.getNearestPointLocatedOnRoundedRepresentation(newRelativePoint, arcSize.arcWidth, arcSize.arcHeight); // FGEPoint pt = getNearestPointOnAuthorizedArea(newRelativePoint); setPoint(pt); AffineTransform at = getGraphicalRepresentation().convertNormalizedPointToViewCoordinatesAT(1.0); pt = pt.transform(at); FGERectPolylin transformedPolylin = polylin.transform(at); // FGESegment segment = new FGESegment(cp1.getPoint(),cp2.getPoint()); getGraphicalRepresentation().setRelativeMiddleSymbolLocation(transformedPolylin.getRelativeLocation(pt)); /* * cp1RelativeToStartObject = GraphicalRepresentation.convertNormalizedPoint( getGraphicalRepresentation(), pt, * getStartObject()); */ getGraphicalRepresentation().notifyConnectorChanged(); return true; } }; } middleSymbolLocationControlPoint.setPoint(getMiddleSymbolLocation()); return middleSymbolLocationControlPoint; } private ConnectorAdjustingControlPoint middleSymbolLocationControlPoint; private class UnnormalizedArcSize { double arcWidth; double arcHeight; } private UnnormalizedArcSize computeUnnormalizedArcSize() { UnnormalizedArcSize returned = new UnnormalizedArcSize(); FGEPoint arcP1 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(new Point(0, 0), 1.0); FGEPoint arcP2 = getGraphicalRepresentation().convertViewCoordinatesToNormalizedPoint(new Point(arcSize, arcSize), 1.0); returned.arcWidth = arcP2.x - arcP1.x; returned.arcHeight = arcP2.y - arcP1.y; return returned; } @Override public double getStartAngle() { if (polylin == null) { return 0; } return polylin.getFirstSegment().getAngle(); } @Override public double getEndAngle() { if (polylin == null) { return -Math.PI; } return polylin.getLastSegment().getAngle(); } public ControlPoint getEndControlPoint() { return p_end; } public ControlPoint getStartControlPoint() { return p_start; } public FGEPoint getCrossedControlPoint() { // if (crossedControlPoint == null) { return _crossedPoint; /* * } return crossedControlPoint.getPoint(); */ } public void setCrossedControlPoint(FGEPoint aPoint) { /* * if (aPoint == null) { _crossedPoint = null; crossedControlPoint = null; return; } else { */ _crossedPoint = aPoint; if (getGraphicalRepresentation() != null) { updateLayout(); getGraphicalRepresentation().notifyConnectorChanged(); } // } } public FGEPoint getCrossedControlPointOnRoundedArc() { if (getCrossedControlPoint() != null) { if (getIsRounded()) { UnnormalizedArcSize arcSize = computeUnnormalizedArcSize(); return polylin.getNearestPointLocatedOnRoundedRepresentation(getCrossedControlPoint(), arcSize.arcWidth, arcSize.arcWidth); } else { return getCrossedControlPoint(); } } return null; } public boolean getIsStartingLocationFixed() { return isStartingLocationFixed; } public void setIsStartingLocationFixed(boolean aFlag) { if (isStartingLocationFixed != aFlag) { isStartingLocationFixed = aFlag; if (isStartingLocationFixed && fixedStartLocationRelativeToStartObject == null && p_start != null) { // In this case, we can initialize fixed start location to its current value fixedStartLocationRelativeToStartObject = GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), p_start.getPoint(), getStartObject()); } if (getGraphicalRepresentation() != null) { updateLayout(); _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public boolean getIsStartingLocationDraggable() { return isStartingLocationDraggable; } public void setIsStartingLocationDraggable(boolean aFlag) { if (isStartingLocationDraggable != aFlag) { isStartingLocationDraggable = aFlag; if (getGraphicalRepresentation() != null) { updateLayout(); // Force control points to be rebuild in order to get draggable feature _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public boolean getIsEndingLocationFixed() { return isEndingLocationFixed; } public void setIsEndingLocationFixed(boolean aFlag) { if (isEndingLocationFixed != aFlag) { isEndingLocationFixed = aFlag; if (isEndingLocationFixed && fixedEndLocationRelativeToEndObject == null && p_end != null) { // In this case, we can initialize fixed start location to its current value fixedEndLocationRelativeToEndObject = GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), p_end.getPoint(), getEndObject()); } if (getGraphicalRepresentation() != null) { updateLayout(); _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public boolean getIsEndingLocationDraggable() { return isEndingLocationDraggable; } public void setIsEndingLocationDraggable(boolean aFlag) { if (isEndingLocationDraggable != aFlag) { isEndingLocationDraggable = aFlag; if (getGraphicalRepresentation() != null) { updateLayout(); // Force control points to be rebuild in order to get draggable feature _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } /** * Return start location asserting start location is fixed. Return position relative to start object (in the start-object coordinates * system) * * @return */ public FGEPoint getFixedStartLocation() { if (!getIsStartingLocationFixed()) { return null; } if (fixedStartLocationRelativeToStartObject == null) { FGEPoint centerOfEndObjectSeenFromStartObject = GraphicalRepresentation.convertNormalizedPoint(getEndObject(), new FGEPoint( 0.5, 0.5), getStartObject()); fixedStartLocationRelativeToStartObject = getStartObject().getShape().outlineIntersect(centerOfEndObjectSeenFromStartObject); if (fixedStartLocationRelativeToStartObject == null) { logger.warning("outlineIntersect() returned null"); fixedStartLocationRelativeToStartObject = new FGEPoint(0.9, 0.9); } } return fixedStartLocationRelativeToStartObject; } /** * Sets start location asserting start location is fixed. Sets position relative to start object (in the start-object coordinates * system) * * @param aPoint * : relative to start object */ public void setFixedStartLocation(FGEPoint aPoint) { if (!isStartingLocationFixed && aPoint != null) { isStartingLocationFixed = true; } if (getStartObject() != null) { FGEShape startArea = getStartObject().getShape().getOutline(); // startArea.setIsFilled(false); aPoint = startArea.getNearestPoint(aPoint); } if (fixedStartLocationRelativeToStartObject == null || !fixedStartLocationRelativeToStartObject.equals(aPoint)) { fixedStartLocationRelativeToStartObject = aPoint; // logger.info("fixedStartLocationRelativeToStartObject="+fixedStartLocationRelativeToStartObject); if (getGraphicalRepresentation() != null) { updateLayout(); _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } /** * Return end location asserting end location is fixed. Return position relative to end object (in the end-object coordinates system) * * @return */ public FGEPoint getFixedEndLocation() { if (!getIsEndingLocationFixed()) { return null; } if (fixedEndLocationRelativeToEndObject == null) { FGEPoint centerOfStartObjectSeenFromEndObject = GraphicalRepresentation.convertNormalizedPoint(getStartObject(), new FGEPoint( 0.5, 0.5), getEndObject()); fixedEndLocationRelativeToEndObject = getEndObject().getShape().outlineIntersect(centerOfStartObjectSeenFromEndObject); if (fixedEndLocationRelativeToEndObject == null) { logger.warning("outlineIntersect() returned null"); fixedEndLocationRelativeToEndObject = new FGEPoint(0.1, 0.1); } } return fixedEndLocationRelativeToEndObject; } /** * Sets end location asserting end location is fixed. Sets position relative to end object (in the end-object coordinates system) * * @param aPoint * , relative to end object */ public void setFixedEndLocation(FGEPoint aPoint) { if (!isEndingLocationFixed && aPoint != null) { isEndingLocationFixed = true; } if (getEndObject() != null) { FGEShape endArea = getEndObject().getShape().getOutline(); // endArea.setIsFilled(false); aPoint = endArea.getNearestPoint(aPoint); } if (fixedEndLocationRelativeToEndObject == null || !fixedEndLocationRelativeToEndObject.equals(aPoint)) { fixedEndLocationRelativeToEndObject = aPoint; if (getGraphicalRepresentation() != null) { updateLayout(); _rebuildControlPoints(); getGraphicalRepresentation().notifyConnectorChanged(); } } } public Vector<SimplifiedCardinalDirection> getAllowedStartOrientations() { Vector<SimplifiedCardinalDirection> returned = getPrimitiveAllowedStartOrientations(); if (getIsStartingLocationFixed() && getFixedStartLocation() != null) { Vector<SimplifiedCardinalDirection> newConstraints = SimplifiedCardinalDirection.intersection(returned, _getAllowedStartOrientationsDueToFixedStartingLocation()); if (newConstraints.size() == 0) { logger.warning("Cannot respect fixed start location orientation constraint primitives=" + returned + " for fixed starting position=" + _getAllowedStartOrientationsDueToFixedStartingLocation()); return returned; } else { return newConstraints; } } return returned; } private Vector<SimplifiedCardinalDirection> _getAllowedStartOrientationsDueToFixedStartingLocation() { Vector<SimplifiedCardinalDirection> returned = new Vector<SimplifiedCardinalDirection>(); FGEArea startArea = getStartObject().getAllowedStartAreaForConnector(getGraphicalRepresentation()); // startArea.setIsFilled(false); for (SimplifiedCardinalDirection o : SimplifiedCardinalDirection.values()) { if (startArea.getAnchorAreaFrom(o).containsPoint(getFixedStartLocation())) { returned.add(o); // System.out.println("CHOOSEN: "+(startArea.getAnchorAreaFrom(o).containsPoint(getFixedStartLocation()))+" Orientation: "+o+" startArea.getAnchorAreaFrom(o)="+startArea.getAnchorAreaFrom(o)+" location="+getFixedStartLocation()); /* * if (startArea.getAnchorAreaFrom(o) instanceof FGEArc) { FGEArc arc = (FGEArc)startArea.getAnchorAreaFrom(o); double angle * = arc.angleForPoint(getFixedStartLocation()); FGEPoint p = arc.getPointAtRadianAngle(angle); * System.out.println("Point "+getFixedStartLocation * ()+" on arc="+arc.containsPoint(getFixedStartLocation())+" angle="+angle+ * " otherPoint="+p+" on arc="+arc.containsPoint(p)); } */ // if (startArea.getOrthogonalPerspectiveArea(o).containsPoint(getFixedStartLocation())) returned.add(o); } } // logger.info("Allowed start orientations due to fixed starting location = "+returned); if (returned.size() == 0) { logger.warning("Allowed start orientations due to fixed starting location returned an empty vector " + returned); } return returned; } /** * Return all allowed start orientation as this is defined in orientation constraint Does NOT take under account the fact that starting * position could have been fixed and can also induced an other start orientation. * * @return */ public Vector<SimplifiedCardinalDirection> getPrimitiveAllowedStartOrientations() { switch (getRectPolylinConstraints()) { case NONE: return SimplifiedCardinalDirection.allDirections(); case START_ORIENTATION_FIXED: if (getStartOrientation() == null) { return SimplifiedCardinalDirection.uniqueDirection(SimplifiedCardinalDirection.NORTH); } return SimplifiedCardinalDirection.uniqueDirection(getStartOrientation()); case ORIENTATIONS_FIXED: if (getStartOrientation() == null) { return SimplifiedCardinalDirection.uniqueDirection(SimplifiedCardinalDirection.NORTH); } return SimplifiedCardinalDirection.uniqueDirection(getStartOrientation()); case HORIZONTAL_LAYOUT: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.EAST, SimplifiedCardinalDirection.WEST); case ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.EAST, SimplifiedCardinalDirection.WEST); case VERTICAL_LAYOUT: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.NORTH, SimplifiedCardinalDirection.SOUTH); case ORTHOGONAL_LAYOUT_VERTICAL_FIRST: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.NORTH, SimplifiedCardinalDirection.SOUTH); default: return SimplifiedCardinalDirection.allDirections(); } } public Vector<SimplifiedCardinalDirection> getExcludedStartOrientations() { return SimplifiedCardinalDirection.allDirectionsExcept(getAllowedStartOrientations()); } public Vector<SimplifiedCardinalDirection> getAllowedEndOrientations() { Vector<SimplifiedCardinalDirection> returned = getPrimitiveAllowedEndOrientations(); if (getIsEndingLocationFixed() && getFixedEndLocation() != null) { Vector<SimplifiedCardinalDirection> newConstraints = SimplifiedCardinalDirection.intersection(returned, _getAllowedEndOrientationsDueToFixedEndingLocation()); if (newConstraints.size() == 0) { logger.warning("Cannot respect fixed end location orientation constraint primitives=" + returned + " for fixed ending position=" + _getAllowedEndOrientationsDueToFixedEndingLocation()); return returned; } else { return newConstraints; } } return returned; } private Vector<SimplifiedCardinalDirection> _getAllowedEndOrientationsDueToFixedEndingLocation() { Vector<SimplifiedCardinalDirection> returned = new Vector<SimplifiedCardinalDirection>(); FGEArea endArea = getEndObject().getAllowedEndAreaForConnector(getGraphicalRepresentation()); // endArea.setIsFilled(false); for (SimplifiedCardinalDirection o : SimplifiedCardinalDirection.values()) { // if (endArea.getOrthogonalPerspectiveArea(o).containsPoint(getFixedEndLocation())) returned.add(o); if (endArea.getAnchorAreaFrom(o).containsPoint(getFixedEndLocation())) { returned.add(o); // System.out.println("CHOOSEN: "+(endArea.getOrthogonalPerspectiveArea(o).containsPoint(getFixedEndLocation()))+" Orientation: "+o+" endArea.getOrthogonalPerspectiveArea(o)="+endArea.getOrthogonalPerspectiveArea(o)); } } // logger.info("Allowed end orientations due to fixed ending location="+returned); if (returned.size() == 0) { logger.warning("Allowed start orientations due to fixed starting location returned an empty vector " + returned); } return returned; } public Vector<SimplifiedCardinalDirection> getPrimitiveAllowedEndOrientations() { switch (getRectPolylinConstraints()) { case NONE: return SimplifiedCardinalDirection.allDirections(); case END_ORIENTATION_FIXED: if (getEndOrientation() == null) { return SimplifiedCardinalDirection.uniqueDirection(SimplifiedCardinalDirection.NORTH); } return SimplifiedCardinalDirection.uniqueDirection(getEndOrientation()); case ORIENTATIONS_FIXED: if (getEndOrientation() == null) { return SimplifiedCardinalDirection.uniqueDirection(SimplifiedCardinalDirection.NORTH); } return SimplifiedCardinalDirection.uniqueDirection(getEndOrientation()); case HORIZONTAL_LAYOUT: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.EAST, SimplifiedCardinalDirection.WEST); case ORTHOGONAL_LAYOUT_VERTICAL_FIRST: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.EAST, SimplifiedCardinalDirection.WEST); case VERTICAL_LAYOUT: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.NORTH, SimplifiedCardinalDirection.SOUTH); case ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST: return SimplifiedCardinalDirection.someDirections(SimplifiedCardinalDirection.NORTH, SimplifiedCardinalDirection.SOUTH); default: return SimplifiedCardinalDirection.allDirections(); } } public Vector<SimplifiedCardinalDirection> getExcludedEndOrientations() { return SimplifiedCardinalDirection.allDirectionsExcept(getAllowedEndOrientations()); } // ******************************************************************************* // * Internal A.P.I. for connector computation * // ******************************************************************************* /** * This is the general method used for connector updating Calling this method is generally safe regarding internal structure */ void updateLayout() { if (getGraphicalRepresentation() == null) { return; } if (!getGraphicalRepresentation().isRegistered()) { return; } if (getAdjustability() == RectPolylinAdjustability.AUTO_LAYOUT || getAdjustability() == RectPolylinAdjustability.FULLY_ADJUSTABLE && !getWasManuallyAdjusted()) { _updateAsAutoLayout(); } else if (getAdjustability() == RectPolylinAdjustability.BASICALLY_ADJUSTABLE) { if (polylin == null) { if (_deserializedPolylin != null) { // Rebuild from deserialized polylin updateWithNewPolylin(new FGERectPolylin(_deserializedPolylin.getPoints(), getStraightLineWhenPossible(), getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap())); _deserializedPolylin = null; } else { // Was never computed, do it now _updateAsAutoLayout(); } } _updateAsBasicallyAdjustable(); } else /* RectPolylinConnector is adjustable, getAdjustability() == RectPolylinAdjustability.FULLY_ADJUSTABLE */{ _updateAsFullyAdjustable(); /* * if (polylin == null) { * * if (_deserializedPolylin != null) { // Rebuild from deserialized polylin updateWithNewPolylin(new * FGERectPolylin(_deserializedPolylin * .getPoints(),getStraightLineWhenPossible(),getOverlapXResultingFromPixelOverlap(),getOverlapYResultingFromPixelOverlap())); * _deserializedPolylin = null; } * * else { // Was never computed, do it now _updateAsAutoLayout(); } } * * // Attempt to restore some locations from stored locations relative to start and end object // - start point is restored from * relative location to start object and put on starting object outline // - end point is restored from relative location to end * object and put on ending object outline // - first point is also restored from relative location to start object if there are * more than 5 points // - next point is updated accordingly to orientation of second segment // - last point is also restored * from relative location to end object if there are more than 5 points // - previous point is updated accordingly to * orientation of previous-last segment * * * int indexOfMiddleSegment = polylin.getSegments().indexOf(polylin.getMiddleSegment()); * * for (int i=0; i<polylin.getPointsNb(); i++) { if (i<=indexOfMiddleSegment && polylinRelativeToStartObject != null) { // That * point is closest to start object // remember location stored relative to start object FGEPoint pointRelativeToStartObject = * polylinRelativeToStartObject.getPointAt(i); if (i==0) { // This is the start object, when not, put it on starting object * shape outline pointRelativeToStartObject = * getStartObject().getShape().getOutline().nearestOutlinePoint(pointRelativeToStartObject); polylin.updatePointAt(i, * GraphicalRepresentation.convertNormalizedPoint( getStartObject(), pointRelativeToStartObject, getGraphicalRepresentation())); * } else if (i==1 && polylin.getPointsNb() >=6) { FGEPoint firstPoint = GraphicalRepresentation.convertNormalizedPoint( * getStartObject(), pointRelativeToStartObject, getGraphicalRepresentation()); FGEPoint nextPoint = polylin.getPointAt(2); if * (polylinRelativeToStartObject.getSegmentAt(1).getApproximatedOrientation().isHorizontal()) { nextPoint.y = firstPoint.y; } * else { nextPoint.x = firstPoint.x; } polylin.updatePointAt(i,firstPoint); polylin.updatePointAt(i+1,nextPoint); } } else if * (polylinRelativeToEndObject != null) { // That point is closest to end object // remember location stored relative to end * object FGEPoint pointRelativeToEndObject = polylinRelativeToEndObject.getPointAt(i); if (i==polylin.getPointsNb()-1) { // * This is the end object, when not, put it on ending object shape outline pointRelativeToEndObject = * getEndObject().getShape().getOutline().nearestOutlinePoint(pointRelativeToEndObject); polylin.updatePointAt(i, * GraphicalRepresentation.convertNormalizedPoint( getEndObject(), pointRelativeToEndObject, getGraphicalRepresentation())); } * else if (i==polylin.getPointsNb()-2 && polylin.getPointsNb() >=6) { FGEPoint lastPoint = * GraphicalRepresentation.convertNormalizedPoint( getEndObject(), pointRelativeToEndObject, getGraphicalRepresentation()); * FGEPoint previousPoint = polylin.getPointAt(polylin.getPointsNb()-3); if * (polylinRelativeToEndObject.getSegmentAt(polylinRelativeToEndObject * .getSegmentNb()-2).getApproximatedOrientation().isHorizontal()) { previousPoint.y = previousPoint.y; } else { previousPoint.x * = previousPoint.x; } polylin.updatePointAt(i,lastPoint); polylin.updatePointAt(i-1,previousPoint); } } } * * updateAndNormalizeCurrentPolylin(); */ } } /** * Compute and return start area outline, in the connector coordinates system * * If some orientation constraints are defined, return portion of start area outline matching allowed orientations * * If starting location is fixed return this location * * @return FGEArea */ protected FGEArea retrieveStartArea() { FGEArea startArea = retrieveAllowedStartArea(true); if (getIsStartingLocationFixed() && getFixedStartLocation() != null) { FGEPoint fixedPoint = GraphicalRepresentation.convertNormalizedPoint(getStartObject(), getFixedStartLocation(), getGraphicalRepresentation()); /* * if (startArea instanceof FGEShape) { return ((FGEShape<?>)startArea).nearestOutlinePoint(fixedPoint); } else */FGEPoint returned = startArea.getNearestPoint(fixedPoint); if (!startArea.containsPoint(returned)) { logger.warning("Inconsistent data: point " + returned + " not located on area: " + startArea + " [was: " + fixedPoint + "]"); } return returned; } return startArea; /* * AffineTransform at1 = GraphicalRepresentation.convertNormalizedCoordinatesAT( getStartObject(), getGraphicalRepresentation()); * * FGEArea startArea = getStartObject().getShape().getShape().transform(at1); if (startArea instanceof FGEShape) { * ((FGEShape<?>)startArea).setIsFilled(false); } * * Vector<SimplifiedCardinalDirection> allowedStartOrientations = getAllowedStartOrientations(); * * if (getIsStartingLocationFixed() && getFixedStartLocation() != null) { if (startArea instanceof FGEShape) { FGEPoint startPoint = * ((FGEShape<?>)startArea).nearestOutlinePoint(GraphicalRepresentation.convertNormalizedPoint(getStartObject(), * getFixedStartLocation(), getGraphicalRepresentation())); Vector<SimplifiedCardinalDirection> * allowedStartOrientationsBecauseOfFixedPoint = new Vector<SimplifiedCardinalDirection>(); for (SimplifiedCardinalDirection o : * SimplifiedCardinalDirection.values()) { if (startArea.getOrthogonalPerspectiveArea(o).containsPoint(startPoint)) { * allowedStartOrientationsBecauseOfFixedPoint.add(o); } } Vector<SimplifiedCardinalDirection> resultingAllowedOrientations = * SimplifiedCardinalDirection.intersection(allowedStartOrientations, allowedStartOrientationsBecauseOfFixedPoint); if * (resultingAllowedOrientations.size() > 0) allowedStartOrientations = resultingAllowedOrientations; // Otherwise, cannot respect * this constraint, give up logger.warning("start area is a point !!!"); startArea = startPoint; } } * * return startArea; */ } /** * Compute and return allowed start area, in the connector coordinates system If some orientation constraints are defined, return * portion of start area outline matching allowed orientations * * @return FGEArea */ protected FGEArea retrieveAllowedStartArea(boolean takeFixedControlPointUnderAccount) { AffineTransform at1 = GraphicalRepresentation.convertNormalizedCoordinatesAT(getStartObject(), getGraphicalRepresentation()); FGEArea startArea = getStartObject().getAllowedStartAreaForConnector(getGraphicalRepresentation()).transform(at1); /* * if (startArea instanceof FGEShape) { ((FGEShape<?>)startArea).setIsFilled(false); } */ Vector<SimplifiedCardinalDirection> allowedStartOrientations = takeFixedControlPointUnderAccount ? getAllowedStartOrientations() : getPrimitiveAllowedStartOrientations(); if (allowedStartOrientations.size() > 0 && allowedStartOrientations.size() < 4) { // Some directions should not be available Vector<FGEArea> allowedAreas = new Vector<FGEArea>(); for (SimplifiedCardinalDirection o : allowedStartOrientations) { // allowedAreas.add(startArea.intersect(startArea.getOrthogonalPerspectiveArea(o))); allowedAreas.add(startArea.getAnchorAreaFrom(o)); /* * logger.info("Orientation: "+o); logger.info("startArea="+startArea); * logger.info("ortho="+startArea.getOrthogonalPerspectiveArea(o)); * logger.info("result="+startArea.intersect(startArea.getOrthogonalPerspectiveArea(o))); */ } return FGEUnionArea.makeUnion(allowedAreas); } else if (allowedStartOrientations.size() == 0) { logger.warning("Cannot respect starting orientation constraints"); } return startArea; } /** * Compute and return end area outline, in the connector coordinates system * * If some orientation constraints are defined, return portion of end area outline matching allowed orientations * * If starting location is fixed return this location * * @return FGEArea */ protected FGEArea retrieveEndArea() { // System.out.println("retrieveAllowedEndArea()="+retrieveAllowedEndArea()); FGEArea endArea = retrieveAllowedEndArea(true); if (getIsEndingLocationFixed() && getFixedEndLocation() != null) { FGEPoint fixedPoint = GraphicalRepresentation.convertNormalizedPoint(getEndObject(), getFixedEndLocation(), getGraphicalRepresentation()); /* * if (endArea instanceof FGEShape) { return ((FGEShape<?>)endArea).nearestOutlinePoint(fixedPoint); } else */ FGEPoint returned = endArea.getNearestPoint(fixedPoint); if (!endArea.containsPoint(returned)) { logger.warning("Inconsistent data: point " + returned + " not located on area: " + endArea + " [was: " + fixedPoint + "]"); } return returned; } return endArea; /* * AffineTransform at2 = GraphicalRepresentation.convertNormalizedCoordinatesAT( getEndObject(), getGraphicalRepresentation()); * * FGEArea endArea = getEndObject().getShape().getShape().transform(at2); if (endArea instanceof FGEShape) { * ((FGEShape<?>)endArea).setIsFilled(false); } * * Vector<SimplifiedCardinalDirection> allowedEndOrientations = getAllowedEndOrientations(); * * if (getIsEndingLocationFixed() && getFixedEndLocation() != null) { if (endArea instanceof FGEShape) { FGEPoint endPoint = * ((FGEShape<?>)endArea).nearestOutlinePoint(GraphicalRepresentation.convertNormalizedPoint(getEndObject(), getFixedEndLocation(), * getGraphicalRepresentation())); Vector<SimplifiedCardinalDirection> allowedEndOrientationsBecauseOfFixedPoint = new * Vector<SimplifiedCardinalDirection>(); for (SimplifiedCardinalDirection o : SimplifiedCardinalDirection.values()) { if * (endArea.getOrthogonalPerspectiveArea(o).containsPoint(endPoint)) { allowedEndOrientationsBecauseOfFixedPoint.add(o); } } * Vector<SimplifiedCardinalDirection> resultingAllowedOrientations = * SimplifiedCardinalDirection.intersection(allowedEndOrientations, allowedEndOrientationsBecauseOfFixedPoint); if * (resultingAllowedOrientations.size() > 0) allowedEndOrientations = resultingAllowedOrientations; // Otherwise, cannot respect * this constraint, give up endArea = endPoint; } } * * return endArea; */ } /** * Compute and return allowed end area, in the connector coordinates system If some orientation constraints are defined, return portion * of end area outline matching allowed orientations * * @return FGEArea */ protected FGEArea retrieveAllowedEndArea(boolean takeFixedControlPointUnderAccount) { AffineTransform at2 = GraphicalRepresentation.convertNormalizedCoordinatesAT(getEndObject(), getGraphicalRepresentation()); FGEArea endArea = getEndObject().getAllowedEndAreaForConnector(getGraphicalRepresentation()).transform(at2); /* * if (endArea instanceof FGEShape) { ((FGEShape<?>)endArea).setIsFilled(false); } */ Vector<SimplifiedCardinalDirection> allowedEndOrientations = takeFixedControlPointUnderAccount ? getAllowedEndOrientations() : getPrimitiveAllowedEndOrientations(); if (allowedEndOrientations.size() > 0 && allowedEndOrientations.size() < 4) { // Some directions should not be available Vector<FGEArea> allowedAreas = new Vector<FGEArea>(); for (SimplifiedCardinalDirection o : allowedEndOrientations) { // allowedAreas.add(endArea.intersect(endArea.getOrthogonalPerspectiveArea(o))); allowedAreas.add(endArea.getAnchorAreaFrom(o)); // System.out.println("Orientation: "+o+" ortho: "+endArea.getOrthogonalPerspectiveArea(o)+" intersect="+endArea.intersect(endArea.getOrthogonalPerspectiveArea(o))); } return FGEUnionArea.makeUnion(allowedAreas); } else if (allowedEndOrientations.size() == 0) { logger.warning("Cannot respect ending orientation constraints"); } return endArea; } /** * Internal method called to update connector asserting layout is automatically performed Whatever happen, compute the shortest polylin * respecting start and end constraints (positions and/or orientations) This new polylin is automatically recomputed and set to current * connector. * */ private void _updateAsAutoLayout() { final FGEArea startArea = retrieveStartArea(); final FGEArea endArea = retrieveEndArea(); // logger.info("startArea="+startArea); // logger.info("endArea="+endArea); FGEPoint startMiddle = null; if (startArea.isFinite()) { FGERectangle startAreaBounds = startArea.getEmbeddingBounds(); if (startAreaBounds != null) { startMiddle = startAreaBounds.getCenter(); } } if (startMiddle == null) { logger.warning("Could not find middle of resulting start area: " + startArea); startMiddle = new FGEPoint(0, 0); } FGEPoint endMiddle = null; if (endArea.isFinite()) { FGERectangle endAreaBounds = endArea.getEmbeddingBounds(); if (endAreaBounds != null) { endMiddle = endAreaBounds.getCenter(); } } if (endMiddle == null) { logger.warning("Could not find middle of resulting start area: " + startArea); endMiddle = new FGEPoint(1, 1); } // First obtain the two affine transform allowing to convert from // extremity objects coordinates to connector drawable /* * AffineTransform at1 = GraphicalRepresentation.convertNormalizedCoordinatesAT( getStartObject(), getGraphicalRepresentation()); * * AffineTransform at2 = GraphicalRepresentation.convertNormalizedCoordinatesAT( getEndObject(), getGraphicalRepresentation()); * * FGEPoint startMiddle = getStartObject().getShape().getShape().getCenter().transform(at1); FGEPoint endMiddle = * getEndObject().getShape().getShape().getCenter().transform(at2); */ Vector<SimplifiedCardinalDirection> potentialStartOrientations = new Vector<SimplifiedCardinalDirection>(); Vector<SimplifiedCardinalDirection> potentialEndOrientations = new Vector<SimplifiedCardinalDirection>(); boolean testAllCombinations = true; // logger.info("getRectPolylinConstraints()="+getRectPolylinConstraints()+" getEndOrientation()="+getEndOrientation()); if (getRectPolylinConstraints() == RectPolylinConstraints.ORIENTATIONS_FIXED) { potentialStartOrientations.add(getStartOrientation()); potentialEndOrientations.add(getEndOrientation()); } else { CardinalQuadrant quadrant = FGEPoint.getCardinalQuadrant(startMiddle, endMiddle); RectPolylinConstraints constraints = getRectPolylinConstraints(); if (constraints == RectPolylinConstraints.START_ORIENTATION_FIXED || constraints == RectPolylinConstraints.END_ORIENTATION_FIXED) { constraints = RectPolylinConstraints.NONE; } if (constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT) { testAllCombinations = false; } if (constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { testAllCombinations = false; } if (quadrant == CardinalQuadrant.NORTH_EAST) { if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); } if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); } } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.HORIZONTAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.VERTICAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); } } else if (quadrant == CardinalQuadrant.SOUTH_EAST) { if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); } if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); } } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.HORIZONTAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.VERTICAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); } } else if (quadrant == CardinalQuadrant.SOUTH_WEST) { if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); } if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); } } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.HORIZONTAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.VERTICAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); } } else /* if (quadrant == CardinalQuadrant.NORTH_WEST) */{ if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST || constraints == RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_HORIZONTAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); } if (constraints != RectPolylinConstraints.ORTHOGONAL_LAYOUT_VERTICAL_FIRST) { potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); } } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.HORIZONTAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); potentialStartOrientations.add(SimplifiedCardinalDirection.WEST); potentialEndOrientations.add(SimplifiedCardinalDirection.WEST); potentialStartOrientations.add(SimplifiedCardinalDirection.EAST); potentialEndOrientations.add(SimplifiedCardinalDirection.EAST); } if (constraints == RectPolylinConstraints.NONE || constraints == RectPolylinConstraints.VERTICAL_LAYOUT || constraints == RectPolylinConstraints.HORIZONTAL_OR_VERTICAL_LAYOUT) { potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialStartOrientations.add(SimplifiedCardinalDirection.NORTH); potentialEndOrientations.add(SimplifiedCardinalDirection.NORTH); potentialStartOrientations.add(SimplifiedCardinalDirection.SOUTH); potentialEndOrientations.add(SimplifiedCardinalDirection.SOUTH); } } if (getRectPolylinConstraints() == RectPolylinConstraints.START_ORIENTATION_FIXED) { potentialStartOrientations.clear(); potentialStartOrientations.add(getStartOrientation()); } if (getRectPolylinConstraints() == RectPolylinConstraints.END_ORIENTATION_FIXED) { potentialEndOrientations.clear(); potentialEndOrientations.add(getEndOrientation()); } } /* * FGEArea startArea = getStartObject().getShape().getShape().transform(at1); FGEArea endArea = * getEndObject().getShape().getShape().transform(at2); */ potentialPolylin.clear(); double minimalLength = Double.POSITIVE_INFINITY; SimplifiedCardinalDirection choosenStartOrientation = null; SimplifiedCardinalDirection choosenEndOrientation = null; Vector<SimplifiedCardinalDirection> allowedStartOrientations = getAllowedStartOrientations(); Vector<SimplifiedCardinalDirection> allowedEndOrientations = getAllowedEndOrientations(); if (!testAllCombinations && potentialStartOrientations.size() != potentialEndOrientations.size()) { logger.warning("Inconsistent data: potentialStartOrientations.size() != potentialEndOrientations.size()"); } if (testAllCombinations) { // Remove doublons Vector<SimplifiedCardinalDirection> newPotentialStartOrientations = new Vector<SimplifiedCardinalDirection>(); for (SimplifiedCardinalDirection o : potentialStartOrientations) { if (!newPotentialStartOrientations.contains(o)) { newPotentialStartOrientations.add(o); } } potentialStartOrientations = newPotentialStartOrientations; Vector<SimplifiedCardinalDirection> newPotentialEndOrientations = new Vector<SimplifiedCardinalDirection>(); for (SimplifiedCardinalDirection o : potentialEndOrientations) { if (!newPotentialEndOrientations.contains(o)) { newPotentialEndOrientations.add(o); } } potentialEndOrientations = newPotentialEndOrientations; } // !!! Faire des methodes pour ca !!! /* * logger.info("Looking for best polylin"); logger.info("potentialStartOrientations="+potentialStartOrientations); * logger.info("potentialEndOrientations="+potentialEndOrientations); */ if (!testAllCombinations && potentialStartOrientations.size() == potentialEndOrientations.size()) { for (int i = 0; i < potentialStartOrientations.size(); i++) { if (allowedStartOrientations.contains(potentialStartOrientations.get(i)) && allowedEndOrientations.contains(potentialEndOrientations.get(i))) { FGERectPolylin newPolylin = new FGERectPolylin(startArea, potentialStartOrientations.get(i), endArea, potentialEndOrientations.get(i), getStraightLineWhenPossible(), getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); potentialPolylin.add(newPolylin); if (newPolylin.getPointsNb() > 0 && newPolylin.getLength() < minimalLength + FGEGeometricObject.EPSILON /* * Hysteresis to * avoid * blinking */) { polylin = newPolylin; minimalLength = newPolylin.getLength(); choosenStartOrientation = potentialStartOrientations.get(i); choosenEndOrientation = potentialEndOrientations.get(i); } } } } else { for (SimplifiedCardinalDirection startOrientation : potentialStartOrientations) { if (allowedStartOrientations.contains(startOrientation)) { for (SimplifiedCardinalDirection endOrientation : potentialEndOrientations) { if (allowedEndOrientations.contains(endOrientation)) { FGERectPolylin newPolylin = new FGERectPolylin(startArea, startOrientation, endArea, endOrientation, getStraightLineWhenPossible(), getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); potentialPolylin.add(newPolylin); if (newPolylin.doesRespectAllConstraints() && newPolylin.getLength() < minimalLength + FGEGeometricObject.EPSILON /* * Hysteresis * to * avoid * blinking */) { polylin = newPolylin; minimalLength = newPolylin.getLength(); choosenStartOrientation = startOrientation; choosenEndOrientation = endOrientation; } } } } } } startOrientation = polylin.getStartOrientation(); endOrientation = polylin.getEndOrientation(); // logger.info("Best polylin found from/to "+startOrientation+"/"+endOrientation+" with "+polylin.getPointsNb()+" points"); // logger.info("Polylin="+polylin); if (startOrientation != choosenStartOrientation) { logger.warning("Requested start orientation was: " + choosenStartOrientation + " but is finally: " + startOrientation); } if (endOrientation != choosenEndOrientation) { logger.warning("Requested end orientation was: " + choosenEndOrientation + " but is finally: " + endOrientation); } // logger.info("Before update, polylin from/to "+startOrientation+"/"+endOrientation+" with "+polylin.getPointsNb()+" points"); // logger.info("Polylin="+polylin); // updateWithNewPolylin(polylin,true); if (polylin.isNormalized()) { updateWithNewPolylin(polylin, true, false); } else { logger.warning("Result of auto-layout computing returned a non-normalized polylin. Please investigate"); updateWithNewPolylin(polylin, false, false); } // logger.info("After update, polylin from/to "+startOrientation+"/"+endOrientation+" with "+polylin.getPointsNb()+" points"); // logger.info("Polylin="+polylin); } /** * Internal method called to update connector asserting layout is defined as BASICALLY_ADJUSTABLE. If crossedControlPoint is defined, * compute the shortest polylin crossing this point and respecting start and end constraints (positions and/or orientations) If no * crossedControlPoint is defined, compute the shortest polylin respecting start and end constraints (positions and/or orientations) */ private void _updateAsBasicallyAdjustable() { final FGEArea startArea = retrieveStartArea(); final FGEArea endArea = retrieveEndArea(); Vector<SimplifiedCardinalDirection> allowedStartOrientations = getAllowedStartOrientations(); Vector<SimplifiedCardinalDirection> allowedEndOrientations = getAllowedEndOrientations(); FGERectPolylin newPolylin; FGEAreaProvider<SimplifiedCardinalDirection> startAreaProvider = getIsStartingLocationFixed() ? new DefaultAreaProvider<SimplifiedCardinalDirection>( startArea) : new FGEAreaProvider<SimplifiedCardinalDirection>() { @Override public FGEArea getArea(SimplifiedCardinalDirection input) { return getStartObject().getAllowedStartAreaForConnectorForDirection(getGraphicalRepresentation(), startArea, input); } }; FGEAreaProvider<SimplifiedCardinalDirection> endAreaProvider = getIsEndingLocationFixed() ? new DefaultAreaProvider<SimplifiedCardinalDirection>( endArea) : new FGEAreaProvider<SimplifiedCardinalDirection>() { @Override public FGEArea getArea(SimplifiedCardinalDirection input) { return getEndObject().getAllowedEndAreaForConnectorForDirection(getGraphicalRepresentation(), endArea, input); } }; if (_crossedPoint != null) { // System.out.println("startArea="+startArea); // System.out.println("endArea="+endArea); newPolylin = FGERectPolylin.makeRectPolylinCrossingPoint(startAreaProvider, endAreaProvider, _crossedPoint, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap(), SimplifiedCardinalDirection.allDirectionsExcept(allowedStartOrientations), SimplifiedCardinalDirection.allDirectionsExcept(allowedEndOrientations)); } else { newPolylin = FGERectPolylin.makeShortestRectPolylin(startAreaProvider, endAreaProvider, getStraightLineWhenPossible(), getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap(), SimplifiedCardinalDirection.allDirectionsExcept(allowedStartOrientations), SimplifiedCardinalDirection.allDirectionsExcept(allowedEndOrientations)); } if (newPolylin == null) { logger.warning("Obtained null polylin allowedStartOrientations=" + allowedStartOrientations); return; } if (newPolylin.isNormalized()) { updateWithNewPolylin(newPolylin, true, false); } else { logger.warning("Result of basically_adjustable layout computing returned a non-normalized polylin. Please investigate"); updateWithNewPolylin(newPolylin, false, false); } } /** * Internal method called to update connector asserting layout is defined as FULLY_ADJUSTABLE. */ private void _updateAsFullyAdjustable() { if (polylin == null) { if (_deserializedPolylin != null) { // Rebuild from deserialized polylin updateWithNewPolylin(new FGERectPolylin(_deserializedPolylin.getPoints(), getStraightLineWhenPossible(), getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap())); _deserializedPolylin = null; } else { // Was never computed, do it now _updateAsAutoLayout(); } } // Special case where there is a unique segment // Used when connector is restructuring (start and/or end shapes are moving or resizing) if (lastKnownCleanPolylinBeforeConnectorRestructuration != null && lastKnownCleanPolylinBeforeConnectorRestructuration.getSegmentNb() == 1 && polylinRelativeToStartObject != null && polylinRelativeToEndObject != null) { FGEPoint lastStartPoint = polylinRelativeToStartObject.getFirstPoint(); FGEPoint lastEndPoint = polylinRelativeToEndObject.getLastPoint(); lastStartPoint = GraphicalRepresentation.convertNormalizedPoint(getStartObject(), lastStartPoint, getGraphicalRepresentation()); lastEndPoint = GraphicalRepresentation.convertNormalizedPoint(getEndObject(), lastEndPoint, getGraphicalRepresentation()); FGEPoint pt = FGEPoint.getMiddlePoint(lastStartPoint, lastEndPoint); if (_updateAsFullyAdjustableForUniqueSegment(pt)) { return; } } // Special case where there is a unique segment // Used when connector beeing edited if (polylin != null && polylin.getSegmentNb() == 1) { FGEPoint lastStartPoint = polylin.getFirstPoint(); FGEPoint lastEndPoint = polylin.getLastPoint(); FGEPoint pt = FGEPoint.getMiddlePoint(lastStartPoint, lastEndPoint); if (_updateAsFullyAdjustableForUniqueSegment(pt)) { return; } } // Attempt to restore some locations from stored locations relative to start and end object // - start point is restored from relative location to start object and put on starting object outline // - end point is restored from relative location to end object and put on ending object outline // - first point is also restored from relative location to start object if there are more than 5 points // - next point is updated accordingly to orientation of second segment // - last point is also restored from relative location to end object if there are more than 5 points // - previous point is updated accordingly to orientation of previous-last segment int indexOfMiddleSegment = polylin.getSegments().indexOf(polylin.getMiddleSegment()); for (int i = 0; i < polylin.getPointsNb(); i++) { if (i <= indexOfMiddleSegment && polylinRelativeToStartObject != null) { // That point is closest to start object // remember location stored relative to start object FGEPoint pointRelativeToStartObject = polylinRelativeToStartObject.getPointAt(i); if (pointRelativeToStartObject != null) { if (i == 0) { // This is the start object, when not, put it on starting object shape outline pointRelativeToStartObject = getStartObject().getAllowedStartAreaForConnector(getGraphicalRepresentation()) .getNearestPoint(pointRelativeToStartObject); polylin.updatePointAt(i, GraphicalRepresentation.convertNormalizedPoint(getStartObject(), pointRelativeToStartObject, getGraphicalRepresentation())); } else if (i == 1 && polylin.getPointsNb() >= 6) { FGEPoint firstPoint = GraphicalRepresentation.convertNormalizedPoint(getStartObject(), pointRelativeToStartObject, getGraphicalRepresentation()); FGEPoint nextPoint = polylin.getPointAt(2); if (polylinRelativeToStartObject.getSegmentAt(1) != null && polylinRelativeToStartObject.getSegmentAt(1).getApproximatedOrientation().isHorizontal()) { nextPoint.y = firstPoint.y; } else { nextPoint.x = firstPoint.x; } polylin.updatePointAt(i, firstPoint); polylin.updatePointAt(i + 1, nextPoint); } } } else if (polylinRelativeToEndObject != null) { // That point is closest to end object // remember location stored relative to end object FGEPoint pointRelativeToEndObject = polylinRelativeToEndObject.getPointAt(i); if (pointRelativeToEndObject != null) { if (i == polylin.getPointsNb() - 1) { // This is the end object, when not, put it on ending object shape outline pointRelativeToEndObject = getEndObject().getAllowedEndAreaForConnector(getGraphicalRepresentation()) .getNearestPoint(pointRelativeToEndObject); polylin.updatePointAt(i, GraphicalRepresentation.convertNormalizedPoint(getEndObject(), pointRelativeToEndObject, getGraphicalRepresentation())); } else if (i == polylin.getPointsNb() - 2 && polylin.getPointsNb() >= 6) { FGEPoint lastPoint = GraphicalRepresentation.convertNormalizedPoint(getEndObject(), pointRelativeToEndObject, getGraphicalRepresentation()); FGEPoint previousPoint = polylin.getPointAt(polylin.getPointsNb() - 3); if (polylinRelativeToEndObject.getSegmentAt(polylinRelativeToEndObject.getSegmentNb() - 2) != null && polylinRelativeToEndObject.getSegmentAt(polylinRelativeToEndObject.getSegmentNb() - 2) .getApproximatedOrientation().isHorizontal()) { previousPoint.y = previousPoint.y; } else { previousPoint.x = previousPoint.x; } polylin.updatePointAt(i, lastPoint); polylin.updatePointAt(i - 1, previousPoint); } } } } /* * * FGEPoint newStartCPLocation = polylin.getFirstPoint(); //startCPRelativeToStartObject = * getStartObject().getShape().outlineIntersect(startCPRelativeToStartObject); startCPRelativeToStartObject = * getStartObject().getShape().getShape().getNearestPoint(startCPRelativeToStartObject); if (startCPRelativeToStartObject != null) { * newStartCPLocation = GraphicalRepresentation.convertNormalizedPoint( getStartObject(), startCPRelativeToStartObject, * getGraphicalRepresentation()); polylin.updatePointAt(0, newStartCPLocation); } * * FGEPoint newEndCPLocation = polylin.getLastPoint(); //endCPRelativeToEndObject = * getEndObject().getShape().outlineIntersect(endCPRelativeToEndObject); endCPRelativeToEndObject = * getEndObject().getShape().getShape().getNearestPoint(endCPRelativeToEndObject); if (endCPRelativeToEndObject != null) { * newEndCPLocation = GraphicalRepresentation.convertNormalizedPoint( getEndObject(), endCPRelativeToEndObject, * getGraphicalRepresentation()); polylin.updatePointAt(polylin.getPointsNb()-1, newEndCPLocation); } */ updateAndNormalizeCurrentPolylin(); } void updateWithNewPolylin(FGERectPolylin aPolylin) { updateWithNewPolylin(aPolylin, false, false); } void updateWithNewPolylin(FGERectPolylin aPolylin, boolean temporary) { updateWithNewPolylin(aPolylin, false, temporary); } void updateWithNewPolylin(FGERectPolylin aPolylin, boolean assertLayoutIsValid, boolean temporary) { // logger.info("updateWithNewPolylin()"); if (logger.isLoggable(Level.FINE)) { logger.fine("Update with polylin with " + aPolylin.getPointsNb() + " points"); } if (aPolylin.hasExtraPoints()) { aPolylin.removeExtraPoints(); } polylin = aPolylin; _rebuildControlPoints(); _connectorChanged(temporary); if (!assertLayoutIsValid) { updateAndNormalizeCurrentPolylin(); } } /** * Internal method restoring valid layout given current polylin. Current polylin is used and adapted to new conditions (eg start and/or * end object have been modified (moved or resized) * * Normalize obtained polylin at the end of process * */ private void updateAndNormalizeCurrentPolylin() { if (!getGraphicalRepresentation().isRegistered()) { return; } if (isCleaningPolylin) { // Avoid infinite loop return; } if (logger.isLoggable(Level.FINER)) { logger.finer("updateAndNormalizeCurrentPolylin()"); } isCleaningPolylin = true; try { // First, check and update start and end control points checkAndUpdateStartCP(lastKnownCleanPolylinBeforeConnectorRestructuration != null ? lastKnownCleanPolylinBeforeConnectorRestructuration : polylin); checkAndUpdateEndCP(lastKnownCleanPolylinBeforeConnectorRestructuration != null ? lastKnownCleanPolylinBeforeConnectorRestructuration : polylin); if (polylin.isNormalized()) { return; } if (polylin.isNormalizable()) { polylin.normalize(); for (int i = 0; i < polylin.getPointsNb(); i++) { controlPoints.elementAt(i).setPoint(polylin.getPointAt(i)); } } else { if (logger.isLoggable(Level.FINER)) { logger.finer("RectPolylin layout changed"); } // Layout has changed, update with new normalized polylin updateWithNewPolylin(polylin.makeNormalizedRectPolylin(), false, true); } } finally { isCleaningPolylin = false; } } /** * Internal method rebuilding control points */ private void _rebuildControlPoints() { controlPoints.clear(); controlAreas.clear(); if (polylin == null) { return; } int nPoints = polylin.getPoints().size(); switch (getAdjustability()) { case AUTO_LAYOUT: for (int i = 0; i < nPoints; i++) { FGEPoint p = polylin.getPointAt(i); if (i == 0 && getIsStartingLocationDraggable()) { controlPoints.add(new AdjustableStartControlPoint(p, this)); } else if (i == nPoints - 1 && getIsEndingLocationDraggable()) { controlPoints.add(new AdjustableEndControlPoint(p, this)); } else { controlPoints.add(new ConnectorNonAdjustableControlPoint(getGraphicalRepresentation(), p)); } } break; case BASICALLY_ADJUSTABLE: for (int i = 0; i < nPoints; i++) { FGEPoint p = polylin.getPointAt(i); if (i == 0 && getIsStartingLocationDraggable()) { controlPoints.add(new AdjustableStartControlPoint(p, this)); } else if (i == nPoints - 1 && getIsEndingLocationDraggable()) { controlPoints.add(new AdjustableEndControlPoint(p, this)); } else { controlPoints.add(new AdjustableIntermediateControlPoint(p, -1, this)); } } controlAreas.add(new RectPolylinAdjustingArea(this)); /* * if (_crossedPoint != null) { if (crossedControlPoint == null) { crossedControlPoint = new * ConnectorNonAdjustableControlPoint(getGraphicalRepresentation(),_crossedPoint); } * crossedControlPoint.setPoint(_crossedPoint); } if (crossedControlPoint != null) controlAreas.add(crossedControlPoint); */ break; case FULLY_ADJUSTABLE: if (nPoints >= 2) { if (getIsStartingLocationDraggable()) { controlPoints.add(new AdjustableStartControlPoint(polylin.getFirstPoint(), this)); } else { controlPoints.add(new ConnectorNonAdjustableControlPoint(getGraphicalRepresentation(), polylin.getFirstPoint())); } if (nPoints == 3) { controlPoints.add(new AdjustableMiddleControlPoint(polylin.getPoints().elementAt(1), this)); } else if (nPoints > 3) { // nPoints > 3 controlPoints.add(new AdjustableFirstControlPoint(polylin.getPoints().elementAt(1), this)); for (int i = 2; i < nPoints - 2; i++) { controlPoints.add(new AdjustableIntermediateControlPoint(polylin.getPoints().elementAt(i), i, this)); } controlPoints.add(new AdjustableLastControlPoint(polylin.getPoints().elementAt(nPoints - 2), this)); } if (getIsEndingLocationDraggable()) { controlPoints.add(new AdjustableEndControlPoint(polylin.getLastPoint(), this)); } else { controlPoints.add(new ConnectorNonAdjustableControlPoint(getGraphicalRepresentation(), polylin.getLastPoint())); } } int nSegments = polylin.getSegments().size(); if (nSegments < 1) { // Pathologic case logger.warning("Unexpected situation here"); } else if (nSegments == 1) { if ((!getIsStartingLocationFixed() || getIsStartingLocationDraggable()) && (!getIsEndingLocationFixed() || getIsEndingLocationDraggable())) { controlAreas.add(new AdjustableUniqueSegment(polylin.getFirstSegment(), this)); } } else { if (!getIsStartingLocationFixed() || getIsStartingLocationDraggable()) { controlAreas.add(new AdjustableFirstSegment(polylin.getFirstSegment(), this)); } for (int i = 1; i < nSegments - 1; i++) { FGESegment s = polylin.getSegmentAt(i); controlAreas.add(new AdjustableIntermediateSegment(s, this)); } if (!getIsEndingLocationFixed() || getIsEndingLocationDraggable()) { controlAreas.add(new AdjustableLastSegment(polylin.getLastSegment(), this)); } } break; default: break; } } /** * Internal method generally called at the end of updating process Internally store polylin relative to start and end objects * */ void _connectorChanged(boolean temporary) { if (getGraphicalRepresentation().isRegistered() && !temporary) { if (polylin != null) { if (GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(getGraphicalRepresentation(), getStartObject())) { AffineTransform at1 = GraphicalRepresentation.convertNormalizedCoordinatesAT(getGraphicalRepresentation(), getStartObject()); polylinRelativeToStartObject = polylin.transform(at1); } // Otherwise, don't try to remember layout, edge is probably beeing deleted if (GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(getGraphicalRepresentation(), getEndObject())) { AffineTransform at2 = GraphicalRepresentation.convertNormalizedCoordinatesAT(getGraphicalRepresentation(), getEndObject()); polylinRelativeToEndObject = polylin.transform(at2); } // Otherwise, don't try to remember layout, edge is probably beeing deleted } } if (controlPoints.size() > 0) { p_start = controlPoints.firstElement(); p_end = controlPoints.lastElement(); } if (getGraphicalRepresentation().getMiddleSymbol() != MiddleSymbolType.NONE) { updateMiddleSymbolLocationControlPoint(); } } /** * Internal method called to update connector asserting layout is defined as FULLY_ADJUSTABLE, and when the last known polylin was a * single segment */ protected boolean _updateAsFullyAdjustableForUniqueSegment(FGEPoint pt) { FGEArea startArea = retrieveAllowedStartArea(true); if (getIsStartingLocationFixed() && !getIsStartingLocationDraggable()) { // If starting location is fixed and not draggable, // Then retrieve start area itself (which is here a single point) startArea = retrieveStartArea(); } FGEArea endArea = retrieveAllowedEndArea(true); if (getIsEndingLocationFixed() && !getIsEndingLocationDraggable()) { // If starting location is fixed and not draggable, // Then retrieve start area itself (which is here a single point) endArea = retrieveEndArea(); } Vector<SimplifiedCardinalDirection> allowedStartOrientations = getAllowedStartOrientations(); Vector<SimplifiedCardinalDirection> allowedEndOrientations = getAllowedEndOrientations(); SimplifiedCardinalDirection orientation = null; FGEPoint newPt = null; for (SimplifiedCardinalDirection o1 : allowedStartOrientations) { for (SimplifiedCardinalDirection o2 : allowedEndOrientations) { if (o1 == o2.getOpposite()) { FGEArea startOrthogonalPerspectiveArea = startArea.getOrthogonalPerspectiveArea(o1); FGEArea endOrthogonalPerspectiveArea = endArea.getOrthogonalPerspectiveArea(o2); FGEArea intersectArea = startOrthogonalPerspectiveArea.intersect(endOrthogonalPerspectiveArea); FGEPoint aPt = intersectArea.getNearestPoint(pt); if (aPt != null) { orientation = o1; newPt = aPt; // System.out.println("orientation:"+orientation+" intersectArea="+intersectArea); } } } } if (orientation != null) { FGEPoint p1 = startArea.nearestPointFrom(newPt, orientation.getOpposite()); FGEPoint p2 = endArea.nearestPointFrom(newPt, orientation); updateWithNewPolylin(new FGERectPolylin(p1, p2), true, false); // System.out.println("Found orientation "+orientation+" p1="+p1+" p2="+p2); return true; } // Could not find a layout with straight segment return false; } /** * This method is internally called while updating starting point of polylin. * * Calling this method assert that we are not sure anymore that start control point is still valid. When not, change this point to a * valid location, and update polylin accordingly (eventually recompute new layout when required: modified orientation). * * @param initialPolylin * Polylin to take under account to recreate new layout * */ void checkAndUpdateStartCP(FGERectPolylin initialPolylin) { if (p_start == null) { _connectorChanged(true); } if (p_start == null) { return; } if (!GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(getStartObject(), getGraphicalRepresentation())) { // Dont't try to do anything, edge is probably beeing deleted return; } // Retrieve start area in connector coordinates system FGEArea startArea = retrieveStartArea(); FGEArea allowedStartArea = retrieveAllowedStartArea(true); if (getIsStartingLocationFixed() && getFixedStartLocation() != null && !getIsStartingLocationDraggable()) { allowedStartArea = getFixedStartLocation(); } if (polylin.getPointsNb() == 0) { return; } // Retrieve control point location to update FGEPoint startCPLocation = polylin.getPointAt(0); if (startCPLocation == null) { return; } // Compute new location by computing nearest point of oldLocation // in end area (if this location was valid, change nothing) // FGEPoint oldCP = startCPLocation.clone(); startCPLocation = startArea.getNearestPoint(startCPLocation); // logger.info("checkAndUpdateStartCP() from "+oldCP+" to "+startCPLocation); // Update polylin and end control point with this new location polylin.updatePointAt(0, startCPLocation); p_start.setPoint(startCPLocation); if (getIsStartingLocationFixed()) { // Don't forget this !!! setFixedStartLocation(GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), startCPLocation, getGraphicalRepresentation().getStartObject())); } // Update for start cp SimplifiedCardinalDirection orientation = polylin.getOrientationOfSegment(0); if (orientation != null) { // This new location is valid // (the last segment is still horizontal or vertical) return; } else { // Start control point has moved (the first segment is not horizontal nor vertical anymore) // Find new orientation by minimizing distance between // current start point location and the nearest point of // all anchor location of all possible directions SimplifiedCardinalDirection newOrientation = null; double bestDistance = Double.POSITIVE_INFINITY; for (SimplifiedCardinalDirection o : getAllowedStartOrientations()) { double distance = FGEPoint.distance(startCPLocation, startArea.getAnchorAreaFrom(o).getNearestPoint(startCPLocation)); if (distance < bestDistance) { newOrientation = o; bestDistance = distance; } } // debugPolylin = null; // Retrieve next point (also called "first" control point) if (polylin.getSegmentAt(0) == null) { logger.warning("Unexpected null first segment. Abort."); return; } FGEPoint nextPoint = polylin.getSegmentAt(0).getP2(); if (allowedStartArea.getOrthogonalPerspectiveArea(newOrientation).containsPoint(nextPoint) /* || (getIsStartingLocationFixed() && getFixedStartLocation() != null) */) { // The general layout of polylin will not change, since next point was // already located in this orthogonal perspective area // We just need here to update previous point according to new end point location FGEPoint newPoint = new FGEPoint(nextPoint); if (newOrientation.isHorizontal()) { newPoint.setY(startCPLocation.y); } else if (newOrientation.isVertical()) { newPoint.setX(startCPLocation.x); } polylin.updatePointAt(1, newPoint); controlPoints.elementAt(1).setPoint(newPoint); } else { // In this case, the situation is worse, that means that start orientation has changed // We need to recompute a new layout for the polylin // Recompute general layout of rect polylin if (initialPolylin.getSegmentNb() > 2) { FGEPoint toPoint = initialPolylin.getPointAt(2); SimplifiedCardinalDirection toPointOrientation = initialPolylin.getApproximatedOrientationOfSegment(2).getOpposite(); FGERectPolylin appendingPath; appendingPath = new FGERectPolylin(startCPLocation, newOrientation, toPoint, toPointOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); // debugPolylin = appendingPath; FGERectPolylin mergedPolylin = mergePolylins(appendingPath, 0, appendingPath.getPointsNb() - 2, initialPolylin, 2, initialPolylin.getPointsNb() - 1); updateWithNewPolylin(mergedPolylin, false, true); } else if (initialPolylin.getSegmentNb() > 1) { FGEPoint toPoint = initialPolylin.getPointAt(2); SimplifiedCardinalDirection toPointOrientation = initialPolylin.getApproximatedOrientationOfSegment(1).getOpposite(); FGERectPolylin appendingPath = new FGERectPolylin(startCPLocation, newOrientation, toPoint, toPointOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); // debugPolylin = appendingPath; FGERectPolylin mergedPolylin = mergePolylins(appendingPath, 0, appendingPath.getPointsNb() - 2, initialPolylin, 2, initialPolylin.getPointsNb() - 1); updateWithNewPolylin(mergedPolylin, false, true); } else { FGEPoint toPoint = initialPolylin.getPointAt(1); toPoint = retrieveEndArea().getNearestPoint(toPoint); newOrientation = initialPolylin.getApproximatedOrientationOfSegment(0); SimplifiedCardinalDirection toPointOrientation = newOrientation.getOpposite(); FGERectPolylin newPolylin = new FGERectPolylin(startCPLocation, newOrientation, toPoint, toPointOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); updateWithNewPolylin(newPolylin, false, true); } } } } /** * This method is internally called while updating ending point of polylin. * * Calling this method assert that we are not sure anymore that end control point is still valid. When not, change this point to a valid * location, and update polylin accordingly (eventually recompute new layout when required: modified orientation). * * @param initialPolylin * Polylin to take under account to recreate new layout * */ void checkAndUpdateEndCP(FGERectPolylin initialPolylin) { if (p_end == null) { _connectorChanged(true); } if (p_end == null) { return; } if (!GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(getEndObject(), getGraphicalRepresentation())) { // Dont't try to do anything, edge is probably beeing deleted return; } // Retrieve end area in connector coordinates system FGEArea endArea = retrieveEndArea(); FGEArea allowedEndArea = retrieveAllowedEndArea(true); if (getIsEndingLocationFixed() && getFixedEndLocation() != null && !getIsEndingLocationDraggable()) { allowedEndArea = getFixedEndLocation(); } if (polylin.getPointsNb() == 0) { return; } // Retrieve control point location to update FGEPoint endCPLocation = polylin.getPointAt(polylin.getPointsNb() - 1); if (endCPLocation == null) { return; } // Compute new location by computing nearest point of oldLocation // in end area (if this location was valid, change nothing) // logger.info("endArea="+endArea); // FGEPoint oldCP = endCPLocation.clone(); endCPLocation = endArea.getNearestPoint(endCPLocation); // logger.info("checkAndUpdateEndCP() from "+oldCP+" to "+endCPLocation); // Update polylin and end control point with this new location polylin.updatePointAt(polylin.getPointsNb() - 1, endCPLocation); p_end.setPoint(endCPLocation); if (getIsEndingLocationFixed()) { // Don't forget this !!! setFixedEndLocation(GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), endCPLocation, getGraphicalRepresentation().getEndObject())); } // Look for orientation of this newly computed segment SimplifiedCardinalDirection orientation = polylin.getOrientationOfSegment(polylin.getSegmentNb() - 1); if (orientation != null) { // This new location is valid // (the last segment is still horizontal or vertical) return; } else { // End control point has moved (the last segment is not horizontal nor vertical anymore) // Find new orientation by minimizing distance between // current end point location and the nearest point of // all anchor location of all possible directions SimplifiedCardinalDirection newOrientation = null; double bestDistance = Double.POSITIVE_INFINITY; for (SimplifiedCardinalDirection o : getAllowedEndOrientations()) { double distance = FGEPoint.distance(endCPLocation, endArea.getAnchorAreaFrom(o).getNearestPoint(endCPLocation)); if (distance < bestDistance - FGEGeometricObject.EPSILON) { newOrientation = o; bestDistance = distance; } } // debugPolylin = null; // Retrieve previous point (also called "last" control point) if (polylin.getSegmentAt(polylin.getSegmentNb() - 1) == null) { logger.warning("Unexpected null last segment. Abort."); return; } FGEPoint previousPoint = polylin.getSegmentAt(polylin.getSegmentNb() - 1).getP1(); if (allowedEndArea.getOrthogonalPerspectiveArea(newOrientation).containsPoint(previousPoint) /* || (getIsEndingLocationFixed() && getFixedEndLocation() != null) */) { // The general layout of polylin will not change, since previous point was // already located in this orthogonal perspective area // We just need here to update previous point according to new end point location FGEPoint newPoint = new FGEPoint(previousPoint); if (newOrientation.isHorizontal()) { newPoint.setY(endCPLocation.y); } else if (newOrientation.isVertical()) { newPoint.setX(endCPLocation.x); } polylin.updatePointAt(polylin.getPointsNb() - 2, newPoint); controlPoints.elementAt(polylin.getPointsNb() - 2).setPoint(newPoint); } else { // In this case, the situation is worse, that means that end orientation has changed // We need to recompute a new layout for the polylin // Recompute general layout of rect polylin if (initialPolylin.getSegmentNb() > 2) { FGEPoint toPoint = initialPolylin.getPointAt(initialPolylin.getPointsNb() - 3); SimplifiedCardinalDirection toPointOrientation = initialPolylin.getApproximatedOrientationOfSegment(initialPolylin .getPointsNb() - 3); FGERectPolylin appendingPath; appendingPath = new FGERectPolylin(toPoint, toPointOrientation, endCPLocation, newOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); // debugPolylin = appendingPath; FGERectPolylin mergedPolylin = mergePolylins(initialPolylin, 0, initialPolylin.getPointsNb() - 3, appendingPath, 1, appendingPath.getPointsNb() - 1); updateWithNewPolylin(mergedPolylin, false, true); } else if (initialPolylin.getSegmentNb() > 1) { FGEPoint toPoint = initialPolylin.getPointAt(initialPolylin.getPointsNb() - 3); SimplifiedCardinalDirection toPointOrientation = initialPolylin.getApproximatedOrientationOfSegment(initialPolylin .getPointsNb() - 3); FGERectPolylin appendingPath = new FGERectPolylin(toPoint, toPointOrientation, endCPLocation, newOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); // debugPolylin = appendingPath; // FGERectPolylin mergedPolylin = mergePolylins(initialPolylin, 0, initialPolylin.getPointsNb()-2, appendingPath, 1, // appendingPath.getPointsNb()-1); // updateWithNewPolylin(mergedPolylin); updateWithNewPolylin(appendingPath, false, true); } else { FGEPoint fromPoint = initialPolylin.getPointAt(0); fromPoint = retrieveStartArea().getNearestPoint(fromPoint); newOrientation = initialPolylin.getApproximatedOrientationOfSegment(0); SimplifiedCardinalDirection toPointOrientation = newOrientation.getOpposite(); FGERectPolylin newPolylin = new FGERectPolylin(fromPoint, newOrientation, endCPLocation, toPointOrientation, true, getOverlapXResultingFromPixelOverlap(), getOverlapYResultingFromPixelOverlap()); updateWithNewPolylin(newPolylin, false, true); } } } } /** * Compute a new polylin by concatening supplied polylins and given indexes * * @param p1 * @param startIndex1 * @param endIndex1 * @param p2 * @param startIndex2 * @param endIndex2 * @return */ FGERectPolylin mergePolylins(FGERectPolylin p1, int startIndex1, int endIndex1, FGERectPolylin p2, int startIndex2, int endIndex2) { FGERectPolylin returned = new FGERectPolylin(); returned.setOverlapX(getOverlapXResultingFromPixelOverlap()); returned.setOverlapY(getOverlapYResultingFromPixelOverlap()); for (int i = startIndex1; i <= endIndex1; i++) { returned.addToPoints(p1.getPointAt(i)); } for (int i = startIndex2; i <= endIndex2; i++) { returned.addToPoints(p2.getPointAt(i)); } return returned; } /** * Simplify layout of current polylin asserting that two points are safelly removable. * * @param index */ void _simplifyLayoutOfCurrentPolylinByDeletingTwoPoints(int index) { _simplifyLayoutOfCurrentPolylinByDeletingTwoPoints(index, null); } /** * Simplify layout of current polylin asserting that two points are safelly removable. If a location is given, this location will be * used to adapt position of previous and next point asserting that they must be located on an horizontal or vertical segment. * * @param index * @param newCPLocation */ void _simplifyLayoutOfCurrentPolylinByDeletingTwoPoints(int index, FGEPoint newCPLocation) { SimplifiedCardinalDirection relatedSegmentOrientation = getCurrentPolylin().getApproximatedOrientationOfSegment(index); getCurrentPolylin().removePointAtIndex(index); getCurrentPolylin().removePointAtIndex(index); _getControlPoints().remove(index); _getControlPoints().remove(index); if (newCPLocation != null) { if (relatedSegmentOrientation.isHorizontal()) { getCurrentPolylin().updatePointAt(index - 1, new FGEPoint(newCPLocation.x, getCurrentPolylin().getPointAt(index - 1).y)); getCurrentPolylin().updatePointAt(index, new FGEPoint(newCPLocation.x, getCurrentPolylin().getPointAt(index).y)); } else if (relatedSegmentOrientation.isVertical()) { getCurrentPolylin().updatePointAt(index - 1, new FGEPoint(getCurrentPolylin().getPointAt(index - 1).x, newCPLocation.y)); getCurrentPolylin().updatePointAt(index, new FGEPoint(getCurrentPolylin().getPointAt(index).x, newCPLocation.y)); } } updateWithNewPolylin(getCurrentPolylin(), false, true); } /** * Return start point, relative to start object * * @return */ @Override public FGEPoint getStartLocation() { if (polylin == null) { return null; } FGEPoint returned = GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), polylin.getFirstPoint(), getStartObject()); returned = getStartObject().getShape().getShape().getNearestPoint(returned); return returned; } /** * Return end point, relative to end object * * @return */ @Override public FGEPoint getEndLocation() { if (polylin == null) { return null; } FGEPoint returned = GraphicalRepresentation.convertNormalizedPoint(getGraphicalRepresentation(), polylin.getLastPoint(), getEndObject()); returned = getEndObject().getShape().getShape().getNearestPoint(returned); return returned; } @Override public RectPolylinConnector clone() { RectPolylinConnector returned = new RectPolylinConnector(null); returned.setRectPolylinConstraints(getRectPolylinConstraints()); returned.setStraightLineWhenPossible(getStraightLineWhenPossible()); returned.setAdjustability(getAdjustability()); returned.setStartOrientation(getStartOrientation()); returned.setEndOrientation(getEndOrientation()); returned.setIsRounded(getIsRounded()); returned.setArcSize(getArcSize()); returned.setIsStartingLocationFixed(getIsStartingLocationFixed()); if (getIsStartingLocationFixed()) { returned.setFixedStartLocation(getFixedStartLocation()); } returned.setIsEndingLocationFixed(getIsEndingLocationFixed()); if (getIsEndingLocationFixed()) { returned.setFixedEndLocation(getFixedEndLocation()); } returned.setIsStartingLocationDraggable(getIsStartingLocationDraggable()); returned.setIsEndingLocationDraggable(getIsEndingLocationDraggable()); returned.setCrossedControlPoint(getCrossedControlPoint()); returned._setPolylin(_getPolylin()); return returned; } }