/**
* $Id: mxEdgeStyle.java,v 1.36 2010-08-02 13:43:11 david 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<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)
{
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)
{
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)
{
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)
{
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));
}
}
}
}
};
}