fix: improve smetana support

https://github.com/plantuml/plantuml/issues/1348
This commit is contained in:
Arnaud Roques 2023-03-21 23:47:11 +01:00
parent 897a4736bc
commit cdec3380ec
14 changed files with 208 additions and 87 deletions

View File

@ -61,10 +61,10 @@ final public class ST_Agedge_s extends ST_Agobj_s {
public String NAME;
@Override
public String toString() {
return NAME;
}
// @Override
// public String toString() {
// return NAME;
// }
@Override
public void ___(__struct__ arg) {

View File

@ -59,14 +59,14 @@ final public class ST_Agnode_s extends ST_Agobj_s {
public String NAME;
@Override
public String toString() {
try {
return NAME + " rank=" + ND_rank(this) + " order=" + ND_order(this);
} catch (Exception e) {
return NAME;
}
}
// @Override
// public String toString() {
// try {
// return NAME + " rank=" + ND_rank(this) + " order=" + ND_order(this);
// } catch (Exception e) {
// return NAME;
// }
// }
@Override
public void ___(__struct__ arg) {

View File

@ -68,10 +68,10 @@ final public class ST_Agraph_s extends ST_Agobj_s {
public String NAME;
private static int CPT = 0;
@Override
public String toString() {
return super.toString() + " " + NAME;
}
// @Override
// public String toString() {
// return super.toString() + " " + NAME;
// }
public ST_Agraph_s() {
this.NAME = "G" + CPT;

View File

@ -541,4 +541,8 @@ public class Link extends WithLinkType implements Hideable, Removeable {
return getLinkArg().getVisibilityModifier();
}
public final boolean isOpale() {
return opale;
}
}

View File

@ -50,6 +50,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -100,6 +101,7 @@ import net.sourceforge.plantuml.svek.GeneralImageBuilder;
import net.sourceforge.plantuml.svek.GraphvizCrash;
import net.sourceforge.plantuml.svek.IEntityImage;
import net.sourceforge.plantuml.svek.SvekNode;
import net.sourceforge.plantuml.svek.image.EntityImageNote;
import net.sourceforge.plantuml.svek.image.EntityImageNoteLink;
import net.sourceforge.plantuml.utils.Position;
import smetana.core.CString;
@ -117,6 +119,7 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
private final Map<Entity, ST_Agnode_s> nodes = new LinkedHashMap<Entity, ST_Agnode_s>();
private final Map<Entity, ST_Agnode_s> coreNodes = new LinkedHashMap<Entity, ST_Agnode_s>();
private final Map<Link, ST_Agedge_s> edges = new LinkedHashMap<Link, ST_Agedge_s>();
private final Map<Link, SmetanaPath> smetanaPathes = new LinkedHashMap<Link, SmetanaPath>();
private final Map<Entity, ST_Agraph_s> clusters = new LinkedHashMap<Entity, ST_Agraph_s>();
private final DotStringFactory dotStringFactory;
@ -155,8 +158,22 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
}
public void drawU(UGraphic ug) {
smetanaPathes.clear();
ug = ug.apply(new UTranslate(6, 6 - minMax.getMinY()));
for (Map.Entry<Link, ST_Agedge_s> ent : edges.entrySet()) {
final Link link = ent.getKey();
if (link.isInvis())
continue;
final ST_Agedge_s edge = ent.getValue();
final SmetanaPath smetanaPath = new SmetanaPath(link, edge, ymirror, diagram, getLabel(link),
getQuantifier(link, 1), getQuantifier(link, 2), dotStringFactory.getBibliotekon());
smetanaPathes.put(link, smetanaPath);
}
for (Map.Entry<Entity, ST_Agraph_s> ent : clusters.entrySet())
drawGroup(ug, ymirror, ent.getKey(), ent.getValue());
@ -166,19 +183,16 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
final XPoint2D corner = getCorner(agnode);
final SvekNode node = dotStringFactory.getBibliotekon().getNode(leaf);
node.resetMoveSvek();
node.moveSvek(corner.getX(), corner.getY());
final IEntityImage image = node.getImage();
image.drawU(ug.apply(UTranslate.point(corner)));
}
for (Map.Entry<Link, ST_Agedge_s> ent : edges.entrySet()) {
final Link link = ent.getKey();
if (link.isInvis())
continue;
for (Entry<Link, SmetanaPath> ent : smetanaPathes.entrySet())
if (ent.getKey().isOpale() == false)
ent.getValue().drawU(ug);
final ST_Agedge_s edge = ent.getValue();
new SmetanaPath(link, edge, ymirror, diagram, getLabel(link), getQuantifier(link, 1),
getQuantifier(link, 2), dotStringFactory.getBibliotekon()).drawU(ug);
}
}
public XDimension2D calculateDimension(StringBounder stringBounder) {
@ -205,6 +219,59 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
printAllSubgroups(diagram.getRootGroup());
printEntities(getUnpackagedEntities());
for (Link link : diagram.getLinks()) {
if (link.isRemoved())
continue;
if (isOpalisable(link.getEntity1())) {
final SvekNode node = dotStringFactory.getBibliotekon().getNode(link.getEntity1());
final SvekNode other = dotStringFactory.getBibliotekon().getNode(link.getEntity2());
if (other != null) {
((EntityImageNote) node.getImage()).setOpaleLink(link, node, other, smetanaPathes);
link.setOpale(true);
}
} else if (isOpalisable(link.getEntity2())) {
final SvekNode node = dotStringFactory.getBibliotekon().getNode(link.getEntity2());
final SvekNode other = dotStringFactory.getBibliotekon().getNode(link.getEntity1());
if (other != null) {
((EntityImageNote) node.getImage()).setOpaleLink(link, node, other, smetanaPathes);
link.setOpale(true);
}
}
}
}
// Duplicate GeneralImageBuilder
private boolean isOpalisable(Entity entity) {
if (entity.isGroup())
return false;
if (entity.getLeafType() != LeafType.NOTE)
return false;
final Link single = onlyOneLink(entity);
if (single == null)
return false;
return single.getOther(entity).getLeafType() != LeafType.NOTE;
}
// Duplicate GeneralImageBuilder
private Link onlyOneLink(Entity ent) {
Link single = null;
for (Link link : diagram.getLinks()) {
if (link.isInvis())
continue;
if (link.contains(ent) == false)
continue;
if (single != null)
return null;
single = link;
}
return single;
}
private void drawGroup(UGraphic ug, YMirror ymirror, Entity group, ST_Agraph_s gr) {
@ -240,7 +307,6 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
g.muteToType(LeafType.EMPTY_PACKAGE);
printEntityNew(g);
} else {
printSingleGroup(g);
}
@ -307,7 +373,7 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
System.err.println("CANNOT FIND NODE");
return;
}
// System.err.println("exportEntity " + leaf);
System.err.println("exportEntity " + leaf);
final ST_Agnode_s agnode = agnode(zz, cluster, new CString(node.getUid()), true);
agsafeset(zz, agnode, new CString("shape"), new CString("box"), new CString(""));
final XDimension2D dim = getDim(node);
@ -337,19 +403,6 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
return result;
}
// private void printCluster(Globals zz, ST_Agraph_s g, Cluster cluster) {
// for (SvekNode node : cluster.getNodes()) {
// final ST_Agnode_s agnode = agnode(zz, g, new CString(node.getUid()), true);
// agsafeset(zz, agnode, new CString("shape"), new CString("box"), new CString(""));
// final String width = "" + (node.getWidth() / 72);
// final String height = "" + (node.getHeight() / 72);
// agsafeset(zz, agnode, new CString("width"), new CString(width), new CString(""));
// agsafeset(zz, agnode, new CString("height"), new CString(height), new CString(""));
// final Entity leaf = dotStringFactory.getBibliotekon().getLeaf(node);
// nodes.put(leaf, agnode);
// }
// }
private static final Lock lock = new ReentrantLock();
public ImageData createFile(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
@ -364,9 +417,6 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
@Override
public void createOneGraphic(UGraphic ug) {
for (Entity leaf : diagram.getEntityFactory().leafs())
printEntityNew(leaf);
final Globals zz = Globals.open();
try {
final TextBlock textBlock = getTextBlock(zz);
@ -381,9 +431,6 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
private ImageData createFileLocked(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException {
for (Entity leaf : diagram.getEntityFactory().leafs())
printEntityNew(leaf);
final Globals zz = Globals.open();
try {
final TextBlock drawable = getTextBlock(zz);
@ -544,7 +591,8 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
node2 = getAgnodeFromLeaf(link.getEntity2());
if (node1 == null || node2 == null)
throw new IllegalStateException();
return null;
// throw new IllegalStateException();
final ST_Agedge_s e = agedge(zz, g, node1, node2, null, true);
agsafeset(zz, e, new CString("arrowtail"), new CString("none"), new CString(""));
@ -591,17 +639,6 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
return strings;
}
private void printEntityNew(Entity ent) {
if (ent.isRemoved()) {
System.err.println("Jdot STRANGE: entity is removed");
return;
}
final IEntityImage image = printEntityInternal(ent);
final SvekNode shape = getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(),
stringBounder);
// dotStringFactory.addShape(shape);
}
private Bibliotekon getBibliotekon() {
return dotStringFactory.getBibliotekon();
}
@ -619,8 +656,11 @@ public class CucaDiagramFileMakerSmetana implements CucaDiagramFileMaker {
// skinParam = new SkinParamSameClassWidth(dotData.getSkinParam(), width);
}
return GeneralImageBuilder.createEntityImageBlock(ent, skinParam, diagram.isHideEmptyDescriptionForState(),
diagram, getBibliotekon(), null, diagram.getUmlDiagramType(), diagram.getLinks());
final IEntityImage result = GeneralImageBuilder.createEntityImageBlock(ent, skinParam,
diagram.isHideEmptyDescriptionForState(), diagram, getBibliotekon(), null,
diagram.getUmlDiagramType(), diagram.getLinks());
ent.setSvekImage(result);
return result;
}
return ent.getSvekImage();
}

View File

@ -101,7 +101,7 @@ public class SmetanaPath implements UDrawable {
} else if (this.link.getSpecificColor() != null)
color = this.link.getSpecificColor();
DotPath dotPath = getDotPath();
DotPath dotPath = getDotPathInternal();
if (ymirror != null && dotPath != null)
dotPath = ymirror.getMirrored(dotPath);
@ -142,15 +142,33 @@ public class SmetanaPath implements UDrawable {
.getMergedStyle(diagram.getSkinParam().getCurrentStyleBuilder());
}
public XPoint2D getStartPoint() {
final DotPath dotPath = getDotPathInternal();
XPoint2D pt = dotPath.getStartPoint();
if (ymirror != null)
pt = ymirror.getMirrored(pt);
return pt;
}
public XPoint2D getEndPoint() {
final DotPath dotPath = getDotPathInternal();
XPoint2D pt = dotPath.getEndPoint();
if (ymirror != null)
pt = ymirror.getMirrored(pt);
return pt;
}
private void printExtremityAtStart(UGraphic ug) {
final ExtremityFactory extremityFactory2 = link.getType().getDecor2()
.getExtremityFactoryComplete(diagram.getSkinParam().getBackgroundColor());
if (extremityFactory2 == null)
return;
DotPath s = getDotPath();
XPoint2D p0 = s.getStartPoint();
double startAngle = s.getStartAngle();
final DotPath dotPath = getDotPathInternal();
XPoint2D p0 = dotPath.getStartPoint();
double startAngle = dotPath.getStartAngle();
if (ymirror != null) {
p0 = ymirror.getMirrored(p0);
startAngle = -startAngle + Math.PI;
@ -172,9 +190,9 @@ public class SmetanaPath implements UDrawable {
if (extremityFactory1 == null)
return;
DotPath s = getDotPath();
XPoint2D p0 = s.getEndPoint();
double endAngle = s.getEndAngle();
final DotPath dotPath = getDotPathInternal();
XPoint2D p0 = dotPath.getEndPoint();
double endAngle = dotPath.getEndAngle();
if (ymirror != null) {
p0 = ymirror.getMirrored(p0);
endAngle = -endAngle;
@ -242,7 +260,7 @@ public class SmetanaPath implements UDrawable {
private DotPath dotPath;
private DotPath getDotPath() {
private DotPath getDotPathInternal() {
if (dotPath != null)
return dotPath;
@ -267,7 +285,8 @@ public class SmetanaPath implements UDrawable {
if (link.getEntity2().isGroup()) {
final Cluster cluster2 = bibliotekon.getCluster(link.getEntity2());
// System.err.println("WARNING: a group " + cluster2.getRectangleArea());
// dotPath = dotPath.simulateCompound(cluster2.getRectangleArea(), cluster2.getRectangleArea());
// dotPath = dotPath.simulateCompound(cluster2.getRectangleArea(),
// cluster2.getRectangleArea());
}
return dotPath;
}

View File

@ -40,8 +40,6 @@ import java.util.Objects;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.MagneticBorder;
import net.sourceforge.plantuml.klimt.geom.MagneticBorderNone;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.stereo.Stereotype;
import net.sourceforge.plantuml.style.ISkinParam;

View File

@ -57,6 +57,12 @@ public class Bibliotekon {
private final List<SvekLine> lines1 = new ArrayList<>();
private final List<SvekLine> allLines = new ArrayList<>();
private final Collection<Link> links;
public Bibliotekon(Collection<Link> links) {
this.links = links;
}
public SvekNode createNode(Entity ent, IEntityImage image, ColorSequence colorSequence,
StringBounder stringBounder) {
final SvekNode node = new SvekNode(ent, image, colorSequence, stringBounder);
@ -190,12 +196,13 @@ public class Bibliotekon {
}
public Entity getOnlyOther(Entity entity) {
for (SvekLine line : allLines) {
final Entity other = line.getOther(entity);
if (other != null)
return other;
for (Link link : links)
if (link.contains(entity)) {
final Entity other = link.getOther(entity);
if (other != null)
return other;
}
}
return null;
}

View File

@ -69,7 +69,7 @@ import net.sourceforge.plantuml.vizjs.GraphvizJsRuntimeException;
public class DotStringFactory implements Moveable {
private final Bibliotekon bibliotekon = new Bibliotekon();
private final Bibliotekon bibliotekon;
private final ColorSequence colorSequence;
private final Cluster root;
@ -92,6 +92,7 @@ public class DotStringFactory implements Moveable {
this.root = new Cluster(dotData.getEntityFactory().getDiagram(), colorSequence, skinParam,
dotData.getRootGroup());
this.current = root;
this.bibliotekon = new Bibliotekon(dotData.getLinks());
}
public DotStringFactory(StringBounder stringBounder, ICucaDiagram diagram) {
@ -103,6 +104,7 @@ public class DotStringFactory implements Moveable {
this.stringBounder = stringBounder;
this.root = new Cluster(diagram, colorSequence, skinParam, diagram.getEntityFactory().getRootGroup());
this.current = root;
this.bibliotekon = new Bibliotekon(diagram.getLinks());
}
public void addNode(SvekNode node) {

View File

@ -1084,12 +1084,12 @@ public class SvekLine implements Moveable, Hideable, GuideLine {
return new XPoint2D(dx + end.getX(), dy + end.getY());
}
public Entity getOther(Entity entity) {
if (link.contains(entity))
return link.getOther(entity);
return null;
}
// public Entity getOther(Entity entity) {
// if (link.contains(entity))
// return link.getOther(entity);
//
// return null;
// }
public StyleBuilder getCurrentStyleBuilder() {
return link.getStyleBuilder();

View File

@ -378,6 +378,11 @@ public class SvekNode implements Positionable, Hideable {
return shield().isZero() == false;
}
public void resetMoveSvek() {
this.minX = 0;
this.minY = 0;
}
public void moveSvek(double deltaX, double deltaY) {
this.minX += deltaX;
this.minY += deltaY;

View File

@ -40,6 +40,7 @@ import java.util.Map;
import java.util.Objects;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.cucadiagram.BodyFactory;
import net.sourceforge.plantuml.klimt.UGroupType;
import net.sourceforge.plantuml.klimt.UPath;
@ -61,6 +62,7 @@ import net.sourceforge.plantuml.klimt.geom.XPoint2D;
import net.sourceforge.plantuml.klimt.shape.DotPath;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlockEmpty;
import net.sourceforge.plantuml.sdot.SmetanaPath;
import net.sourceforge.plantuml.skin.ColorParam;
import net.sourceforge.plantuml.skin.CornerParam;
import net.sourceforge.plantuml.skin.SkinParamBackcolored;
@ -201,7 +203,37 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
ug.startUrl(url);
final UGraphic ug2 = UGraphicStencil.create(ug, this, UStroke.simple());
if (opaleLine == null || opaleLine.isOpale() == false) {
if (opaleLink != null) {
final StringBounder stringBounder = ug.getStringBounder();
final SmetanaPath smetanaEdged = smetanaPathes.get(opaleLink);
final UTranslate move = new UTranslate(-node.getMinX(), -node.getMinY());
final XPoint2D startPoint = move.getTranslated(smetanaEdged.getStartPoint());
final XPoint2D endPoint = move.getTranslated(smetanaEdged.getEndPoint());
final UTranslate force1 = getMagneticBorder().getForceAt(stringBounder, smetanaEdged.getStartPoint());
final UTranslate force2 = other.getMagneticBorder().getForceAt(stringBounder, smetanaEdged.getEndPoint());
final double textWidth = getTextWidth(stringBounder);
final double textHeight = getTextHeight(stringBounder);
final XPoint2D center = new XPoint2D(textWidth / 2, textHeight / 2);
XPoint2D pp1 = force2.getTranslated(startPoint);
XPoint2D pp2 = force1.getTranslated(endPoint);
if (pp1.distance(center) < pp2.distance(center)) {
pp1 = force1.getTranslated(endPoint);
pp2 = force2.getTranslated(startPoint);
}
final Direction strategy = getOpaleStrategy(textWidth, textHeight, pp2);
final Opale opale = new Opale(shadowing, borderColor, noteBackgroundColor, textBlock, true, getStroke());
opale.setRoundCorner(getRoundCorner());
opale.setOpale(strategy, pp2, pp1);
final UGraphic stroked = applyStroke(ug2);
opale.drawU(Colors.applyStroke(stroked, getEntity().getColors()));
} else if (opaleLine == null || opaleLine.isOpale() == false) {
drawNormal(ug2);
} else {
final StringBounder stringBounder = ug.getStringBounder();
@ -306,8 +338,10 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
}
private SvekLine opaleLine;
private Link opaleLink;
private SvekNode node;
private SvekNode other;
private Map<Link, SmetanaPath> smetanaPathes;
public void setOpaleLine(SvekLine line, SvekNode node, SvekNode other) {
this.opaleLine = line;
@ -315,6 +349,13 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
this.other = Objects.requireNonNull(other);
}
public void setOpaleLink(Link link, SvekNode node, SvekNode other, Map<Link, SmetanaPath> edges) {
this.opaleLink = link;
this.node = node;
this.other = Objects.requireNonNull(other);
this.smetanaPathes = edges;
}
public double getStartingX(StringBounder stringBounder, double y) {
return 0;
}

View File

@ -125,19 +125,24 @@ public class EntityImageTips extends AbstractEntityImage {
public void drawU(UGraphic ug) {
final StringBounder stringBounder = ug.getStringBounder();
final Entity other = bibliotekon.getOnlyOther(getEntity());
final Entity tmp = getEntity();
final Entity other = bibliotekon.getOnlyOther(tmp);
if (other == null) {
System.err.println("Error1 in EntityImageTips");
return;
}
final SvekNode nodeMe = bibliotekon.getNode(getEntity());
final SvekNode nodeOther = bibliotekon.getNode(other);
final XPoint2D positionMe = nodeMe.getPosition();
if (nodeOther == null) {
System.err.println("Error in EntityImageTips");
System.err.println("Error2 in EntityImageTips");
return;
}
final XPoint2D positionOther = nodeOther.getPosition();
bibliotekon.getNode(getEntity());
final Position position = getPosition();
Direction direction = position.reverseDirection();
final XPoint2D positionMe = nodeMe.getPosition();
double height = 0;
for (Map.Entry<String, Display> ent : getEntity().getTips().entrySet()) {
final Display display = ent.getValue();

View File

@ -82,7 +82,7 @@ public class Version {
}
public static int beta() {
final int beta = 5;
final int beta = 6;
return beta;
}