From: jsimomaa Date: Thu, 29 Jun 2017 11:06:32 +0000 (+0300) Subject: Diagram to SVG enhancements and stabilization X-Git-Tag: v1.31.0~283 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=241a1dddfbaa9c46832f443884ca1bbfd1beb2c9;p=simantics%2Fplatform.git Diagram to SVG enhancements and stabilization * Introducing the possibility to map component identifiers from default to something else in order to stabilize SVG printing of same diagrams in different databases refs #7340 Change-Id: I2338703a4b0e04fe1680854f749c9e8a28db134e --- diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java index 5dde0b081..bfab7fc6c 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java @@ -9,11 +9,16 @@ import java.awt.geom.Rectangle2D; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; @@ -24,11 +29,16 @@ import javax.xml.transform.stream.StreamResult; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGeneratorContext; import org.apache.batik.svggen.SVGGraphics2D; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.diagram.elements.DiagramNodeUtil; import org.simantics.diagram.elements.TextGridNode; import org.simantics.diagram.elements.TextNode; +import org.simantics.diagram.stubs.DiagramResource; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; @@ -37,6 +47,7 @@ import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.IElement; import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; import org.simantics.g2d.utils.CanvasUtils; +import org.simantics.layer0.Layer0; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.ParentNode; import org.simantics.scenegraph.g2d.G2DParentNode; @@ -57,6 +68,7 @@ import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.scl.runtime.function.Function1; import org.simantics.trend.impl.ItemNode; import org.simantics.utils.threads.ThreadUtils; import org.slf4j.Logger; @@ -435,7 +447,97 @@ public class SCLScenegraph { } - public static String renderSVG(ICanvasContext ctx) { + public static String renderSVG3(ICanvasContext ctx) { + return renderSVG0(ctx, p0 -> p0.stream().collect(Collectors.toMap(p1 -> p1, p2 -> p2))); + } + + /** + * @param graph + * @param component + * @throws DatabaseException + */ + private static Object[] createURIBasedL0Identifier(ReadGraph graph, Resource component) throws DatabaseException { + String uri = graph.getPossibleURI(component); + int hashCode = uri.hashCode(); + Random random = new Random(hashCode); + long l1 = random.nextLong(); + long l2 = random.nextLong(); + return new Object[] { l1, l2 }; + } + + /** + * Default no-op mapper + */ + private static final Function1, Map> mapper = p0 -> p0.stream().collect(Collectors.toMap(p1 -> p1, p2 -> p2)); + + public static String renderSVG(ICanvasContext ctx) { + return renderSVG0(ctx, mapper); + } + + /** + * Renders ICanvasContext into SVG by mapping the SVG id's into URI based + * GUID's + * + * @param ctx + * @return + */ + public static String renderSVGMapIdentifiers(ICanvasContext ctx) { + return renderSVG0(ctx, new Function1, Map>() { + + @Override + public Map apply(Set p0) { + try { + return Simantics.getSession().syncRequest(new UnaryRead, Map>(p0) { + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + DiagramResource DIA = DiagramResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + return parameter.stream().collect(Collectors.toMap(p -> p, p -> { + try { + if (p instanceof Resource) { + Resource element = (Resource) p; + if (graph.isInstanceOf(element, DIA.Connection) || graph.isInstanceOf(element, DIA.Terminal)) { + // Ok, lets create a hashcode for connections and terminals as they do not have identifiers + return graph.getURI(element).hashCode(); + } else if (graph.isInstanceOf(element, DIA.Element)) { + Resource component = graph.getPossibleObject(element, MOD.ElementToComponent); + if (component != null) { + Object relatedValue = graph.getPossibleRelatedValue(component, L0.identifier); + if (relatedValue != null) { + if (relatedValue instanceof Object[]) { + // OK, found guid, lets generate new based on URI + return Arrays.toString(createURIBasedL0Identifier(graph, component)); + } else { + System.err.println("Component " + component + " identifeir is not normal: " + relatedValue); + } + } else { + System.err.println("Compoennt " + component + " dooes not have identifier"); + } + } else { + System.err.println("Element " + element + " does not have component"); + } + } + } else { + System.err.println("p is not resource but is " + p); + } + return p; + } catch (DatabaseException e) { + throw new RuntimeDatabaseException(e); + } + })); + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not apply mappings", e); + throw new RuntimeDatabaseException(e); + } + } + }); + } + + private static String renderSVG0(ICanvasContext ctx, Function1, Map> mappingFunction) { // Get a DOMImplementation. DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); @@ -495,166 +597,14 @@ public class SCLScenegraph { result.append(""); - IG2DNodeVisitor visitor = new IG2DNodeVisitor() { - - int indent = 0; - - HashMap senBuilders = new HashMap<>(); - - @Override - public void enter(IG2DNode node) { - - StringBuilder parentBuilder = getParentBuilder(node); - - indent++; - if(node instanceof ConnectionNode) { - - for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { - n.setIgnoreSelection(true); - } - - String key = ((ConnectionNode)node).getKey().toString(); - - parentBuilder.append("\n"); - Element doc = renderSVGNode((IG2DNode)node); - String svg = printSVGDocument(doc); - parentBuilder.append(svg); - - for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { - n.setIgnoreSelection(false); - } - - parentBuilder.append("\n"); - doc = renderSVGNode((IG2DNode)node); - svg = printSVGDocument(doc); - parentBuilder.append(svg); - parentBuilder.append("\n"); - - BasicStroke bs = new BasicStroke(5f); - - for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { - n.setDynamicStroke(bs); - } - - parentBuilder.append("\n"); - doc = renderSVGNode((IG2DNode)node); - svg = printSVGDocument(doc); - parentBuilder.append(svg); - parentBuilder.append("\n"); - - parentBuilder.append("\n"); - - } else if (node instanceof SelectionNode) { - - SelectionNode n = (SelectionNode)node; - SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); - if(parentSEN != null && parentSEN.getKey() != null) { - - StringBuilder parentBuilder2 = getParentBuilder(parentSEN); - - String key = parentSEN.getKey().toString(); - Element doc = renderSVGNode((IG2DNode)node); - String svg = printSVGDocument(doc); - parentBuilder2.append("\n"); - parentBuilder2.append(svg); - parentBuilder2.append("\n"); - parentBuilder2.append("\n"); - Rectangle2D rect = n.getRect(); - parentBuilder2.append(""); - parentBuilder2.append("\n"); - } - } else if (node instanceof SVGNode) { - SVGNode svg = (SVGNode)node; - parentBuilder.append(svg.getSVGText()); - } else if (node instanceof G2DParentNode) { - AffineTransform at = node.getTransform(); - if(node instanceof SingleElementNode) { - SingleElementNode sen = (SingleElementNode)node; - if(sen.getKey() != null) { - String key = sen.getKey().toString(); - parentBuilder.append("\n"); - } - senBuilders.put(sen, new StringBuilder()); - } - if(!at.isIdentity()) { - if(at.getScaleX() == 1.0 && at.getScaleY() == 1.0 && at.getShearX() == 0.0 && at.getShearY() == 0.0) { - String m = "translate(" + at.getTranslateX() + " " + at.getTranslateY() + ")"; - parentBuilder.append("\n"); - } else { - double[] ds = new double[6]; - at.getMatrix(ds); - String m = "matrix(" + ds[0] + " " + ds[1] + " " + ds[2] + " " + ds[3] + " " + ds[4] + " " + ds[5] + ")"; - parentBuilder.append("\n"); - } - } - } - - //enters.put(node, b.length()); - - } - - private StringBuilder getParentBuilder(IG2DNode node) { - - INode parentSEN = NodeUtil.getNearestParentOfType(node, SingleElementNode.class); - if(parentSEN instanceof G2DSceneGraph) return result; - - StringBuilder parentBuilder = senBuilders.get(parentSEN); - if(parentBuilder == null) return result; - - return parentBuilder; - - } - - @Override - public void leave(IG2DNode node) { - - if(node instanceof ConnectionNode || node instanceof SVGNode) { - // We are done - } else if (node instanceof G2DParentNode) { - - StringBuilder parentBuilder = getParentBuilder(node); - - if(node instanceof SingleElementNode) { - SingleElementNode sen = (SingleElementNode)node; -// if(sen.getKey() != null) { - StringBuilder b = senBuilders.get(sen); - String content = b.toString(); - if(content.isEmpty()) { - if(sen.getKey() != null) { - - for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) { - n.setIgnore(true); - } - - Element doc = renderSVGNode((IG2DNode)node); - String svg = printSVGDocument(doc); - parentBuilder.append(svg); - } - } else { - parentBuilder.append(content); - } -// } - } - - - AffineTransform at = node.getTransform(); - if(!at.isIdentity()) { - parentBuilder.append(""); - } - if(node instanceof SingleElementNode) { - SingleElementNode sen = (SingleElementNode)node; - if(sen.getKey() != null) { - parentBuilder.append(""); - } - } - } - indent --; - } + KeyVisitor keyVisitor = new KeyVisitor(); + sg.accept(keyVisitor); + + Set keys = keyVisitor.getKeys(); + + Map mappings = mappingFunction.apply(keys); - }; + IG2DNodeVisitor visitor = new PrintingVisitor(result, mappings); sg.accept(visitor); } catch (Throwable t) { @@ -665,8 +615,208 @@ public class SCLScenegraph { //System.err.println(" == FINAL RESULT == "); //System.err.println(b); return result.toString(); + } + + + + private static class KeyVisitor implements IG2DNodeVisitor { + + private Set keys = new HashSet<>(); + @Override + public void enter(IG2DNode node) { + if (node instanceof SingleElementNode) { + Object key = ((SingleElementNode) node).getKey(); + if (key != null) { + keys.add(key); + } + } + } + + @Override + public void leave(IG2DNode node) { + // Nothing to do + } + + public Set getKeys() { + return keys; + } } + private static class PrintingVisitor implements IG2DNodeVisitor { + + int indent = 0; + + HashMap senBuilders = new HashMap<>(); + + private StringBuilder result; + + private Map mappings; + + public PrintingVisitor(StringBuilder result, Map mappings) { + this.result = result; + this.mappings = mappings; + } + + private String getKey(SingleElementNode node) { + String key; + if (mappings.containsKey(node.getKey())) + key = mappings.get(node.getKey()).toString(); + else + key = node.getKey().toString(); + return key; + } + + @Override + public void enter(IG2DNode node) { + + StringBuilder parentBuilder = getParentBuilder(node); + + indent++; + if(node instanceof ConnectionNode) { + + for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { + n.setIgnoreSelection(true); + } + + String key = getKey((ConnectionNode) node); + parentBuilder.append("\n"); + Element doc = renderSVGNode((IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(svg); + + for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { + n.setIgnoreSelection(false); + } + + parentBuilder.append("\n"); + doc = renderSVGNode((IG2DNode)node); + svg = printSVGDocument(doc); + parentBuilder.append(svg); + parentBuilder.append("\n"); + + BasicStroke bs = new BasicStroke(5f); + + for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { + n.setDynamicStroke(bs); + } + + parentBuilder.append("\n"); + doc = renderSVGNode((IG2DNode)node); + svg = printSVGDocument(doc); + parentBuilder.append(svg); + parentBuilder.append("\n"); + + parentBuilder.append("\n"); + + } else if (node instanceof SelectionNode) { + + SelectionNode n = (SelectionNode)node; + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null && parentSEN.getKey() != null) { + + StringBuilder parentBuilder2 = getParentBuilder(parentSEN); + + String key = getKey(parentSEN); + Element doc = renderSVGNode((IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder2.append("\n"); + parentBuilder2.append(svg); + parentBuilder2.append("\n"); + parentBuilder2.append("\n"); + Rectangle2D rect = n.getRect(); + parentBuilder2.append(""); + parentBuilder2.append("\n"); + } + } else if (node instanceof SVGNode) { + SVGNode svg = (SVGNode)node; + parentBuilder.append(svg.getSVGText()); + } else if (node instanceof G2DParentNode) { + AffineTransform at = node.getTransform(); + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; + if(sen.getKey() != null) { + String key = getKey(sen); + parentBuilder.append("\n"); + } + senBuilders.put(sen, new StringBuilder()); + } + if(!at.isIdentity()) { + if(at.getScaleX() == 1.0 && at.getScaleY() == 1.0 && at.getShearX() == 0.0 && at.getShearY() == 0.0) { + String m = "translate(" + at.getTranslateX() + " " + at.getTranslateY() + ")"; + parentBuilder.append("\n"); + } else { + double[] ds = new double[6]; + at.getMatrix(ds); + String m = "matrix(" + ds[0] + " " + ds[1] + " " + ds[2] + " " + ds[3] + " " + ds[4] + " " + ds[5] + ")"; + parentBuilder.append("\n"); + } + } + } + + //enters.put(node, b.length()); + + } + + private StringBuilder getParentBuilder(IG2DNode node) { + + INode parentSEN = NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN instanceof G2DSceneGraph) return result; + + StringBuilder parentBuilder = senBuilders.get(parentSEN); + if(parentBuilder == null) return result; + + return parentBuilder; + + } + @Override + public void leave(IG2DNode node) { + + if(node instanceof ConnectionNode || node instanceof SVGNode) { + // We are done + } else if (node instanceof G2DParentNode) { + + StringBuilder parentBuilder = getParentBuilder(node); + + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; +// if(sen.getKey() != null) { + StringBuilder b = senBuilders.get(sen); + String content = b.toString(); + if(content.isEmpty()) { + if(sen.getKey() != null) { + + for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) { + n.setIgnore(true); + } + + Element doc = renderSVGNode((IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(svg); + } + } else { + parentBuilder.append(content); + } +// } + } + + + AffineTransform at = node.getTransform(); + if(!at.isIdentity()) { + parentBuilder.append(""); + } + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; + if(sen.getKey() != null) { + parentBuilder.append(""); + } + } + } + indent --; + } + } } \ No newline at end of file