/************************************************************************************************ * Copyright (c) 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: * Tamas Miklossy (itemis AG) - Add support for arrowType edge decorations (bug #477980) * ***********************************************************************************************/ package org.eclipse.gef.dot.internal.ui; import org.eclipse.gef.dot.internal.language.arrowtype.AbstractArrowShape; import org.eclipse.gef.dot.internal.language.arrowtype.ArrowShape; import org.eclipse.gef.dot.internal.language.arrowtype.ArrowType; import org.eclipse.gef.dot.internal.language.arrowtype.DeprecatedArrowShape; import org.eclipse.gef.fx.utils.NodeUtils; import javafx.collections.ObservableList; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.paint.Color; import javafx.scene.shape.Arc; import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; import javafx.scene.shape.Shape; import javafx.scene.shape.StrokeLineJoin; public class DotArrowShapeDecorations { /** * Returns the default dot arrow shape decoration for directed/non-directed * graphs. * * @param arrowSize * The size of the arrow shape decoration. * * @param isGraphDirected * true if the graph is directed, false otherwise * * @return The default dot arrow shape decoration */ public static Node getDefault(double arrowSize, boolean isGraphDirected) { Shape shape = isGraphDirected ? new Normal(arrowSize) : null; setStroke(shape); return shape; } /** * Returns the dot arrow shape decoration corresponding to the * <i>arrowType</i> parameter. * * @param arrowType * The arrow type for which the dot edge decoration should be * determined. * * @param arrowSize * The size of the arrow shape decoration. * * @return The dot arrow shape decoration. */ public static Node get(ArrowType arrowType, double arrowSize) { // The first arrow shape specified should occur closest to the node. double offset = 0.0; Group group = new Group(); for (AbstractArrowShape arrowShape : arrowType.getArrowShapes()) { Shape currentShape = get(arrowShape, arrowSize); if (currentShape == null) { // represent the "none" arrow shape with a transparent box with // the corresponding size currentShape = new Box(arrowSize); currentShape.setFill(Color.TRANSPARENT); currentShape.setTranslateX(offset); } else { if (currentShape instanceof Circle) { // translate a circle-based shape specially because of its // middle-point-based translation currentShape.setTranslateX(offset + currentShape.getLayoutBounds().getWidth() / 2); } else { currentShape.setTranslateX(offset); } } offset += NodeUtils.getShapeBounds(currentShape).getWidth() - currentShape.getStrokeWidth(); group.getChildren().add(currentShape); } return group; } private static Shape get(AbstractArrowShape abstractArrowShape, double arrowSize) { Shape shape = null; if (abstractArrowShape instanceof DeprecatedArrowShape) { switch (((DeprecatedArrowShape) abstractArrowShape).getShape()) { case EDIAMOND: // "ediamond" is deprecated, use "odiamond" shape = new Diamond(arrowSize); setOpen(shape); break; case OPEN: // "open" is deprecated, use "vee" shape = new Vee(arrowSize); setStroke(shape); break; case HALFOPEN: // "halfopen" is deprecated, use "lvee" shape = new Vee(arrowSize); setSide(shape, "l"); //$NON-NLS-1$ setStroke(shape); break; case EMPTY: // "empty" is deprecated, use "onormal" shape = new Normal(arrowSize); setOpen(shape); break; case INVEMPTY: // "invempty" is deprecated, use "oinv" shape = new Inv(arrowSize); setOpen(shape); break; default: break; } } else { ArrowShape arrowShape = (ArrowShape) abstractArrowShape; shape = getPrimitiveShape(arrowShape.getShape(), arrowSize); if (arrowShape.isOpen()) { setOpen(shape); } else { setStroke(shape); } if (arrowShape.getSide() != null) { setSide(shape, arrowShape.getSide()); } } return shape; } private static void setStroke(Shape shape) { if (shape != null) { shape.setStroke(Color.BLACK); shape.setStrokeLineJoin(StrokeLineJoin.ROUND); } } private static void setOpen(Shape shape) { if (shape != null) { shape.setStroke(Color.BLACK); shape.setFill(Color.WHITE); shape.setStrokeLineJoin(StrokeLineJoin.ROUND); } } private static void setSide(Shape shape, String side) { if (shape instanceof Polygon) { setSide((Polygon) shape, side); } else if (shape instanceof Arc) { setSide((Arc) shape, side); } } private static void setSide(Polygon polygon, String side) { // setting the side of a polygon based shape to left/right means to use // 0.0 instead of the negative/positive y coordinates ObservableList<Double> points = polygon.getPoints(); for (int i = 1; i < points.size(); i += 2) { double yCoordinate = points.get(i); if (yCoordinate < 0 && side.equals("l") //$NON-NLS-1$ || yCoordinate > 0 && side.equals("r")) { //$NON-NLS-1$ points.remove(i); points.add(i, 0.0); } } } private static void setSide(Arc arc, String side) { // setting the side of an arc based shape to left/right if (arc instanceof Curve && side.equals("l")) {//$NON-NLS-1$ arc.setStartAngle(180); arc.setLength(90); } if (arc instanceof ICurve && side.equals("l")) {//$NON-NLS-1$ arc.setStartAngle(0); arc.setLength(-90); } if (arc instanceof Curve && side.equals("r")) {//$NON-NLS-1$ arc.setLength(90); } if (arc instanceof ICurve && side.equals("r")) {//$NON-NLS-1$ arc.setLength(-90); } } private static Shape getPrimitiveShape( org.eclipse.gef.dot.internal.language.arrowtype.PrimitiveShape primitiveShape, double arrowSize) { switch (primitiveShape) { case BOX: return new Box(arrowSize); case CROW: return new Crow(arrowSize); case CURVE: return new Curve(arrowSize); case ICURVE: return new ICurve(arrowSize); case DIAMOND: return new Diamond(arrowSize); case DOT: return new Dot(arrowSize); case INV: return new Inv(arrowSize); case NONE: return null; case NORMAL: return new Normal(arrowSize); case TEE: return new Tee(arrowSize); case VEE: return new Vee(arrowSize); default: return null; } } public static interface IPrimitiveShape { double getOffset(); } private static class Box extends Polygon implements IPrimitiveShape { private Box(double arrowSize) { super(0, arrowSize * size / 2, 0, -arrowSize * size / 2, arrowSize * size, -arrowSize * size / 2, arrowSize * size, arrowSize * size / 2); } @Override public double getOffset() { return -NodeUtils.getShapeBounds(this).getX() - getStrokeWidth() / 2; } } private static class Crow extends Polygon implements IPrimitiveShape { private Crow(double arrowSize) { super(arrowSize * size / 2, 0, 0, -arrowSize * size / 2, arrowSize * size, 0, 0, arrowSize * size / 2); } @Override public double getOffset() { return NodeUtils.getShapeBounds(this).getX(); } } private static class Curve extends Arc implements IPrimitiveShape { private Curve(double arrowSize) { super(arrowSize * size / 2, // centerX 0, // centerY arrowSize * size / 2, // radiusX arrowSize * size / 2, // radiusY 90, // startAngle 180// length ); setStyle("-fx-stroke: black;-fx-fill: transparent;"); //$NON-NLS-1$ } @Override public double getOffset() { return 0; } } private static class Diamond extends Polygon implements IPrimitiveShape { private Diamond(double arrowSize) { super(0, 0, arrowSize * size / 2, -arrowSize * size / 3, arrowSize * size, 0, arrowSize * size / 2, arrowSize * size / 3); } @Override public double getOffset() { return NodeUtils.getShapeBounds(this).getX(); } } private static class Dot extends Circle implements IPrimitiveShape { private Dot(double arrowSize) { super(0, 0, arrowSize * size / 2); } @Override public double getOffset() { return -getStrokeWidth() / 2; } } private static class ICurve extends Arc implements IPrimitiveShape { private ICurve(double arrowSize) { super(0, // centerX 0, // centerY arrowSize * size / 2, // radiusX arrowSize * size / 2, // radiusY 90, // startAngle -180// length ); setStyle("-fx-stroke: black;-fx-fill: transparent;"); //$NON-NLS-1$ } @Override public double getOffset() { return NodeUtils.getShapeBounds(this).getWidth(); } } private static class Inv extends Polygon implements IPrimitiveShape { private Inv(double arrowSize) { super(0, arrowSize * size / 3, arrowSize * size, 0, 0, -arrowSize * size / 3); } @Override public double getOffset() { return 0; } } private static class Normal extends Polygon implements IPrimitiveShape { private Normal(double arrowSize) { super(0, 0, arrowSize * size, -arrowSize * size / 3, arrowSize * size, arrowSize * size / 3); } @Override public double getOffset() { return 0; } } private static class Tee extends Polygon implements IPrimitiveShape { private Tee(double arrowSize) { super(0, -arrowSize * size / 2, arrowSize * size / 4, -arrowSize * size / 2, arrowSize * size / 4, arrowSize * size / 2, 0, arrowSize * size / 2); } @Override public double getOffset() { return -NodeUtils.getShapeBounds(this).getX(); } } private static class Vee extends Polygon implements IPrimitiveShape { private Vee(double arrowSize) { super(0, 0, arrowSize * size, -arrowSize * size / 2, 2 * arrowSize * size / 3, 0, arrowSize * size, arrowSize * size / 2); } @Override public double getOffset() { return 0; } } private static double size = 10; }