From 320ab186b4e84dd890626a86f7271292d9cba09a Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Wed, 14 Apr 2021 20:08:29 +0200 Subject: [PATCH] Improve ELK support --- .../plantuml/elk/CucaDiagramFileMakerElk.java | 77 ++++++++- src/net/sourceforge/plantuml/elk/ElkPath.java | 147 ++++++++++++++++++ 2 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 src/net/sourceforge/plantuml/elk/ElkPath.java diff --git a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java index 7dce40f64..7ac13a77e 100644 --- a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java +++ b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java @@ -59,15 +59,19 @@ import org.eclipse.elk.graph.ElkNode; import org.eclipse.elk.graph.util.ElkGraphUtil; import net.sourceforge.plantuml.FileFormatOption; +import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.StringUtils; import net.sourceforge.plantuml.UmlDiagram; import net.sourceforge.plantuml.api.ImageDataSimple; import net.sourceforge.plantuml.core.ImageData; import net.sourceforge.plantuml.cucadiagram.CucaDiagram; +import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.Link; import net.sourceforge.plantuml.graphic.AbstractTextBlock; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; import net.sourceforge.plantuml.graphic.QuoteUtils; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; @@ -91,6 +95,7 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { private final DotStringFactory dotStringFactory; private final Map nodes = new LinkedHashMap(); + private final Map edges = new LinkedHashMap(); public CucaDiagramFileMakerElk(CucaDiagram diagram, StringBounder stringBounder) { this.diagram = diagram; @@ -99,6 +104,36 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { } + // Unused right now + private TextBlock getLabel(Link link) { + final double marginLabel = 1; // startUid.equals(endUid) ? 6 : 1; + ISkinParam skinParam = diagram.getSkinParam(); + final FontConfiguration labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null); + final TextBlock label = link.getLabel().create(labelFont, + skinParam.getDefaultTextAlignment(HorizontalAlignment.CENTER), skinParam); + if (TextBlockUtils.isEmpty(label, stringBounder)) { + return label; + } + return TextBlockUtils.withMargin(label, marginLabel, marginLabel); + } + + // Unused right now + private TextBlock getQualifier(Link link, int n) { + final String tmp = n == 1 ? link.getQualifier1() : link.getQualifier2(); + if (tmp == null) { + return null; + } + final double marginLabel = 1; // startUid.equals(endUid) ? 6 : 1; + ISkinParam skinParam = diagram.getSkinParam(); + final FontConfiguration labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null); + final TextBlock label = Display.getWithNewlines(tmp).create(labelFont, + skinParam.getDefaultTextAlignment(HorizontalAlignment.CENTER), skinParam); + if (TextBlockUtils.isEmpty(label, stringBounder)) { + return label; + } + return TextBlockUtils.withMargin(label, marginLabel, marginLabel); + } + // The Drawing class does the real drawing class Drawing extends AbstractTextBlock implements TextBlockBackcolored { @@ -125,6 +160,17 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { image.drawU(ug.apply(new UTranslate(corner))); } + // Draw all edges + for (Entry ent : edges.entrySet()) { + final Link link = ent.getKey(); + if (link.isInvis()) { + continue; + } + final ElkEdge edge = ent.getValue(); + new ElkPath(link, edge, diagram, getLabel(link), getQualifier(link, 1), getQualifier(link, 2)) + .drawU(ug); + } + } public Dimension2D calculateDimension(StringBounder stringBounder) { @@ -146,7 +192,9 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { try { final ElkNode root = ElkGraphUtil.createGraph(); - final ElkPadding labelPadding = new ElkPadding(2.0); + + // This padding setting have no impact ? + final ElkPadding labelPadding = new ElkPadding(100.0); // Convert all "leaf" to ELK node for (ILeaf leaf : diagram.getLeafsvalues()) { @@ -162,7 +210,11 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { // There is no real "label" here // We just would like to force node dimension final ElkLabel label = ElkGraphUtil.createLabel(node); - label.setDimensions(dimension.getWidth(), dimension.getHeight()); + label.setText("X"); + + // I don't know why we have to do this hack, but somebody has to fix it + final double VERY_STRANGE_OFFSET = 10; + label.setDimensions(dimension.getWidth() - VERY_STRANGE_OFFSET, dimension.getHeight()); // No idea of what we are doing here :-) label.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, EnumSet.of(NodeLabelPlacement.INSIDE, @@ -175,22 +227,31 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { nodes.put(leaf, node); } + // https://www.eclipse.org/forums/index.php/t/1095737/ + for (final Link link : diagram.getLinks()) { final ElkEdge edge = ElkGraphUtil.createEdge(root); - System.err.println("edge=" + edge); edge.getSources().add(nodes.get(link.getEntity1())); edge.getTargets().add(nodes.get(link.getEntity2())); + edges.put(link, edge); } final RecursiveGraphLayoutEngine engine = new RecursiveGraphLayoutEngine(); engine.layout(root, new NullElkProgressMonitor()); // Debug - for (final ElkNode node : nodes.values()) { - final String name = node.getLabels().get(0).getText(); - System.out.println("node " + name + " : " + node.getX() + ", " + node.getY() + " (" + node.getWidth() - + ", " + node.getHeight() + ")"); - } +// for (final ElkNode node : nodes.values()) { +// final String name = node.getLabels().get(0).getText(); +// System.out.println("node " + name + " : " + node.getX() + ", " + node.getY() + " (" + node.getWidth() +// + ", " + node.getHeight() + ")"); +// } +// for (final ElkEdge edge : edges.values()) { +// final EList sections = edge.getSections(); +// System.out.println("edge=" + edge.getSections()); +// System.out.println("edge=" + edge.getProperty(LayeredOptions.JUNCTION_POINTS)); +// for (ElkEdgeSection s : sections) +// System.out.println(s.getBendPoints()); +// } final MinMax minMax = TextBlockUtils.getMinMax(new Drawing(null), stringBounder, false); diff --git a/src/net/sourceforge/plantuml/elk/ElkPath.java b/src/net/sourceforge/plantuml/elk/ElkPath.java new file mode 100644 index 000000000..158151f50 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/ElkPath.java @@ -0,0 +1,147 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.elk; + +import org.eclipse.elk.graph.ElkBendPoint; +import org.eclipse.elk.graph.ElkEdge; +import org.eclipse.elk.graph.ElkEdgeSection; +import org.eclipse.emf.common.util.EList; + +import net.sourceforge.plantuml.ColorParam; +import net.sourceforge.plantuml.LineParam; +import net.sourceforge.plantuml.UmlDiagramType; +import net.sourceforge.plantuml.cucadiagram.CucaDiagram; +import net.sourceforge.plantuml.cucadiagram.Link; +import net.sourceforge.plantuml.cucadiagram.LinkType; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.graphic.UDrawable; +import net.sourceforge.plantuml.graphic.color.ColorType; +import net.sourceforge.plantuml.skin.rose.Rose; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.ULine; +import net.sourceforge.plantuml.ugraphic.UStroke; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; + +public class ElkPath implements UDrawable { + + private final Link link; + private final ElkEdge edge; + + private final CucaDiagram diagram; + private final TextBlock label; + private final TextBlock headLabel; + private final TextBlock tailLabel; + private final Rose rose = new Rose(); + + public ElkPath(Link link, ElkEdge edge, CucaDiagram diagram, TextBlock label, TextBlock tailLabel, + TextBlock headLabel) { + this.link = link; + this.edge = edge; + + this.diagram = diagram; + this.label = label; + this.tailLabel = tailLabel; + this.headLabel = headLabel; + } + + private ColorParam getArrowColorParam() { + if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS) { + return ColorParam.arrow; + } else if (diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) { + return ColorParam.arrow; + } else if (diagram.getUmlDiagramType() == UmlDiagramType.DESCRIPTION) { + return ColorParam.arrow; + } else if (diagram.getUmlDiagramType() == UmlDiagramType.ACTIVITY) { + return ColorParam.arrow; + } else if (diagram.getUmlDiagramType() == UmlDiagramType.STATE) { + return ColorParam.arrow; + } + throw new IllegalStateException(); + } + + public void drawU(UGraphic ug) { + + if (link.isHidden()) { + return; + } + + HColor color = rose.getHtmlColor(diagram.getSkinParam(), null, getArrowColorParam()); + + if (this.link.getColors() != null) { + final HColor newColor = this.link.getColors().getColor(ColorType.ARROW, ColorType.LINE); + if (newColor != null) { + color = newColor; + } + + } else if (this.link.getSpecificColor() != null) { + color = this.link.getSpecificColor(); + } + + final LinkType linkType = link.getType(); + UStroke stroke = linkType.getStroke3(diagram.getSkinParam().getThickness(LineParam.arrow, null)); + if (link.getColors() != null && link.getColors().getSpecificLineStroke() != null) { + stroke = link.getColors().getSpecificLineStroke(); + } + ug = ug.apply(stroke).apply(color); + + final EList sections = edge.getSections(); + + for (ElkEdgeSection section : sections) { + + final EList points = section.getBendPoints(); + + double x1 = section.getStartX(); + double y1 = section.getStartY(); + + for (ElkBendPoint pt : points) { + drawLine(ug, x1, y1, pt.getX(), pt.getY()); + x1 = pt.getX(); + y1 = pt.getY(); + } + + drawLine(ug, x1, y1, section.getEndX(), section.getEndY()); + + } + + } + + private void drawLine(UGraphic ug, final double x1, final double y1, final double x2, final double y2) { + final ULine line = new ULine(x2 - x1, y2 - y1); + ug.apply(new UTranslate(x1, y1)).draw(line); + } + +}