diff --git a/src/net/sourceforge/plantuml/command/CommandPragma.java b/src/net/sourceforge/plantuml/command/CommandPragma.java index 020ddaab6..c039813df 100644 --- a/src/net/sourceforge/plantuml/command/CommandPragma.java +++ b/src/net/sourceforge/plantuml/command/CommandPragma.java @@ -78,15 +78,19 @@ public class CommandPragma extends SingleLineCommand2 { system.getPragma().define(name, value); if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("jdot")) { return CommandExecutionResult.error( - "This directive has been renamed to '!pragma graphviz_dot smetana'. Please update your diagram."); + "This directive has been renamed to '!pragma layout smetana'. Please update your diagram."); } - if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("smetana")) { + if (name.equalsIgnoreCase("graphviz_dot")) { + return CommandExecutionResult.error("This directive has been renamed to '!pragma layout " + value + + "'. Please update your diagram."); + } + if (name.equalsIgnoreCase("layout") && value.equalsIgnoreCase("smetana")) { system.setUseSmetana(true); } - if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("elk")) { + if (name.equalsIgnoreCase("layout") && value.equalsIgnoreCase("elk")) { system.setUseElk(true); } - if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase(GraphvizUtils.VIZJS)) { + if (name.equalsIgnoreCase("layout") && value.equalsIgnoreCase(GraphvizUtils.VIZJS)) { system.getSkinParam().setUseVizJs(true); } } diff --git a/src/net/sourceforge/plantuml/dedication/BlumBlumShub.java b/src/net/sourceforge/plantuml/dedication/BlumBlumShub.java new file mode 100644 index 000000000..7c61d21f0 --- /dev/null +++ b/src/net/sourceforge/plantuml/dedication/BlumBlumShub.java @@ -0,0 +1,27 @@ +package net.sourceforge.plantuml.dedication; + +import java.math.BigInteger; + +public class BlumBlumShub { + + private static final BigInteger two = BigInteger.valueOf(2L); + + private BigInteger state; + private final BigInteger pq; + + public BlumBlumShub(BigInteger pq, byte[] seed) { + this.pq = pq; + this.state = new BigInteger(1, seed).mod(pq); + } + + public int nextRnd(int numBits) { + int result = 0; + for (int i = numBits; i != 0; --i) { + state = state.modPow(two, pq); + final int bit = state.testBit(0) ? 1 : 0; + result = (result << 1) | bit; + } + return result; + } + +} diff --git a/src/net/sourceforge/plantuml/dedication/Noise.java b/src/net/sourceforge/plantuml/dedication/Noise.java new file mode 100644 index 000000000..b6bae6cf6 --- /dev/null +++ b/src/net/sourceforge/plantuml/dedication/Noise.java @@ -0,0 +1,40 @@ +/* ======================================================================== + * 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.dedication; + +public class Noise { + +} diff --git a/src/net/sourceforge/plantuml/dedication/PSystemDedication.java b/src/net/sourceforge/plantuml/dedication/PSystemDedication.java index 2c0acd8aa..82bf01fc3 100644 --- a/src/net/sourceforge/plantuml/dedication/PSystemDedication.java +++ b/src/net/sourceforge/plantuml/dedication/PSystemDedication.java @@ -36,11 +36,16 @@ package net.sourceforge.plantuml.dedication; import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.lang.reflect.Method; + +import javax.imageio.stream.ImageInputStream; import net.sourceforge.plantuml.FileFormatOption; import net.sourceforge.plantuml.PlainDiagram; import net.sourceforge.plantuml.core.DiagramDescription; import net.sourceforge.plantuml.graphic.UDrawable; +import net.sourceforge.plantuml.security.ImageIO; import net.sourceforge.plantuml.ugraphic.AffineTransformType; import net.sourceforge.plantuml.ugraphic.PixelImage; import net.sourceforge.plantuml.ugraphic.UGraphic; @@ -68,6 +73,26 @@ public class PSystemDedication extends PlainDiagram { }; } + public static BufferedImage getBufferedImage(InputStream is) { + try { + final Class clVP8Decoder = Class.forName("net.sourceforge.plantuml.webp.VP8Decoder"); + final Object vp8Decoder = clVP8Decoder.newInstance(); + // final VP8Decoder vp8Decoder = new VP8Decoder(); + final Method decodeFrame = clVP8Decoder.getMethod("decodeFrame", ImageInputStream.class); + final ImageInputStream iis = ImageIO.createImageInputStream(is); + decodeFrame.invoke(vp8Decoder, iis); + // vp8Decoder.decodeFrame(iis); + iis.close(); + final Object frame = clVP8Decoder.getMethod("getFrame").invoke(vp8Decoder); + return (BufferedImage) frame.getClass().getMethod("getBufferedImage").invoke(frame); + // final VP8Frame frame = vp8Decoder.getFrame(); + // return frame.getBufferedImage(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public DiagramDescription getDescription() { return new DiagramDescription("(Dedication)"); } diff --git a/src/net/sourceforge/plantuml/dedication/RBlock.java b/src/net/sourceforge/plantuml/dedication/RBlock.java new file mode 100644 index 000000000..b777a2aab --- /dev/null +++ b/src/net/sourceforge/plantuml/dedication/RBlock.java @@ -0,0 +1,72 @@ +/* ======================================================================== + * 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.dedication; + +import java.math.BigInteger; + +public class RBlock { + + private final byte[] buffer; + + private RBlock(final byte[] init) { + this.buffer = new byte[init.length + 1]; + System.arraycopy(init, 0, buffer, 1, init.length); + } + + public RBlock(final byte[] init, int start, int size) { + this.buffer = new byte[size + 1]; + if (start + size < init.length) + System.arraycopy(init, start, buffer, 1, size); + else + System.arraycopy(init, start, buffer, 1, init.length - start); + } + + public RBlock change(BigInteger E, BigInteger N) { + final BigInteger big = new BigInteger(buffer); + final BigInteger changed = big.modPow(E, N); + return new RBlock(changed.toByteArray()); + } + + public byte[] getData(int size) { + if (buffer.length == size) { + return buffer; + } + final byte[] result = new byte[size]; + System.arraycopy(buffer, buffer.length - size, result, 0, size); + return result; + } + +} diff --git a/src/net/sourceforge/plantuml/dedication/RBlocks.java b/src/net/sourceforge/plantuml/dedication/RBlocks.java new file mode 100644 index 000000000..921f9b057 --- /dev/null +++ b/src/net/sourceforge/plantuml/dedication/RBlocks.java @@ -0,0 +1,105 @@ +/* ======================================================================== + * 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.dedication; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class RBlocks { + + private final List all = new ArrayList(); + + private RBlocks() { + + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(all.size()); + for (RBlock block : all) { + sb.append(" - "); + sb.append(block.toString()); + } + return sb.toString(); + } + + public static RBlocks readFrom(byte[] fileContent, int size) { + final RBlocks result = new RBlocks(); + int start = 0; + while (start < fileContent.length) { + final RBlock block = new RBlock(fileContent, start, size); + start += size; + result.all.add(block); + } + return result; + } + + public RBlocks change(BigInteger E, BigInteger N) { + final RBlocks result = new RBlocks(); + for (RBlock rsa : all) { + result.all.add(rsa.change(E, N)); + } + return result; + } + + public void writeTo(Path out, int size) throws IOException { + writeTo(new FileOutputStream(out.toFile()), size); + } + + public byte[] toByteArray(int size) throws IOException { + final byte[] result = new byte[size * all.size()]; + for (int i = 0; i < all.size(); i++) { + final byte[] tmp = all.get(i).getData(size); + System.arraycopy(tmp, 0, result, i * size, tmp.length); + } + return result; + } + + public void writeTo(OutputStream os, int size) throws IOException { + for (RBlock rsa : all) { + final byte[] tmp = rsa.getData(size); + os.write(tmp); + } + os.close(); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java index ca7fb1b61..323a33f3a 100644 --- a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java +++ b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java @@ -40,6 +40,7 @@ import java.awt.geom.Point2D; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; @@ -47,9 +48,9 @@ import java.util.Map; import java.util.Map.Entry; import org.eclipse.elk.core.RecursiveGraphLayoutEngine; -import org.eclipse.elk.core.math.ElkPadding; import org.eclipse.elk.core.options.CoreOptions; import org.eclipse.elk.core.options.Direction; +import org.eclipse.elk.core.options.EdgeLabelPlacement; import org.eclipse.elk.core.options.EdgeType; import org.eclipse.elk.core.options.NodeLabelPlacement; import org.eclipse.elk.core.options.SizeConstraint; @@ -69,8 +70,11 @@ 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.GroupType; +import net.sourceforge.plantuml.cucadiagram.IGroup; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.Link; +import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory; import net.sourceforge.plantuml.graphic.AbstractTextBlock; import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.HorizontalAlignment; @@ -87,9 +91,23 @@ import net.sourceforge.plantuml.svek.IEntityImage; import net.sourceforge.plantuml.svek.TextBlockBackcolored; import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.URectangle; +import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.color.HColorUtils; +/* + * Some notes: + * +https://www.eclipse.org/elk/documentation/tooldevelopers/graphdatastructure.html +https://www.eclipse.org/elk/documentation/tooldevelopers/graphdatastructure/coordinatesystem.html + +Long hierarchical edge + +https://rtsys.informatik.uni-kiel.de/~biblio/downloads/theses/yab-bt.pdf +https://rtsys.informatik.uni-kiel.de/~biblio/downloads/theses/thw-bt.pdf + */ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { private final CucaDiagram diagram; @@ -97,6 +115,7 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { private final DotStringFactory dotStringFactory; private final Map nodes = new LinkedHashMap(); + private final Map clusters = new LinkedHashMap(); private final Map edges = new LinkedHashMap(); public CucaDiagramFileMakerElk(CucaDiagram diagram, StringBounder stringBounder) { @@ -106,34 +125,51 @@ 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(); + if (Display.isNull(link.getLabel())) { + return null; + } + final 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 null; } - return TextBlockUtils.withMargin(label, marginLabel, marginLabel); + return label; } - // 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 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 null; } - return TextBlockUtils.withMargin(label, marginLabel, marginLabel); + return label; + } + + // Retrieve the real position of a node, depending on its parents + private Point2D getPosition(ElkNode elkNode) { + final ElkNode parent = elkNode.getParent(); + + final double x = elkNode.getX(); + final double y = elkNode.getY(); + + // This nasty test checks that parent is "root" + if (parent == null || parent.getLabels().size() == 0) { + return new Point2D.Double(x, y); + } + + // Right now, this is recursive + final Point2D parentPosition = getPosition(parent); + return new Point2D.Double(parentPosition.getX() + x, parentPosition.getY() + y); + } // The Drawing class does the real drawing @@ -148,15 +184,28 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { public void drawU(UGraphic ug) { + // Draw all clusters + for (Entry ent : clusters.entrySet()) { + final IGroup group = ent.getKey(); + final ElkNode elkNode = ent.getValue(); + + final Point2D corner = getPosition(elkNode); + + final URectangle rect = new URectangle(elkNode.getWidth(), elkNode.getHeight()); + + // Print a simple rectangle right now + ug.apply(HColorUtils.BLACK).apply(new UStroke(1.5)).apply(new UTranslate(corner)).draw(rect); + } + // Draw all nodes for (Entry ent : nodes.entrySet()) { final ILeaf leaf = ent.getKey(); - final ElkNode agnode = ent.getValue(); + final ElkNode elkNode = ent.getValue(); final IEntityImage image = printEntityInternal(leaf); // Retrieve coord from ELK - final Point2D corner = new Point2D.Double(agnode.getX(), agnode.getY()); + final Point2D corner = getPosition(elkNode); // Print the node image at right coord image.drawU(ug.apply(new UTranslate(corner))); @@ -169,15 +218,14 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { continue; } final ElkEdge edge = ent.getValue(); + + // Unfortunately, we have to translate "edge" in its own "cluster" coordonate + final Point2D translate = getPosition(edge.getContainingNode()); + final UGraphic ugTranslated = ug.apply(new UTranslate(translate)); + new ElkPath(link, edge, diagram, getLabel(link), getQualifier(link, 1), getQualifier(link, 2)) - .drawU(ug); - if (Display.isNull(link.getLabel()) == false) { - final ElkLabel label = edge.getLabels().get(0); - final double x = label.getX(); - final double y = label.getY(); - final TextBlock labelLink = getLabel(link); - labelLink.drawU(ug.apply(new UTranslate(x, y))); - } + .drawU(ugTranslated); + } } @@ -195,83 +243,31 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { } + private Collection getUnpackagedEntities() { + final List result = new ArrayList(); + for (ILeaf ent : diagram.getLeafsvalues()) { + if (diagram.getEntityFactory().getRootGroup() == ent.getParentContainer()) { + result.add(ent); + } + } + return result; + } + @Override public ImageData createFile(OutputStream os, List dotStrings, FileFormatOption fileFormatOption) throws IOException { + // https://www.eclipse.org/forums/index.php/t/1095737/ try { final ElkNode root = ElkGraphUtil.createGraph(); root.setProperty(CoreOptions.DIRECTION, Direction.DOWN); - // This padding setting have no impact ? - final ElkPadding labelPadding = new ElkPadding(100.0); + printAllSubgroups(root, diagram.getRootGroup()); + printEntities(root, getUnpackagedEntities()); - // Convert all "leaf" to ELK node - for (ILeaf leaf : diagram.getLeafsvalues()) { - final IEntityImage image = printEntityInternal(leaf); + manageAllEdges(); - // Expected dimension of the node - final Dimension2D dimension = image.calculateDimension(stringBounder); - - // Here, we try to tell ELK to use this dimension as node dimension - final ElkNode node = ElkGraphUtil.createNode(root); - node.setDimensions(dimension.getWidth(), dimension.getHeight()); - - // There is no real "label" here - // We just would like to force node dimension - final ElkLabel label = ElkGraphUtil.createLabel(node); - 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(), dimension.getHeight() - VERY_STRANGE_OFFSET); - - // No idea of what we are doing here :-) - label.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, EnumSet.of(NodeLabelPlacement.INSIDE, - NodeLabelPlacement.H_CENTER, NodeLabelPlacement.V_CENTER)); - label.setProperty(CoreOptions.NODE_LABELS_PADDING, labelPadding); - node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.NODE_LABELS)); - node.setProperty(CoreOptions.NODE_SIZE_OPTIONS, EnumSet.noneOf(SizeOptions.class)); - - // Let's store this - 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); - if (Display.isNull(link.getLabel()) == false) { - final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); - final TextBlock labelLink = getLabel(link); - final Dimension2D labelLinkDim = labelLink.calculateDimension(stringBounder); - edgeLabel.setText("X"); - edgeLabel.setDimensions(labelLinkDim.getWidth(), labelLinkDim.getHeight()); - edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); - edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); - - } - 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 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()); -// } + new RecursiveGraphLayoutEngine().layout(root, new NullElkProgressMonitor()); final MinMax minMax = TextBlockUtils.getMinMax(new Drawing(null), stringBounder, false); @@ -288,6 +284,134 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { } + private void printAllSubgroups(ElkNode cluster, IGroup group) { + for (IGroup g : diagram.getChildrenGroups(group)) { + if (g.isRemoved()) { + continue; + } + if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) { + final ISkinParam skinParam = diagram.getSkinParam(); + final EntityFactory entityFactory = diagram.getEntityFactory(); + final ILeaf folder = entityFactory.createLeafForEmptyGroup(g, skinParam); + System.err.println("STILL IN PROGRESS"); + // printEntityNew(folder); + } else { + + // We create the "cluster" in ELK for this group + final ElkNode elkCluster = ElkGraphUtil.createNode(cluster); + elkCluster.setProperty(CoreOptions.DIRECTION, Direction.DOWN); + + // Not sure this is usefull to put a label on a "cluster" + final ElkLabel label = ElkGraphUtil.createLabel(elkCluster); + label.setText("C"); + // We need it anyway to recurse up to the real "root" + + this.clusters.put(g, elkCluster); + printSingleGroup(g); + } + } + + } + + private void printSingleGroup(IGroup g) { + if (g.getGroupType() == GroupType.CONCURRENT_STATE) { + return; + } + this.printEntities(clusters.get(g), g.getLeafsDirect()); + printAllSubgroups(clusters.get(g), g); + } + + private void printEntities(ElkNode parent, Collection entities) { + // Convert all "leaf" to ELK node + for (ILeaf ent : entities) { + if (ent.isRemoved()) { + continue; + } + manageSingleNode(parent, ent); + } + } + + private void manageAllEdges() { + // Convert all "link" to ELK edge + for (final Link link : diagram.getLinks()) { + manageSingleEdge(link); + } + } + + private void manageSingleNode(final ElkNode root, ILeaf leaf) { + final IEntityImage image = printEntityInternal(leaf); + + // Expected dimension of the node + final Dimension2D dimension = image.calculateDimension(stringBounder); + + // Here, we try to tell ELK to use this dimension as node dimension + final ElkNode node = ElkGraphUtil.createNode(root); + node.setDimensions(dimension.getWidth(), dimension.getHeight()); + + // There is no real "label" here + // We just would like to force node dimension + final ElkLabel label = ElkGraphUtil.createLabel(node); + 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(), dimension.getHeight() - VERY_STRANGE_OFFSET); + + // No idea of what we are doing here :-) + label.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, + EnumSet.of(NodeLabelPlacement.INSIDE, NodeLabelPlacement.H_CENTER, NodeLabelPlacement.V_CENTER)); + // This padding setting have no impact ? + // label.setProperty(CoreOptions.NODE_LABELS_PADDING, new ElkPadding(100.0)); + node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.NODE_LABELS)); + node.setProperty(CoreOptions.NODE_SIZE_OPTIONS, EnumSet.noneOf(SizeOptions.class)); + + // Let's store this + nodes.put(leaf, node); + } + + private void manageSingleEdge(final Link link) { + final ElkNode node1 = nodes.get(link.getEntity1()); + final ElkNode node2 = nodes.get(link.getEntity2()); + + final ElkEdge edge = ElkGraphUtil.createEdge(node1.getParent()); + + final TextBlock labelLink = getLabel(link); + if (labelLink != null) { + final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); + final Dimension2D dim = labelLink.calculateDimension(stringBounder); + edgeLabel.setText("X"); + edgeLabel.setDimensions(dim.getWidth(), dim.getHeight()); + // Duplicated, with qualifier, but who cares? + edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); + edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + } + if (link.getQualifier1() != null) { + final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); + final Dimension2D dim = getQualifier(link, 1).calculateDimension(stringBounder); + // Nasty trick, we store the kind of label in the text + edgeLabel.setText("1"); + edgeLabel.setDimensions(dim.getWidth(), dim.getHeight()); + edgeLabel.setProperty(CoreOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.TAIL); + // Duplicated, with main label, but who cares? + edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); + edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + } + if (link.getQualifier2() != null) { + final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); + final Dimension2D dim = getQualifier(link, 2).calculateDimension(stringBounder); + // Nasty trick, we store the kind of label in the text + edgeLabel.setText("2"); + edgeLabel.setDimensions(dim.getWidth(), dim.getHeight()); + edgeLabel.setProperty(CoreOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.HEAD); + // Duplicated, with main label, but who cares? + edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); + edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + } + edge.getSources().add(node1); + edge.getTargets().add(node2); + edges.put(link, edge); + } + static private List getFailureText3(Throwable exception) { exception.printStackTrace(); final List strings = new ArrayList(); diff --git a/src/net/sourceforge/plantuml/elk/ElkPath.java b/src/net/sourceforge/plantuml/elk/ElkPath.java index 158151f50..d4ee757ee 100644 --- a/src/net/sourceforge/plantuml/elk/ElkPath.java +++ b/src/net/sourceforge/plantuml/elk/ElkPath.java @@ -38,6 +38,7 @@ 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.elk.graph.ElkLabel; import org.eclipse.emf.common.util.EList; import net.sourceforge.plantuml.ColorParam; @@ -62,18 +63,18 @@ public class ElkPath implements UDrawable { private final ElkEdge edge; private final CucaDiagram diagram; - private final TextBlock label; + private final TextBlock centerLabel; 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, + public ElkPath(Link link, ElkEdge edge, CucaDiagram diagram, TextBlock centerLabel, TextBlock tailLabel, TextBlock headLabel) { this.link = link; this.edge = edge; this.diagram = diagram; - this.label = label; + this.centerLabel = centerLabel; this.tailLabel = tailLabel; this.headLabel = headLabel; } @@ -119,9 +120,39 @@ public class ElkPath implements UDrawable { ug = ug.apply(stroke).apply(color); final EList sections = edge.getSections(); + if (sections.size() == 0) { + System.err.println("Strange: no section?"); + System.err.println("Maybe a 'Long hierarchical edge' " + edge.isHierarchical()); + } else { + drawSections(ug, sections); + } + drawLabels(ug); + + } + + private void drawLabels(UGraphic ug) { + for (ElkLabel label : edge.getLabels()) { + final double x = label.getX(); + final double y = label.getY(); + final TextBlock labelLink; + // Nasty trick: we store the type of label (center/head/tail) in the text + final String type = label.getText(); + if ("X".equals(type)) { + labelLink = centerLabel; + } else if ("1".equals(type)) { + labelLink = tailLabel; + } else if ("2".equals(type)) { + labelLink = headLabel; + } else { + continue; + } + labelLink.drawU(ug.apply(new UTranslate(x, y))); + } + } + + private void drawSections(UGraphic ug, final EList sections) { for (ElkEdgeSection section : sections) { - final EList points = section.getBendPoints(); double x1 = section.getStartX(); @@ -136,7 +167,6 @@ public class ElkPath implements UDrawable { 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) { diff --git a/src/net/sourceforge/plantuml/nwdiag/GridTextBlockDecorated.java b/src/net/sourceforge/plantuml/nwdiag/GridTextBlockDecorated.java index 8349442cd..6dcb112c5 100644 --- a/src/net/sourceforge/plantuml/nwdiag/GridTextBlockDecorated.java +++ b/src/net/sourceforge/plantuml/nwdiag/GridTextBlockDecorated.java @@ -178,8 +178,10 @@ public class GridTextBlockDecorated extends GridTextBlockSimple { final Footprint footprint1 = getFootprint(group1); final Footprint footprint2 = getFootprint(group2); final Footprint inter = footprint1.intersection(footprint2); - data.swapCols(inter.getMin(), inter.getMax()); - return; + if (inter != null) { + data.swapCols(inter.getMin(), inter.getMax()); + return; + } } } diff --git a/src/net/sourceforge/plantuml/sdot/CucaDiagramFileMakerSmetana.java b/src/net/sourceforge/plantuml/sdot/CucaDiagramFileMakerSmetana.java index f5ca14af9..6a79d2e25 100644 --- a/src/net/sourceforge/plantuml/sdot/CucaDiagramFileMakerSmetana.java +++ b/src/net/sourceforge/plantuml/sdot/CucaDiagramFileMakerSmetana.java @@ -194,7 +194,7 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { this.stringBounder = stringBounder; this.dotStringFactory = new DotStringFactory(stringBounder, diagram); - printGroups(diagram.getRootGroup()); + printAllSubgroups(diagram.getRootGroup()); printEntities(getUnpackagedEntities()); } @@ -227,7 +227,7 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { } } - private void printGroups(IGroup parent) { + private void printAllSubgroups(IGroup parent) { for (IGroup g : diagram.getChildrenGroups(parent)) { if (g.isRemoved()) { continue; @@ -238,12 +238,12 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { final ILeaf folder = entityFactory.createLeafForEmptyGroup(g, skinParam); printEntityNew(folder); } else { - printGroup(g); + printSingleGroup(g); } } } - private void printGroup(IGroup g) { + private void printSingleGroup(IGroup g) { if (g.getGroupType() == GroupType.CONCURRENT_STATE) { return; } @@ -274,7 +274,7 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { dotStringFactory.openCluster(titleAndAttributeWidth, titleAndAttributeHeight, title, stereo, g); this.printEntities(g.getLeafsDirect()); - printGroups(g); + printAllSubgroups(g); dotStringFactory.closeCluster(); } @@ -284,8 +284,8 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { .getMergedStyle(diagram.getSkinParam().getCurrentStyleBuilder()); } - private void printEntities(Collection entities2) { - for (ILeaf ent : entities2) { + private void printEntities(Collection entities) { + for (ILeaf ent : entities) { if (ent.isRemoved()) { continue; } @@ -293,8 +293,8 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker { } } - private void exportEntities(ST_Agraph_s g, Collection entities2) { - for (ILeaf ent : entities2) { + private void exportEntities(ST_Agraph_s g, Collection entities) { + for (ILeaf ent : entities) { if (ent.isRemoved()) { continue; }