/*******************************************************************************
* Copyright (c) 2015, 2016 itemis AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API & implementation
* Alexander Nyßen (itemis AG) - initial API & implementation
* Tamas Miklossy (itemis AG) - Add support for arrowType edge decorations (bug #477980)
* - Add support for polygon-based node shapes (bug #441352)
* - Add support for all dot attributes (bug #461506)
*
*******************************************************************************/
package org.eclipse.gef.dot.internal.ui;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.gef.common.attributes.IAttributeCopier;
import org.eclipse.gef.common.attributes.IAttributeStore;
import org.eclipse.gef.dot.internal.DotAttributes;
import org.eclipse.gef.dot.internal.language.arrowtype.ArrowType;
import org.eclipse.gef.dot.internal.language.color.Color;
import org.eclipse.gef.dot.internal.language.color.DotColors;
import org.eclipse.gef.dot.internal.language.color.HSVColor;
import org.eclipse.gef.dot.internal.language.color.RGBColor;
import org.eclipse.gef.dot.internal.language.color.StringColor;
import org.eclipse.gef.dot.internal.language.dir.DirType;
import org.eclipse.gef.dot.internal.language.dot.EdgeOp;
import org.eclipse.gef.dot.internal.language.dot.GraphType;
import org.eclipse.gef.dot.internal.language.layout.Layout;
import org.eclipse.gef.dot.internal.language.rankdir.Rankdir;
import org.eclipse.gef.dot.internal.language.shape.PolygonBasedNodeShape;
import org.eclipse.gef.dot.internal.language.shape.PolygonBasedShape;
import org.eclipse.gef.dot.internal.language.splines.Splines;
import org.eclipse.gef.dot.internal.language.splinetype.Spline;
import org.eclipse.gef.dot.internal.language.splinetype.SplineType;
import org.eclipse.gef.dot.internal.language.style.EdgeStyle;
import org.eclipse.gef.dot.internal.language.style.NodeStyle;
import org.eclipse.gef.dot.internal.language.style.Style;
import org.eclipse.gef.dot.internal.language.style.StyleItem;
import org.eclipse.gef.fx.nodes.GeometryNode;
import org.eclipse.gef.fx.nodes.OrthogonalRouter;
import org.eclipse.gef.fx.nodes.PolylineInterpolator;
import org.eclipse.gef.fx.nodes.StraightRouter;
import org.eclipse.gef.geometry.planar.Dimension;
import org.eclipse.gef.geometry.planar.Ellipse;
import org.eclipse.gef.geometry.planar.ICurve;
import org.eclipse.gef.geometry.planar.Point;
import org.eclipse.gef.geometry.planar.Polygon;
import org.eclipse.gef.geometry.planar.Rectangle;
import org.eclipse.gef.graph.Edge;
import org.eclipse.gef.graph.Graph;
import org.eclipse.gef.graph.Node;
import org.eclipse.gef.layout.ILayoutAlgorithm;
import org.eclipse.gef.layout.algorithms.GridLayoutAlgorithm;
import org.eclipse.gef.layout.algorithms.RadialLayoutAlgorithm;
import org.eclipse.gef.layout.algorithms.SpringLayoutAlgorithm;
import org.eclipse.gef.layout.algorithms.TreeLayoutAlgorithm;
import org.eclipse.gef.zest.fx.ZestProperties;
import javafx.geometry.Bounds;
import javafx.scene.text.Text;
/**
* A converter that transforms a {@link Graph} that is attributed with
* {@link DotAttributes} into a {@link Graph} that is attributed with
* {@link ZestProperties}.
*
* @author anyssen
*
*/
public class Dot2ZestAttributesConverter implements IAttributeCopier {
public final static class Options {
/**
* Indicates whether layout should be emulated or not. If set to
* <code>true</code>, an {@link ILayoutAlgorithm} is to be inferred for
* the given dot, and set as value of the
* {@link ZestProperties#LAYOUT_ALGORITHM__G} attribute. If set to
* <code>false</code> (i.e. native layout is performed via Graphviz and
* position and size information is already provided in the dot input),
* the {@link ZestProperties#LAYOUT_ALGORITHM__G} should remain unset.
*/
public boolean emulateLayout = true;
/**
* Whether to ignore position data.
*/
public boolean ignorePositions = false;
/**
* Specifies whether the y-coordinate values of all position information
* is to be inverted. If set to <code>true</code> the y-values of all
* position information is to be inverted. If set to <code>false</code>,
* it is to be transformed without inversion.
*/
public boolean invertYAxis = false;
}
@Override
public void copy(IAttributeStore source, IAttributeStore target) {
if (source instanceof Node && target instanceof Node) {
convertAttributes((Node) source, (Node) target);
} else if (source instanceof Edge && target instanceof Edge) {
convertAttributes((Edge) source, (Edge) target);
} else if (source instanceof Graph && target instanceof Graph) {
convertAttributes((Graph) source, (Graph) target);
} else {
throw new IllegalArgumentException();
}
}
protected void convertAttributes(Edge dot, Edge zest) {
// convert id and label
String dotId = DotAttributes.getId(dot);
if (dotId != null) {
ZestProperties.setCssId(zest, dotId);
}
String dotLabel = DotAttributes.getLabel(dot);
if (dotLabel != null && dotLabel.equals("\\E")) { //$NON-NLS-1$
// The edge default label '\E' is used to indicate that an edge's
// name or id becomes its label.
boolean directed = GraphType.DIGRAPH.equals(
DotAttributes._getType(dot.getGraph().getRootGraph()));
String dotName = DotAttributes._getName(dot.getSource())
+ (directed ? EdgeOp.DIRECTED.toString()
: EdgeOp.UNDIRECTED.toString())
+ DotAttributes._getName(dot.getTarget());
dotLabel = dotId != null ? dotId : dotName;
}
if (dotLabel != null) {
ZestProperties.setLabel(zest, dotLabel);
}
// external label (xlabel)
String dotXLabel = DotAttributes.getXlabel(dot);
if (dotXLabel != null) {
ZestProperties.setExternalLabel(zest, dotXLabel);
}
// head and tail labels (headlabel, taillabel)
String dotHeadLabel = DotAttributes.getHeadlabel(dot);
if (dotHeadLabel != null) {
ZestProperties.setTargetLabel(zest, dotHeadLabel);
}
String dotTailLabel = DotAttributes.getTaillabel(dot);
if (dotTailLabel != null) {
ZestProperties.setSourceLabel(zest, dotTailLabel);
}
// convert edge style
String dotStyle = DotAttributes.getStyle(dot);
String connectionCssStyle = null;
if (EdgeStyle.DASHED.toString().equals(dotStyle)) {
connectionCssStyle = "-fx-stroke-dash-array: 7 7;"; //$NON-NLS-1$
} else if (EdgeStyle.DOTTED.toString().equals(dotStyle)) {
connectionCssStyle = "-fx-stroke-dash-array: 1 7;"; //$NON-NLS-1$
} else if (EdgeStyle.BOLD.toString().equals(dotStyle)) {
connectionCssStyle = "-fx-stroke-width: 2;"; //$NON-NLS-1$
} else if (EdgeStyle.INVIS.toString().equals(dotStyle)) {
// mark as invisible
ZestProperties.setInvisible(zest, true);
}
// TODO: handle tapered edges
if (connectionCssStyle == null) {
connectionCssStyle = "-fx-stroke-line-cap: butt;"; //$NON-NLS-1$
}
// direction
DirType dotDir = DotAttributes.getDirParsed(dot);
if (dotDir == null) {
// use the default direction if no direction is specified for
// the edge
dotDir = GraphType.DIGRAPH.equals(
DotAttributes._getType(dot.getGraph().getRootGraph()))
? DirType.FORWARD : DirType.NONE;
}
// color
Color dotColor = DotAttributes.getColorParsed(dot);
String javaFxColor = computeZestColor(dotColor);
if (javaFxColor != null) {
String zestStroke = "-fx-stroke: " + javaFxColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
connectionCssStyle += zestStroke;
if (DirType.BACK.equals(dotDir) || DirType.BOTH.equals(dotDir)) {
String zestFill = "-fx-fill: " + javaFxColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
String zestSourceDecorationCssStyle = zestStroke + zestFill;
ZestProperties.setSourceDecorationCssStyle(zest,
zestSourceDecorationCssStyle);
}
if (DirType.FORWARD.equals(dotDir) || DirType.BOTH.equals(dotDir)) {
String zestFill = "-fx-fill: " + javaFxColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
String zestTargetDecorationCssStyle = zestStroke + zestFill;
ZestProperties.setTargetDecorationCssStyle(zest,
zestTargetDecorationCssStyle);
}
}
ZestProperties.setCurveCssStyle(zest, connectionCssStyle);
// fillcolor
Color dotFillColor = DotAttributes.getFillcolorParsed(dot);
String javaFxFillColor = computeZestColor(dotFillColor);
if (javaFxFillColor != null) {
String zestSourceDecorationCssStyle = ZestProperties
.getSourceDecorationCssStyle(zest);
ZestProperties.setSourceDecorationCssStyle(zest,
zestSourceDecorationCssStyle + "-fx-fill: " //$NON-NLS-1$
+ javaFxFillColor + ";"); //$NON-NLS-1$
String zestTargetDecorationCssStyle = ZestProperties
.getTargetDecorationCssStyle(zest);
ZestProperties.setTargetDecorationCssStyle(zest,
zestTargetDecorationCssStyle + "-fx-fill: " //$NON-NLS-1$
+ javaFxFillColor + ";"); //$NON-NLS-1$
}
// arrow size
Double arrowSizeParsed = DotAttributes.getArrowsizeParsed(dot);
double arrowSize = arrowSizeParsed == null ? 1.0 : arrowSizeParsed;
// arrow head
String dotArrowHead = DotAttributes.getArrowhead(dot);
javafx.scene.Node zestEdgeTargetDecoration = null;
if (dotArrowHead == null) {
// use the default arrow head decoration in case the graph is
// directed
if (GraphType.DIGRAPH.equals(DotAttributes
._getType(dot.getGraph().getRootGraph().getRootGraph()))) {
zestEdgeTargetDecoration = DotArrowShapeDecorations
.getDefault(arrowSize, true);
}
} else {
zestEdgeTargetDecoration = computeZestDecoration(
DotAttributes.getArrowheadParsed(dot), arrowSize);
}
// The zest edge target decoration should only appear if the edge
// direction is "forward" or "both".
if (DirType.FORWARD.equals(dotDir) || DirType.BOTH.equals(dotDir)) {
ZestProperties.setTargetDecoration(zest, zestEdgeTargetDecoration);
}
// arrow tail
String dotArrowTail = DotAttributes.getArrowtail(dot);
javafx.scene.Node zestEdgeSourceDecoration = null;
if (dotArrowTail == null) {
// use the default arrow tail decoration in case the graph is
// directed
if (GraphType.DIGRAPH.equals(DotAttributes
._getType(dot.getGraph().getRootGraph().getRootGraph()))) {
zestEdgeSourceDecoration = DotArrowShapeDecorations
.getDefault(arrowSize, true);
}
} else {
zestEdgeSourceDecoration = computeZestDecoration(
DotAttributes.getArrowtailParsed(dot), arrowSize);
}
// The zest edge source decoration should only appear if the edge
// direction is "back" or "both".
if (DirType.BACK.equals(dotDir) || DirType.BOTH.equals(dotDir)) {
ZestProperties.setSourceDecoration(zest, zestEdgeSourceDecoration);
}
// create edge curve
GeometryNode<ICurve> curve = new GeometryNode<>();
ZestProperties.setCurve(zest, curve);
// only convert layout information in native mode, as the results
// will otherwise not match
if (!options().emulateLayout) {
// splines attribute defines connection type
String splines = DotAttributes
.getSplines(dot.getGraph().getRootGraph());
if (Splines.EMPTY.toString().equals(splines)
|| Splines.NONE.toString().equals(splines)) {
// mark as invisible
ZestProperties.setInvisible(zest, true);
}
// position (pos)
String dotPos = DotAttributes.getPos(dot);
if (dotPos != null && !options().ignorePositions) {
// XXX: We use a special format to represent DOT B-splines:
// in case start or end is not given, the
// first or last control point will be contained twice.
final List<Point> bSplineControlPoints = computeZestBSplineControlPoints(
dot);
// mapping to Zest depends on value of 'splines' graph
// attribute
if (Splines.LINE.toString().equals(splines)
|| Splines.FALSE.toString().equals(splines)) {
// use polyline interpolator
// use straight router
// do not use control points
ZestProperties.setInterpolator(zest,
new PolylineInterpolator());
ZestProperties.setRouter(zest, new StraightRouter());
ZestProperties.setStartPoint(zest,
bSplineControlPoints.get(0));
ZestProperties.setEndPoint(zest, bSplineControlPoints
.get(bSplineControlPoints.size() - 1));
} else if (Splines.POLYLINE.toString().equals(splines)) {
// use polyline interpolator
// use straight router
// use control points (without start/end) TODO: verify
ZestProperties.setInterpolator(zest,
new PolylineInterpolator());
ZestProperties.setRouter(zest, new StraightRouter());
ZestProperties.setStartPoint(zest,
bSplineControlPoints.get(0));
ZestProperties.setEndPoint(zest, bSplineControlPoints
.get(bSplineControlPoints.size() - 1));
ZestProperties.setControlPoints(zest, bSplineControlPoints
.subList(1, bSplineControlPoints.size() - 1));
} else if (Splines.ORTHO.toString().equals(splines)) {
// use polyline interpolator
// use orthogonal router
// normalize control points for orthogonal lines
ZestProperties.setInterpolator(zest,
new PolylineInterpolator());
ZestProperties.setRouter(zest, new OrthogonalRouter());
ZestProperties.setStartPoint(zest,
bSplineControlPoints.get(0));
ZestProperties.setEndPoint(zest, bSplineControlPoints
.get(bSplineControlPoints.size() - 1));
ZestProperties.setControlPoints(zest,
computeZestOrthogonalControlPoints(
bSplineControlPoints));
// XXX: OrthogonalProjectionStrategy is set within EdgePart
// when an anchor is attached.
} else if (Splines.COMPOUND.toString().equals(splines)) {
// TODO
} else {
// splines = spline, true and unset
// use dot bspline interpolator
// use dot bspline router
// use control points (without start/end)
ZestProperties.setInterpolator(zest,
new DotBSplineInterpolator());
// use start/end as reference points for the anchor
// computation
ZestProperties.setRouter(zest, new StraightRouter());
ZestProperties.setStartPoint(zest,
bSplineControlPoints.get(0));
ZestProperties.setEndPoint(zest, bSplineControlPoints
.get(bSplineControlPoints.size() - 1));
// first and last way point are provided by start and end
// anchor, so we need to remove them as control points
ZestProperties.setControlPoints(zest, bSplineControlPoints
.subList(1, bSplineControlPoints.size() - 1));
}
}
// label position (lp)
String dotLp = DotAttributes.getLp(dot);
if (dotLabel != null && dotLp != null
&& !options().ignorePositions) {
ZestProperties.setLabelPosition(zest, computeZestLabelPosition(
DotAttributes.getLpParsed(dot), dotLabel));
}
// external label position (xlp)
String dotXlp = DotAttributes.getXlp(dot);
if (dotXLabel != null && dotXlp != null
&& !options().ignorePositions) {
ZestProperties.setExternalLabelPosition(zest,
computeZestLabelPosition(
DotAttributes.getXlpParsed(dot), dotXLabel));
}
// head and tail label positions (head_lp, tail_lp)
String headLp = DotAttributes.getHeadLp(dot);
if (dotHeadLabel != null && headLp != null
&& !options().ignorePositions) {
ZestProperties.setTargetLabelPosition(zest,
computeZestLabelPosition(
DotAttributes.getHeadLpParsed(dot),
dotHeadLabel));
}
String tailLp = DotAttributes.getTailLp(dot);
if (dotTailLabel != null && tailLp != null
&& !options().ignorePositions) {
ZestProperties.setSourceLabelPosition(zest,
computeZestLabelPosition(
DotAttributes.getTailLpParsed(dot),
dotTailLabel));
}
}
}
private List<Point> computeZestOrthogonalControlPoints(
List<Point> bSplineControlPoints) {
// remove start and end point (both are present twice)
List<Point> subList = new ArrayList<>(bSplineControlPoints.subList(1,
bSplineControlPoints.size() - 1));
// normalize remaining points
for (int i = subList.size() - 2; i > 0; i--) {
Point p = subList.get(i + 1);
Point q = subList.get(i);
Point r = subList.get(i - 1);
if (p.x == q.x && q.x == r.x || p.y == q.y && q.y == r.y) {
// remove q
subList.remove(i);
}
}
List<Point> subList2 = subList.subList(1, subList.size() - 1);
return subList2;
}
private javafx.scene.Node computeZestDecoration(ArrowType arrowType,
double arrowSize) {
return DotArrowShapeDecorations.get(arrowType, arrowSize);
}
private List<Point> computeZestBSplineControlPoints(Edge dot) {
SplineType splineType = DotAttributes.getPosParsed(dot);
List<Point> controlPoints = new ArrayList<>();
for (Spline spline : splineType.getSplines()) {
// start
org.eclipse.gef.dot.internal.language.point.Point startp = spline
.getStartp();
if (startp == null) {
// if we have no start point, add the first control
// point twice
startp = spline.getControlPoints().get(0);
}
controlPoints.add(new Point(startp.getX(),
(options().invertYAxis ? -1 : 1) * startp.getY()));
// control points
for (org.eclipse.gef.dot.internal.language.point.Point cp : spline
.getControlPoints()) {
controlPoints.add(new Point(cp.getX(),
(options().invertYAxis ? -1 : 1) * cp.getY()));
}
// end
org.eclipse.gef.dot.internal.language.point.Point endp = spline
.getEndp();
if (endp == null) {
// if we have no end point, add the last control point
// twice
endp = spline.getControlPoints()
.get(spline.getControlPoints().size() - 1);
}
controlPoints.add(new Point(endp.getX(),
(options().invertYAxis ? -1 : 1) * endp.getY()));
}
return controlPoints;
}
protected void convertAttributes(Node dot, Node zest) {
// id
String dotId = DotAttributes.getId(dot);
if (dotId != null) {
ZestProperties.setCssId(zest, dotId);
}
// style and color
String zestShapeStyle = computeZestStyle(dot);
org.eclipse.gef.dot.internal.language.shape.Shape dotShape = DotAttributes
.getShapeParsed(dot);
javafx.scene.Node zestShape = null;
if (dotShape == null) {
// ellipse is default shape
zestShape = new GeometryNode<>(new Ellipse(new Rectangle()));
} else if (dotShape.getShape() instanceof PolygonBasedShape) {
PolygonBasedNodeShape polygonShape = ((PolygonBasedShape) dotShape
.getShape()).getShape();
// handle different polygon shapes
if (PolygonBasedNodeShape.CIRCLE.equals(polygonShape)
|| PolygonBasedNodeShape.ELLIPSE.equals(polygonShape)
|| PolygonBasedNodeShape.OVAL.equals(polygonShape)) {
zestShape = new GeometryNode<>(new Ellipse(new Rectangle()));
} else if (PolygonBasedNodeShape.BOX.equals(polygonShape)
|| PolygonBasedNodeShape.RECT.equals(polygonShape)
|| PolygonBasedNodeShape.RECTANGLE.equals(polygonShape)
|| PolygonBasedNodeShape.SQUARE.equals(polygonShape)) {
zestShape = new GeometryNode<>(new Rectangle());
} else if (PolygonBasedNodeShape.DIAMOND.equals(polygonShape)) {
zestShape = new GeometryNode<>(
new Polygon(0, 50, 50, 0, 100, 50, 50, 100, 0, 50));
} else if (PolygonBasedNodeShape.INVTRIANGLE.equals(polygonShape)) {
zestShape = new GeometryNode<>(
new Polygon(0, 10, 100, 10, 50, 100, 0, 10));
} else if (PolygonBasedNodeShape.TRIANGLE.equals(polygonShape)) {
zestShape = new GeometryNode<>(
new Polygon(0, 50, 50, 0, 100, 50, 0, 50));
} else {
// TODO: handle other polygon shapes
}
} else {
// handle record and custom shapes
}
if (zestShape != null) {
if (zestShapeStyle != null) {
zestShape.setStyle(zestShapeStyle);
}
ZestProperties.setShape(zest, zestShape);
}
// label
String dotLabel = DotAttributes.getLabel(dot);
if (dotLabel == null || dotLabel.equals("\\N")) { //$NON-NLS-1$
// The node default label '\N' is used to indicate that a node's
// name or id becomes its label.
dotLabel = dotId != null ? dotId : DotAttributes._getName(dot);
}
ZestProperties.setLabel(zest, dotLabel);
// external label (xlabel)
String dotXLabel = DotAttributes.getXlabel(dot);
if (dotXLabel != null) {
ZestProperties.setExternalLabel(zest, dotXLabel);
}
// Convert position and size; as node position is interpreted as
// center,
// we need to know the size in order to infer correct zest positions
String dotHeight = DotAttributes.getHeight(dot);
String dotWidth = DotAttributes.getWidth(dot);
// default width is 0.75 inches
double zestWidth = (dotWidth == null ? 0.75
: Double.parseDouble(dotWidth)) * 72;
// default height is 0.5 inches
double zestHeight = (dotHeight == null ? 0.5
: Double.parseDouble(dotHeight)) * 72;
if (options().emulateLayout && !Boolean.TRUE
.equals(DotAttributes.getFixedsizeParsed(dot))) {
// if we are to emulate dot and fixedsize=true is not given, we have
// to compute the size to enclose image, label, and margin.
// TODO: also enclose image and margin
Dimension labelSize = computeZestLabelSize(dotLabel);
ZestProperties.setSize(zest, Dimension
.max(new Dimension(zestWidth, zestHeight), labelSize));
} else {
ZestProperties.setSize(zest, new Dimension(zestWidth, zestHeight));
}
String dotPos = DotAttributes.getPos(dot);
if (dotPos != null && !options().ignorePositions) {
// node position is interpreted as center of node in Dot,
// and top-left in Zest
org.eclipse.gef.dot.internal.language.point.Point dotPosParsed = DotAttributes
.getPosParsed(dot);
ZestProperties.setPosition(zest,
computeZestPosition(dotPosParsed, zestWidth, zestHeight));
// if a position is marked as input-only in Dot, have Zest
// ignore it
ZestProperties.setLayoutIrrelevant(zest,
dotPosParsed.isInputOnly());
}
// external label position (xlp)
String dotXlp = DotAttributes.getXlp(dot);
if (dotXLabel != null && dotXlp != null && !options().ignorePositions) {
org.eclipse.gef.dot.internal.language.point.Point dotXlpParsed = DotAttributes
.getXlpParsed(dot);
ZestProperties.setExternalLabelPosition(zest,
computeZestLabelPosition(dotXlpParsed, dotXLabel));
}
}
private String computeZestStyle(Node dot) {
String zestStyle = null;
// color
Color dotColor = DotAttributes.getColorParsed(dot);
String javaFxColor = computeZestColor(dotColor);
if (javaFxColor != null) {
zestStyle = "-fx-stroke: " + javaFxColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
}
// fillcolor: evaluate only if the node style is set to 'filled'.
boolean isFilledStyle = false;
Style nodeStyle = DotAttributes.getStyleParsed(dot);
if (nodeStyle != null) {
for (StyleItem styleItem : nodeStyle.getStyleItems()) {
if (styleItem.getName().equals(NodeStyle.FILLED.toString())) {
isFilledStyle = true;
break;
}
}
}
if (isFilledStyle) {
Color dotFillColor = DotAttributes.getFillcolorParsed(dot);
String javaFxFillColor = computeZestColor(dotFillColor);
if (javaFxFillColor != null) {
if (zestStyle == null) {
zestStyle = "-fx-fill: " + javaFxFillColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
zestStyle += "-fx-fill: " + javaFxFillColor + ";"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
return zestStyle;
}
private Point computeZestPosition(
org.eclipse.gef.dot.internal.language.point.Point dotPosition,
double widthInPixel, double heightInPixel) {
// dot positions are provided as center positions, Zest uses
// top-left
return new Point(dotPosition.getX() - widthInPixel / 2,
(options().invertYAxis ? -1 : 1) * (dotPosition.getY())
- heightInPixel / 2);
}
private Point computeZestLabelPosition(
org.eclipse.gef.dot.internal.language.point.Point dotLabelPosition,
String labelText) {
Dimension labelSize = computeZestLabelSize(labelText);
return computeZestPosition(dotLabelPosition, labelSize.getWidth(),
labelSize.getHeight());
}
static Dimension computeZestLabelSize(String labelText) {
// TODO: respect font settings (font name and size)
Bounds layoutBounds = new Text(labelText).getLayoutBounds();
return new Dimension(layoutBounds.getWidth(), layoutBounds.getHeight());
}
/**
* Returns the javafx representation of a dot color.
*
* @param dotColor
* The color in dot representation.
* @return The color in javafx representation, or null if the javafx color
* representation cannot be determined.
*/
private String computeZestColor(Color dotColor) {
String javaFxColor = null;
if (dotColor instanceof RGBColor) {
RGBColor rgbColor = (RGBColor) dotColor;
StringBuffer sb = new StringBuffer();
sb.append("#"); //$NON-NLS-1$
sb.append(rgbColor.getR());
sb.append(rgbColor.getG());
sb.append(rgbColor.getB());
if (rgbColor.getA() != null) {
sb.append(rgbColor.getA());
}
javaFxColor = sb.toString();
} else if (dotColor instanceof HSVColor) {
HSVColor hsvColor = (HSVColor) dotColor;
javaFxColor = String.format("hsb(%s, %s%%, %s%%)", //$NON-NLS-1$
Double.parseDouble(hsvColor.getH()) * 360,
Double.parseDouble(hsvColor.getS()) * 100,
Double.parseDouble(hsvColor.getV()) * 100);
} else if (dotColor instanceof StringColor) {
StringColor stringColor = (StringColor) dotColor;
String colorSchema = stringColor.getScheme();
String colorName = stringColor.getName();
if (colorSchema == null || colorSchema.isEmpty()) {
colorSchema = "x11"; //$NON-NLS-1$
}
javaFxColor = DotColors.get(colorSchema, colorName);
}
return javaFxColor;
}
protected void convertAttributes(Graph dot, Graph zest) {
// TODO: graph label
if (options().emulateLayout) {
// convert layout and rankdir to LayoutAlgorithm
Object dotLayout = DotAttributes.getLayout(dot);
ILayoutAlgorithm algo = null;
if (Layout.CIRCO.toString().equals(dotLayout)
|| Layout.NEATO.toString().equals(dotLayout)
|| Layout.TWOPI.toString().equals(dotLayout)) {
algo = new RadialLayoutAlgorithm();
} else if (Layout.FDP.toString().equals(dotLayout)
|| Layout.SFDP.toString().equals(dotLayout)) {
algo = new SpringLayoutAlgorithm();
} else if (Layout.OSAGE.toString().equals(dotLayout)) {
algo = new GridLayoutAlgorithm();
} else {
Rankdir dotRankdir = DotAttributes.getRankdirParsed(dot);
algo = new TreeLayoutAlgorithm(Rankdir.LR.equals(dotRankdir)
? TreeLayoutAlgorithm.LEFT_RIGHT
: TreeLayoutAlgorithm.TOP_DOWN);
}
ZestProperties.setLayoutAlgorithm(zest, algo);
}
}
private Options options;
public Options options() {
if (options == null) {
options = new Options();
}
return options;
}
}