/**
* Copyright (c) 2007, Gaudenz Alder
*/
package com.mxgraph.view;
import java.util.List;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUtils;
/**
* Provides various edge styles to be used as the values for
* mxConstants.STYLE_EDGE in a cell style. Alternatevly, the mxConstants.
* EDGESTYLE_* constants can be used to reference an edge style via the
* mxStyleRegistry.
*/
public class mxEdgeStyle
{
/**
* Defines the requirements for an edge style function.
*/
public interface mxEdgeStyleFunction
{
/**
* Implements an edge style function. At the time the function is called, the result
* array contains a placeholder (null) for the first absolute point,
* that is, the point where the edge and source terminal are connected.
* The implementation of the style then adds all intermediate waypoints
* except for the last point, that is, the connection point between the
* edge and the target terminal. The first ant the last point in the
* result array are then replaced with mxPoints that take into account
* the terminal's perimeter and next point on the edge.
*
* @param state Cell state that represents the edge to be updated.
* @param source Cell state that represents the source terminal.
* @param target Cell state that represents the target terminal.
* @param points List of relative control points.
* @param result Array of points that represent the actual points of the
* edge.
*/
void apply(mxCellState state, mxCellState source, mxCellState target,
List<mxPoint> points, List<mxPoint> result);
}
/**
* Provides an entity relation style for edges (as used in database
* schema diagrams).
*/
public static mxEdgeStyleFunction EntityRelation = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
mxGraphView view = state.getView();
mxIGraphModel model = view.getGraph().getModel();
double segment = mxUtils.getDouble(state.getStyle(),
mxConstants.STYLE_SEGMENT, mxConstants.ENTITY_SEGMENT)
* state.view.getScale();
mxPoint p0 = state.getAbsolutePoint(0);
mxPoint pe = state
.getAbsolutePoint(state.getAbsolutePointCount() - 1);
boolean isSourceLeft = false;
if (p0 != null)
{
source = new mxCellState();
source.setX(p0.getX());
source.setY(p0.getY());
}
else if (source != null)
{
int constraint = mxUtils.getPortConstraints(source, state, true, mxConstants.DIRECTION_MASK_NONE);
if (constraint != mxConstants.DIRECTION_MASK_NONE)
{
isSourceLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
}
else
{
mxGeometry sourceGeometry = model.getGeometry(source.cell);
if (sourceGeometry.isRelative())
{
isSourceLeft = sourceGeometry.getX() <= 0.5;
}
else if (target != null)
{
isSourceLeft = target.getX() + target.getWidth() < source
.getX();
}
}
}
boolean isTargetLeft = true;
if (pe != null)
{
target = new mxCellState();
target.setX(pe.getX());
target.setY(pe.getY());
}
else if (target != null)
{
int constraint = mxUtils.getPortConstraints(target, state, false, mxConstants.DIRECTION_MASK_NONE);
if (constraint != mxConstants.DIRECTION_MASK_NONE)
{
isTargetLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
}
else
{
mxGeometry targetGeometry = model.getGeometry(target.cell);
if (targetGeometry.isRelative())
{
isTargetLeft = targetGeometry.getX() <= 0.5;
}
else if (source != null)
{
isTargetLeft = source.getX() + source.getWidth() < target
.getX();
}
}
}
if (source != null && target != null)
{
double x0 = (isSourceLeft) ? source.getX() : source.getX()
+ source.getWidth();
double y0 = view.getRoutingCenterY(source);
double xe = (isTargetLeft) ? target.getX() : target.getX()
+ target.getWidth();
double ye = view.getRoutingCenterY(target);
double seg = segment;
double dx = (isSourceLeft) ? -seg : seg;
mxPoint dep = new mxPoint(x0 + dx, y0);
result.add(dep);
dx = (isTargetLeft) ? -seg : seg;
mxPoint arr = new mxPoint(xe + dx, ye);
// Adds intermediate points if both go out on same side
if (isSourceLeft == isTargetLeft)
{
double x = (isSourceLeft) ? Math.min(x0, xe) - segment
: Math.max(x0, xe) + segment;
result.add(new mxPoint(x, y0));
result.add(new mxPoint(x, ye));
}
else if ((dep.getX() < arr.getX()) == isSourceLeft)
{
double midY = y0 + (ye - y0) / 2;
result.add(new mxPoint(dep.getX(), midY));
result.add(new mxPoint(arr.getX(), midY));
}
result.add(arr);
}
}
};
/**
* Provides a self-reference, aka. loop.
*/
public static mxEdgeStyleFunction Loop = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
if (source != null)
{
mxGraphView view = state.getView();
mxGraph graph = view.getGraph();
mxPoint pt = (points != null && points.size() > 0) ? points
.get(0) : null;
if (pt != null)
{
pt = view.transformControlPoint(state, pt);
if (source.contains(pt.getX(), pt.getY()))
{
pt = null;
}
}
double x = 0;
double dx = 0;
double y = 0;
double dy = 0;
double seg = mxUtils.getDouble(state.getStyle(),
mxConstants.STYLE_SEGMENT, graph.getGridSize())
* view.getScale();
String dir = mxUtils
.getString(state.getStyle(),
mxConstants.STYLE_DIRECTION,
mxConstants.DIRECTION_WEST);
if (dir.equals(mxConstants.DIRECTION_NORTH)
|| dir.equals(mxConstants.DIRECTION_SOUTH))
{
x = view.getRoutingCenterX(source);
dx = seg;
}
else
{
y = view.getRoutingCenterY(source);
dy = seg;
}
if (pt == null || pt.getX() < source.getX()
|| pt.getX() > source.getX() + source.getWidth())
{
if (pt != null)
{
x = pt.getX();
dy = Math.max(Math.abs(y - pt.getY()), dy);
}
else
{
if (dir.equals(mxConstants.DIRECTION_NORTH))
{
y = source.getY() - 2 * dx;
}
else if (dir.equals(mxConstants.DIRECTION_SOUTH))
{
y = source.getY() + source.getHeight() + 2 * dx;
}
else if (dir.equals(mxConstants.DIRECTION_EAST))
{
x = source.getX() - 2 * dy;
}
else
{
x = source.getX() + source.getWidth() + 2 * dy;
}
}
}
else
{
// pt != null
x = view.getRoutingCenterX(source);
dx = Math.max(Math.abs(x - pt.getX()), dy);
y = pt.getY();
dy = 0;
}
result.add(new mxPoint(x - dx, y - dy));
result.add(new mxPoint(x + dx, y + dy));
}
}
};
/**
* Uses either SideToSide or TopToBottom depending on the horizontal
* flag in the cell style. SideToSide is used if horizontal is true or
* unspecified.
*/
public static mxEdgeStyleFunction ElbowConnector = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
mxPoint pt = (points != null && points.size() > 0) ? points.get(0)
: null;
boolean vertical = false;
boolean horizontal = false;
if (source != null && target != null)
{
if (pt != null)
{
double left = Math.min(source.getX(), target.getX());
double right = Math.max(source.getX() + source.getWidth(),
target.getX() + target.getWidth());
double top = Math.min(source.getY(), target.getY());
double bottom = Math.max(
source.getY() + source.getHeight(), target.getY()
+ target.getHeight());
pt = state.getView().transformControlPoint(state, pt);
vertical = pt.getY() < top || pt.getY() > bottom;
horizontal = pt.getX() < left || pt.getX() > right;
}
else
{
double left = Math.max(source.getX(), target.getX());
double right = Math.min(source.getX() + source.getWidth(),
target.getX() + target.getWidth());
vertical = left == right;
if (!vertical)
{
double top = Math.max(source.getY(), target.getY());
double bottom = Math.min(
source.getY() + source.getHeight(),
target.getY() + target.getHeight());
horizontal = top == bottom;
}
}
}
if (!horizontal
&& (vertical || mxUtils.getString(state.getStyle(),
mxConstants.STYLE_ELBOW, "").equals(
mxConstants.ELBOW_VERTICAL)))
{
mxEdgeStyle.TopToBottom.apply(state, source, target, points,
result);
}
else
{
mxEdgeStyle.SideToSide.apply(state, source, target, points,
result);
}
}
};
/**
* Provides a vertical elbow edge.
*/
public static mxEdgeStyleFunction SideToSide = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
mxGraphView view = state.getView();
mxPoint pt = ((points != null && points.size() > 0) ? points.get(0)
: null);
mxPoint p0 = state.getAbsolutePoint(0);
mxPoint pe = state
.getAbsolutePoint(state.getAbsolutePointCount() - 1);
if (pt != null)
{
pt = view.transformControlPoint(state, pt);
}
if (p0 != null)
{
source = new mxCellState();
source.setX(p0.getX());
source.setY(p0.getY());
}
if (pe != null)
{
target = new mxCellState();
target.setX(pe.getX());
target.setY(pe.getY());
}
if (source != null && target != null)
{
double l = Math.max(source.getX(), target.getX());
double r = Math.min(source.getX() + source.getWidth(),
target.getX() + target.getWidth());
double x = (pt != null) ? pt.getX() : r + (l - r) / 2;
double y1 = view.getRoutingCenterY(source);
double y2 = view.getRoutingCenterY(target);
if (pt != null)
{
if (pt.getY() >= source.getY()
&& pt.getY() <= source.getY() + source.getHeight())
{
y1 = pt.getY();
}
if (pt.getY() >= target.getY()
&& pt.getY() <= target.getY() + target.getHeight())
{
y2 = pt.getY();
}
}
if (!target.contains(x, y1) && !source.contains(x, y1))
{
result.add(new mxPoint(x, y1));
}
if (!target.contains(x, y2) && !source.contains(x, y2))
{
result.add(new mxPoint(x, y2));
}
if (result.size() == 1)
{
if (pt != null)
{
if (!target.contains(x, pt.getY()) && !source.contains(x, pt.getY()))
{
result.add(new mxPoint(x, pt.getY()));
}
}
else
{
double t = Math.max(source.getY(), target.getY());
double b = Math.min(source.getY() + source.getHeight(),
target.getY() + target.getHeight());
result.add(new mxPoint(x, t + (b - t) / 2));
}
}
}
}
};
/**
* Provides a horizontal elbow edge.
*/
public static mxEdgeStyleFunction TopToBottom = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
mxGraphView view = state.getView();
mxPoint pt = ((points != null && points.size() > 0) ? points.get(0)
: null);
mxPoint p0 = state.getAbsolutePoint(0);
mxPoint pe = state
.getAbsolutePoint(state.getAbsolutePointCount() - 1);
if (pt != null)
{
pt = view.transformControlPoint(state, pt);
}
if (p0 != null)
{
source = new mxCellState();
source.setX(p0.getX());
source.setY(p0.getY());
}
if (pe != null)
{
target = new mxCellState();
target.setX(pe.getX());
target.setY(pe.getY());
}
if (source != null && target != null)
{
double t = Math.max(source.getY(), target.getY());
double b = Math.min(source.getY() + source.getHeight(),
target.getY() + target.getHeight());
double x = view.getRoutingCenterX(source);
if (pt != null && pt.getX() >= source.getX()
&& pt.getX() <= source.getX() + source.getWidth())
{
x = pt.getX();
}
double y = (pt != null) ? pt.getY() : b + (t - b) / 2;
if (!target.contains(x, y) && !source.contains(x, y))
{
result.add(new mxPoint(x, y));
}
if (pt != null && pt.getX() >= target.getX()
&& pt.getX() <= target.getX() + target.getWidth())
{
x = pt.getX();
}
else
{
x = view.getRoutingCenterX(target);
}
if (!target.contains(x, y) && !source.contains(x, y))
{
result.add(new mxPoint(x, y));
}
if (result.size() == 1)
{
if (pt != null)
{
if (!target.contains(pt.getX(), y) && !source.contains(pt.getX(), y))
{
result.add(new mxPoint(pt.getX(), y));
}
}
else
{
double l = Math.max(source.getX(), target.getX());
double r = Math.min(source.getX() + source.getWidth(),
target.getX() + target.getWidth());
result.add(new mxPoint(l + (r - l) / 2, y));
}
}
}
}
};
/**
* Implements an orthogonal edge style. Use <mxEdgeSegmentHandler>
* as an interactive handler for this style.
*/
public static mxEdgeStyleFunction SegmentConnector = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source, mxCellState target, List<mxPoint> hints, List<mxPoint> result)
{
// Creates array of all way- and terminalpoints
List<mxPoint> pts = state.absolutePoints;
boolean horizontal = true;
mxPoint hint = null;
// Adds the first point
mxPoint pt = pts.get(0);
if (pt == null && source != null)
{
pt = new mxPoint(state.view.getRoutingCenterX(source), state.view.getRoutingCenterY(source));
}
else if (pt != null)
{
pt = (mxPoint) pt.clone();
}
int lastInx = pts.size() - 1;
// Adds the waypoints
if (hints != null && hints.size() > 0)
{
hint = state.view.transformControlPoint(state, hints.get(0));
mxCellState currentTerm = source;
mxPoint currentPt = pts.get(0);
boolean hozChan = false;
boolean vertChan = false;
mxPoint currentHint = hint;
int hintsLen = hints.size();
for (int i = 0; i < 2; i++)
{
boolean fixedVertAlign = currentPt != null && currentPt.getX() == currentHint.getX();
boolean fixedHozAlign = currentPt != null && currentPt.getY() == currentHint.getY();
boolean inHozChan = currentTerm != null
&& (currentHint.getY() >= currentTerm.getY() && currentHint.getY() <= currentTerm.getY()
+ currentTerm.getHeight());
boolean inVertChan = currentTerm != null
&& (currentHint.getX() >= currentTerm.getX() && currentHint.getX() <= currentTerm.getX()
+ currentTerm.getWidth());
hozChan = fixedHozAlign || (currentPt == null && inHozChan);
vertChan = fixedVertAlign || (currentPt == null && inVertChan);
if (currentPt != null && (!fixedHozAlign && !fixedVertAlign) && (inHozChan || inVertChan))
{
horizontal = inHozChan ? false : true;
break;
}
if (vertChan || hozChan)
{
horizontal = hozChan;
if (i == 1)
{
// Work back from target end
horizontal = hints.size() % 2 == 0 ? hozChan : vertChan;
}
break;
}
currentTerm = target;
currentPt = pts.get(lastInx);
currentHint = state.view.transformControlPoint(state, hints.get(hintsLen - 1));
}
if (horizontal
&& ((pts.get(0) != null && pts.get(0).getY() != hint.getY()) || (pts.get(0) == null && source != null && (hint
.getY() < source.getY() || hint.getY() > source.getY() + source.getHeight()))))
{
result.add(new mxPoint(pt.getX(), hint.getY()));
}
else if (!horizontal
&& ((pts.get(0) != null && pts.get(0).getX() != hint.getX()) || (pts.get(0) == null && source != null && (hint
.getX() < source.getX() || hint.getX() > source.getX() + source.getWidth()))))
{
result.add(new mxPoint(hint.getX(), pt.getY()));
}
if (horizontal)
{
pt.setY(hint.getY());
}
else
{
pt.setX(hint.getX());
}
for (int i = 0; i < hints.size(); i++)
{
horizontal = !horizontal;
hint = state.view.transformControlPoint(state, hints.get(i));
// mxLog.show();
// mxLog.debug('hint', i, hint.x, hint.y);
if (horizontal)
{
pt.setY(hint.getY());
}
else
{
pt.setX(hint.getX());
}
result.add((mxPoint) pt.clone());
}
}
else
{
hint = pt;
// FIXME: First click in connect preview toggles orientation
horizontal = true;
}
// Adds the last point
pt = pts.get(lastInx);
if (pt == null && target != null)
{
pt = new mxPoint(state.view.getRoutingCenterX(target), state.view.getRoutingCenterY(target));
}
if (horizontal
&& ((pts.get(lastInx) != null && pts.get(lastInx).getY() != hint.getY()) || (pts.get(lastInx) == null && target != null && (hint
.getY() < target.getY() || hint.getY() > target.getY() + target.getHeight()))))
{
result.add(new mxPoint(pt.getX(), hint.getY()));
}
else if (!horizontal
&& ((pts.get(lastInx) != null && pts.get(lastInx).getX() != hint.getX()) || (pts.get(lastInx) == null && target != null && (hint
.getX() < target.getX() || hint.getX() > target.getX() + target.getWidth()))))
{
result.add(new mxPoint(hint.getX(), pt.getY()));
}
// Removes bends inside the source terminal for floating ports
if (pts.get(0) == null && source != null)
{
while (result.size() > 1 && source.contains(result.get(1).getX(), result.get(1).getY()))
{
result.remove(1);
}
}
// Removes bends inside the target terminal
if (pts.get(lastInx) == null && target != null)
{
while (result.size() > 1 && target.contains(result.get(result.size() - 1).getX(), result.get(result.size() - 1).getY()))
{
result.remove(result.size() - 1);
}
}
}
};
public static double orthBuffer = 10;
public static double[][] dirVectors = new double[][] { { -1, 0 },
{ 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 0 } };
public static double[][] wayPoints1 = new double[128][2];
/**
* The default routing patterns for orthogonal connections
*/
public static int[][][] routePatterns = new int[][][] {
{ { 513, 2308, 2081, 2562 }, { 513, 1090, 514, 2184, 2114, 2561 },
{ 513, 1090, 514, 2564, 2184, 2562 },
{ 513, 2308, 2561, 1090, 514, 2568, 2308 } },
{ { 514, 1057, 513, 2308, 2081, 2562 }, { 514, 2184, 2114, 2561 },
{ 514, 2184, 2562, 1057, 513, 2564, 2184 },
{ 514, 1057, 513, 2568, 2308, 2561 } },
{ { 1090, 514, 1057, 513, 2308, 2081, 2562 }, { 2114, 2561 },
{ 1090, 2562, 1057, 513, 2564, 2184 },
{ 1090, 514, 1057, 513, 2308, 2561, 2568 } },
{ { 2081, 2562 }, { 1057, 513, 1090, 514, 2184, 2114, 2561 },
{ 1057, 513, 1090, 514, 2184, 2562, 2564 },
{ 1057, 2561, 1090, 514, 2568, 2308 } } };
/**
* Overriden routing patterns for orthogonal connections
* where the vertices have
*/
public static int[][][] inlineRoutePatterns = new int[][][] {
{ null, { 2114, 2568 }, null, null },
{ null, { 514, 2081, 2114, 2568 }, null, null },
{ null, { 2114, 2561 }, null, null },
{ { 2081, 2562 }, { 1057, 2114, 2568 }, { 2184, 2562 }, null } };
public static double[] vertexSeperations = new double[5];
public static double[][] limits = new double[2][9];
public static int LEFT_MASK = 32;
public static int TOP_MASK = 64;
public static int RIGHT_MASK = 128;
public static int BOTTOM_MASK = 256;
public static int LEFT = 1;
public static int TOP = 2;
public static int RIGHT = 4;
public static int BOTTOM = 8;
public static int SIDE_MASK = LEFT_MASK | TOP_MASK | RIGHT_MASK
| BOTTOM_MASK;
public static int CENTER_MASK = 512;
public static int SOURCE_MASK = 1024;
public static int TARGET_MASK = 2048;
public static int VERTEX_MASK = SOURCE_MASK | TARGET_MASK;
public static double vertBendProportion = 0.5;
public static double hozBendProportion = 0.5;
/**
* An orthogonal connector that avoids connecting vertices and
* respects port constraints
*/
public static mxEdgeStyleFunction OrthConnector = new mxEdgeStyleFunction()
{
/* (non-Javadoc)
* @see com.mxgraph.view.mxEdgeStyle.mxEdgeStyleFunction#apply(com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, com.mxgraph.view.mxCellState, java.util.List, java.util.List)
*/
public void apply(mxCellState state, mxCellState source,
mxCellState target, List<mxPoint> points, List<mxPoint> result)
{
mxGraph graph = state.view.graph;
boolean sourceEdge = source == null ? false : graph.getModel().isEdge(source.cell);
boolean targetEdge = target == null ? false : graph.getModel().isEdge(target.cell);
if ((points != null && points.size() > 0) || (sourceEdge) || (targetEdge))
{
mxEdgeStyle.SegmentConnector.apply(state, source, target, points, result);
return;
}
if (source != null && target != null)
{
double scaledOrthBuffer = orthBuffer
* state.getView().getScale();
// Determine the side(s) of the source and target vertices
// that the edge may connect to
// portConstraint -> [source, target];
int portConstraint[] = new int[2];
portConstraint[0] = mxUtils.getPortConstraints(source, state,
true);
portConstraint[1] = mxUtils.getPortConstraints(target, state,
false);
// dir -> [source, target] initial direction leaving vertices
int dir[] = new int[2];
// Work out which faces of the vertices present against each other
// in a way that would allow a 3-segment connection if port constraints
// permitted.
// geo -> [source, target] [x, y, width, height]
double[][] geo = new double[2][4];
geo[0][0] = source.getX();
geo[0][1] = source.getY();
geo[0][2] = source.getWidth();
geo[0][3] = source.getHeight();
geo[1][0] = target.getX();
geo[1][1] = target.getY();
geo[1][2] = target.getWidth();
geo[1][3] = target.getHeight();
for (int i = 0; i < 2; i++)
{
limits[i][1] = geo[i][0] - scaledOrthBuffer;
limits[i][2] = geo[i][1] - scaledOrthBuffer;
limits[i][4] = geo[i][0] + geo[i][2] + scaledOrthBuffer;
limits[i][8] = geo[i][1] + geo[i][3] + scaledOrthBuffer;
}
// Work out which quad the target is in
double sourceCenX = geo[0][0] + geo[0][2] / 2.0;
double sourceCenY = geo[0][1] + geo[0][3] / 2.0;
double targetCenX = geo[1][0] + geo[1][2] / 2.0;
double targetCenY = geo[1][1] + geo[1][3] / 2.0;
double dx = sourceCenX - targetCenX;
double dy = sourceCenY - targetCenY;
int quad = 0;
if (dx < 0)
{
if (dy < 0)
{
quad = 2;
}
else
{
quad = 1;
}
}
else
{
if (dy <= 0)
{
quad = 3;
// Special case on x = 0 and negative y
if (dx == 0)
{
quad = 2;
}
}
}
// Check for connection constraints
mxPoint p0 = state.getAbsolutePoint(0);
mxPoint pe = state.getAbsolutePoint(state
.getAbsolutePointCount() - 1);
mxPoint currentTerm = p0;
// constraint[source, target] [x, y]
double constraint[][] = new double[][] { { 0.5, 0.5 },
{ 0.5, 0.5 } };
for (int i = 0; i < 2; i++)
{
if (currentTerm != null)
{
constraint[i][0] = (currentTerm.getX() - geo[i][0])
/ geo[i][2];
if (constraint[i][0] < 0.01)
{
dir[i] = mxConstants.DIRECTION_MASK_WEST;
}
else if (constraint[i][0] > 0.99)
{
dir[i] = mxConstants.DIRECTION_MASK_EAST;
}
constraint[i][1] = (currentTerm.getY() - geo[i][1])
/ geo[i][3];
if (constraint[i][1] < 0.01)
{
dir[i] = mxConstants.DIRECTION_MASK_NORTH;
}
else if (constraint[i][1] > 0.99)
{
dir[i] = mxConstants.DIRECTION_MASK_SOUTH;
}
}
currentTerm = pe;
}
double sourceTopDist = geo[0][1] - (geo[1][1] + geo[1][3]);
double sourceLeftDist = geo[0][0] - (geo[1][0] + geo[1][2]);
double sourceBottomDist = geo[1][1] - (geo[0][1] + geo[0][3]);
double sourceRightDist = geo[1][0] - (geo[0][0] + geo[0][2]);
vertexSeperations[1] = Math.max(sourceLeftDist - 2
* scaledOrthBuffer, 0);
vertexSeperations[2] = Math.max(sourceTopDist - 2
* scaledOrthBuffer, 0);
vertexSeperations[4] = Math.max(sourceBottomDist - 2
* scaledOrthBuffer, 0);
vertexSeperations[3] = Math.max(sourceRightDist - 2
* scaledOrthBuffer, 0);
//==============================================================
// Start of source and target direction determination
// Work through the preferred orientations by relative positioning
// of the vertices and list them in preferred and available order
int dirPref[] = new int[2];
int horPref[] = new int[2];
int vertPref[] = new int[2];
horPref[0] = sourceLeftDist >= sourceRightDist ? mxConstants.DIRECTION_MASK_WEST
: mxConstants.DIRECTION_MASK_EAST;
vertPref[0] = sourceTopDist >= sourceBottomDist ? mxConstants.DIRECTION_MASK_NORTH
: mxConstants.DIRECTION_MASK_SOUTH;
horPref[1] = mxUtils.reversePortConstraints(horPref[0]);
vertPref[1] = mxUtils.reversePortConstraints(vertPref[0]);
double preferredHorizDist = sourceLeftDist >= sourceRightDist ? sourceLeftDist
: sourceRightDist;
double preferredVertDist = sourceTopDist >= sourceBottomDist ? sourceTopDist
: sourceBottomDist;
int prefOrdering[][] = new int[2][2];
boolean preferredOrderSet = false;
// If the preferred port isn't available, switch it
for (int i = 0; i < 2; i++)
{
if (dir[i] != 0x0)
{
continue;
}
if ((horPref[i] & portConstraint[i]) == 0)
{
horPref[i] = mxUtils.reversePortConstraints(horPref[i]);
}
if ((vertPref[i] & portConstraint[i]) == 0)
{
vertPref[i] = mxUtils
.reversePortConstraints(vertPref[i]);
}
prefOrdering[i][0] = vertPref[i];
prefOrdering[i][1] = horPref[i];
}
if (preferredVertDist > scaledOrthBuffer * 2
&& preferredHorizDist > scaledOrthBuffer * 2)
{
// Possibility of two segment edge connection
if (((horPref[0] & portConstraint[0]) > 0)
&& ((vertPref[1] & portConstraint[1]) > 0))
{
prefOrdering[0][0] = horPref[0];
prefOrdering[0][1] = vertPref[0];
prefOrdering[1][0] = vertPref[1];
prefOrdering[1][1] = horPref[1];
preferredOrderSet = true;
}
else if (((vertPref[0] & portConstraint[0]) > 0)
&& ((horPref[1] & portConstraint[1]) > 0))
{
prefOrdering[0][0] = vertPref[0];
prefOrdering[0][1] = horPref[0];
prefOrdering[1][0] = horPref[1];
prefOrdering[1][1] = vertPref[1];
preferredOrderSet = true;
}
}
if (preferredVertDist > scaledOrthBuffer * 2
&& !preferredOrderSet)
{
prefOrdering[0][0] = vertPref[0];
prefOrdering[0][1] = horPref[0];
prefOrdering[1][0] = vertPref[1];
prefOrdering[1][1] = horPref[1];
preferredOrderSet = true;
}
if (preferredHorizDist > scaledOrthBuffer * 2
&& !preferredOrderSet)
{
prefOrdering[0][0] = horPref[0];
prefOrdering[0][1] = vertPref[0];
prefOrdering[1][0] = horPref[1];
prefOrdering[1][1] = vertPref[1];
preferredOrderSet = true;
}
// The source and target prefs are now an ordered list of
// the preferred port selections
// It the list can contain gaps, compact it
for (int i = 0; i < 2; i++)
{
if (dir[i] != 0x0)
{
continue;
}
if ((prefOrdering[i][0] & portConstraint[i]) == 0)
{
prefOrdering[i][0] = prefOrdering[i][1];
}
dirPref[i] = prefOrdering[i][0] & portConstraint[i];
dirPref[i] |= (prefOrdering[i][1] & portConstraint[i]) << 8;
dirPref[i] |= (prefOrdering[1 - i][i] & portConstraint[i]) << 16;
dirPref[i] |= (prefOrdering[1 - i][1 - i] & portConstraint[i]) << 24;
if ((dirPref[i] & 0xF) == 0)
{
dirPref[i] = dirPref[i] << 8;
}
if ((dirPref[i] & 0xF00) == 0)
{
dirPref[i] = (dirPref[i] & 0xF) | dirPref[i] >> 8;
}
if ((dirPref[i] & 0xF0000) == 0)
{
dirPref[i] = (dirPref[i] & 0xFFFF)
| ((dirPref[i] & 0xF000000) >> 8);
}
dir[i] = dirPref[i] & 0xF;
if (portConstraint[i] == mxConstants.DIRECTION_MASK_WEST
|| portConstraint[i] == mxConstants.DIRECTION_MASK_NORTH
|| portConstraint[i] == mxConstants.DIRECTION_MASK_EAST
|| portConstraint[i] == mxConstants.DIRECTION_MASK_SOUTH)
{
dir[i] = portConstraint[i];
}
}
//==============================================================
// End of source and target direction determination
int[] routePattern = getRoutePattern(dir, quad, dx, dy);
if (dx == 0 || dy == 0)
{
}
wayPoints1[0][0] = geo[0][0];
wayPoints1[0][1] = geo[0][1];
switch (dir[0])
{
case mxConstants.DIRECTION_MASK_WEST:
wayPoints1[0][0] -= scaledOrthBuffer;
wayPoints1[0][1] += constraint[0][1] * geo[0][3];
break;
case mxConstants.DIRECTION_MASK_SOUTH:
wayPoints1[0][0] += constraint[0][0] * geo[0][2];
wayPoints1[0][1] += geo[0][3] + scaledOrthBuffer;
break;
case mxConstants.DIRECTION_MASK_EAST:
wayPoints1[0][0] += geo[0][2] + scaledOrthBuffer;
wayPoints1[0][1] += constraint[0][1] * geo[0][3];
break;
case mxConstants.DIRECTION_MASK_NORTH:
wayPoints1[0][0] += constraint[0][0] * geo[0][2];
wayPoints1[0][1] -= scaledOrthBuffer;
break;
}
int currentIndex = 0;
int lastOrientation = (dir[0] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
: 1;
int currentOrientation = 0;
for (int i = 0; i < routePattern.length; i++)
{
int nextDirection = routePattern[i] & 0xF;
// Rotate the index of this direction by the quad
// to get the real direction
int directionIndex = nextDirection == mxConstants.DIRECTION_MASK_EAST ? 3
: nextDirection;
directionIndex += quad;
if (directionIndex > 4)
{
directionIndex -= 4;
}
double[] direction = dirVectors[directionIndex - 1];
currentOrientation = (directionIndex % 2 > 0) ? 0 : 1;
// Only update the current index if the point moved
// in the direction of the current segment move,
// otherwise the same point is moved until there is
// a segment direction change
if (currentOrientation != lastOrientation)
{
currentIndex++;
// Copy the previous way point into the new one
// We can't base the new position on index - 1
// because sometime elbows turn out not to exist,
// then we'd have to rewind.
wayPoints1[currentIndex][0] = wayPoints1[currentIndex - 1][0];
wayPoints1[currentIndex][1] = wayPoints1[currentIndex - 1][1];
}
boolean tar = (routePattern[i] & TARGET_MASK) > 0;
boolean sou = (routePattern[i] & SOURCE_MASK) > 0;
int side = (routePattern[i] & SIDE_MASK) >> 5;
side = side << quad;
if (side > 0xF)
{
side = side >> 4;
}
boolean center = (routePattern[i] & CENTER_MASK) > 0;
if ((sou || tar) && side < 9)
{
double limit = 0;
int souTar = sou ? 0 : 1;
if (center && currentOrientation == 0)
{
limit = geo[souTar][0] + constraint[souTar][0]
* geo[souTar][2];
}
else if (center)
{
limit = geo[souTar][1] + constraint[souTar][1]
* geo[souTar][3];
}
else
{
limit = limits[souTar][side];
}
if (currentOrientation == 0)
{
double lastX = wayPoints1[currentIndex][0];
double deltaX = (limit - lastX) * direction[0];
if (deltaX > 0)
{
wayPoints1[currentIndex][0] += direction[0]
* deltaX;
}
}
else
{
double lastY = wayPoints1[currentIndex][1];
double deltaY = (limit - lastY) * direction[1];
if (deltaY > 0)
{
wayPoints1[currentIndex][1] += direction[1]
* deltaY;
}
}
}
else if (center)
{
// Which center we're travelling to depend on the current direction
wayPoints1[currentIndex][0] += direction[0]
* Math.abs(vertexSeperations[directionIndex] / 2);
wayPoints1[currentIndex][1] += direction[1]
* Math.abs(vertexSeperations[directionIndex] / 2);
}
if (currentIndex > 0
&& wayPoints1[currentIndex][currentOrientation] == wayPoints1[currentIndex - 1][currentOrientation])
{
currentIndex--;
}
else
{
lastOrientation = currentOrientation;
}
}
for (int i = 0; i <= currentIndex; i++)
{
result.add(new mxPoint(wayPoints1[i][0], wayPoints1[i][1]));
}
}
}
/**
* Hook method to return the routing pattern for the given state
* @param dir
* @param quad
* @param dx
* @param dy
* @return
*/
protected int[] getRoutePattern(int[] dir, double quad, double dx,
double dy)
{
int sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
: dir[0];
int targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
: dir[1];
sourceIndex -= quad;
targetIndex -= quad;
if (sourceIndex < 1)
{
sourceIndex += 4;
}
if (targetIndex < 1)
{
targetIndex += 4;
}
int[] result = routePatterns[sourceIndex - 1][targetIndex - 1];
if (dx == 0 || dy == 0)
{
if (inlineRoutePatterns[sourceIndex - 1][targetIndex - 1] != null)
{
result = inlineRoutePatterns[sourceIndex - 1][targetIndex - 1];
}
}
return result;
}
};
}