com.mxgraph.view.mxEdgeStyle Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jgraphx Show documentation
Show all versions of jgraphx Show documentation
JGraphX Swing Component - Java Graph Visualization Library
This is a binary & source redistribution of the original, unmodified JGraphX library originating from:
"https://github.com/jgraph/jgraphx/archive/v3.4.1.3.zip".
The purpose of this redistribution is to make the library available to other Maven projects.
/**
* $Id: mxEdgeStyle.java,v 1.49 2013/04/05 12:43:17 gaudenz Exp $
* 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 points, List 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 points, List 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 points, List 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 points, List 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 points, List 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 points, List 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
* 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 hints, List result)
{
// Creates array of all way- and terminalpoints
List 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 points, List 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;
}
};
}