X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.modeling%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2FSCLScenegraph.java;h=40a5265852fa79083df1781db24a563df95d4532;hp=33d7f06290e109e69fdc4ceff7afb00c8b7cd00a;hb=2bd26538bb54ec285c01ba1a170f2486086c96a4;hpb=0ae2b770234dfc3cbb18bd38f324125cf0faca07 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 33d7f0629..40a526585 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java @@ -1,15 +1,52 @@ package org.simantics.modeling; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.geom.AffineTransform; +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.function.Function; +import java.util.stream.Collectors; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +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.datatypes.literal.GUID; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.request.IndexRoot; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.elements.DecorationSVGNode; 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.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; @@ -18,39 +55,96 @@ 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; +import org.simantics.scenegraph.g2d.G2DRenderingHints; import org.simantics.scenegraph.g2d.G2DSceneGraph; +import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.IG2DNodeVisitor; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.scenegraph.g2d.nodes.BackgroundNode; import org.simantics.scenegraph.g2d.nodes.BoundsNode; import org.simantics.scenegraph.g2d.nodes.ConnectionNode; import org.simantics.scenegraph.g2d.nodes.DataNode; -import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode; +import org.simantics.scenegraph.g2d.nodes.LinkNode; import org.simantics.scenegraph.g2d.nodes.NavigationNode; +import org.simantics.scenegraph.g2d.nodes.SVGNode; +import org.simantics.scenegraph.g2d.nodes.SelectionNode; 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.scl.runtime.tuple.Tuple2; import org.simantics.trend.impl.ItemNode; import org.simantics.utils.threads.ThreadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; public class SCLScenegraph { + + private static final Logger LOGGER = LoggerFactory.getLogger(SCLScenegraph.class); + private static final String MAIN_SECTION = "main"; + private static final String SELECTION_SECTION = "selection"; + private static final String SELECTION_MASK_SECTION = "selectionMask"; + + private static final String[] ALL_SECTIONS = { MAIN_SECTION, SELECTION_SECTION, SELECTION_MASK_SECTION }; + + // Changed from 0.001 to 0.0001 to prevent creation of huge BufferedImage's when + // generating PDF from SVG. If SVG contains any transparency then Batik uses + // bitmap-rendering which remarkably slows things down + // See org.apache.batik.gvt.AbstractGraphicsNode.paint(Graphics2D) where decisions are made + // if AlphaComposite should be painted + private static final String OPACITY = "0.0001"; + + @Deprecated public static ICanvasSceneGraphProvider getICanvasSceneGraphProvider(Resource model, Resource diagram, String diagramRVI) throws DatabaseException, InterruptedException { ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(model, diagram, diagramRVI); return provider; } + @Deprecated public static void disposeSceneGraphProvider(ICanvasSceneGraphProvider provider) { provider.dispose(); } - - //public static Resource getDiagramRuntime(Resource ) - - -// public static String getNodeTransform(ICanvasContext ctx, String name) throws DatabaseException, InterruptedException { -// return getNodeTransform(ctx, name); -// } - + + public static T doWithICanvasSceneGraphProvider(Resource diagram, Function1 func) throws DatabaseException { + return doWithICanvasSceneGraphProvider(diagram, (Function) provider -> func.apply(provider)); + } + + public static T doWithICanvasSceneGraphProvider(Resource diagram, Function func) throws DatabaseException { + Tuple2 result = Simantics.getSession().syncRequest(new ResourceRead(diagram) { + + @Override + public Tuple2 perform(ReadGraph graph) throws DatabaseException { + Resource indexRoot = graph.syncRequest(new IndexRoot(resource)); + String diagramRVI = Variables.getRVI(graph, resource); + return new Tuple2(indexRoot, diagramRVI); + } + }); + ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider((Resource) result.c0, diagram, (String) result.c1); + try { + return func.apply(provider); + } finally { + provider.dispose(); + } + } + + public static T doWithCanvasContext(Resource diagram, Function1 func) throws DatabaseException { + return doWithCanvasContext(diagram, (Function) canvasContext -> func.apply(canvasContext)); + } + + public static T doWithCanvasContext(Resource diagram, Function func) throws DatabaseException { + return doWithICanvasSceneGraphProvider(diagram, (Function) provider -> func.apply(provider.getCanvasContext())); + } + public static String getNodeTransform(ICanvasContext ctx, String name) { Set texts = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class); @@ -192,6 +286,23 @@ public class SCLScenegraph { } return "No nodes in scenegraph!"; } + + /** + * Returns background color of a canvasContext or null. + * @param ctx + * @return color in RGBA List format, or null. + */ + public static List getBackgroundColor(ICanvasContext ctx) { + Color color = ctx.getDefaultHintContext().getHint(Hints.KEY_BACKGROUND_COLOR); + if (color == null) + return null; + ArrayList rgba = new ArrayList<>(4); + rgba.add(color.getRed()); + rgba.add(color.getGreen()); + rgba.add(color.getBlue()); + rgba.add(color.getAlpha()); + return rgba; + } public static String sceneGraphTest (ICanvasContext ctx, String module, String value) { @@ -234,7 +345,7 @@ public class SCLScenegraph { } } - public static boolean copyPaste (final ICanvasContext source_ctx, final ICanvasContext target_ctx, List modules) throws DatabaseException { + public static boolean copyPaste (final ICanvasContext source_ctx, final ICanvasContext target_ctx, List modules) throws DatabaseException { IDiagram idiagram = source_ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); @@ -286,5 +397,604 @@ public class SCLScenegraph { return true; } + static class Generator extends SVGGraphics2D { + + int elemLevel = 0; + String newElementId = null; + ArrayList elements = new ArrayList(); + + public static final String svgNS = "http://www.w3.org/2000/svg"; + + public Generator(SVGGeneratorContext ctx, boolean textAsShapes) { + super(ctx, textAsShapes); + } + + public Generator(Document document) { + super(document); + // prevent batik comment in each g-element + getGeneratorContext().setComment(null); + } + + @Override + public Element getRoot() { + Element root = super.getRoot(); + for(Element e : elements) { + root.appendChild(e); + } + return root; + } + + @Override + public void setRenderingHint(Key arg0, Object arg1) { + if(G2DRenderingHints.KEY_BEGIN_ELEMENT == arg0) { + elemLevel++; + } + if(G2DRenderingHints.KEY_ELEMENT_ID == arg0) { + if(arg1 != null) + newElementId = arg1.toString(); + else + newElementId = UUID.randomUUID().toString(); + } + if(G2DRenderingHints.KEY_END_ELEMENT == arg0) { + elemLevel--; + if(elemLevel == 0) { + Element group = getDOMFactory().createElement(SVG_G_TAG); + //Element group = getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG); + group.setAttributeNS(null, "id", newElementId); + group.setAttributeNS(null, "class", arg1.toString()); + getRoot(group); + elements.add(group); + } + } + super.setRenderingHint(arg0, arg1); + } + + } + + public static Element renderSVGNode(SVGGraphics2D svgGenerator, IG2DNode node) { + + try { + + svgGenerator.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + svgGenerator.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + svgGenerator.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + node.render(svgGenerator); + + } catch (Throwable t) { + LOGGER.error("Problems rendering scene graph to SVG", t); + } + + return svgGenerator.getRoot(); + } + + public static String printSVGDocument(Element doce) { + + StringBuilder result = new StringBuilder(); + + NodeList nl = doce.getChildNodes(); + + for(int i=0;i 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, double width, double height) { + return renderSVG0(width, height, ctx, mapper); + } + + public static String renderSVG(ICanvasContext ctx) { + return renderSVG(ctx, -1, -1); + } + + public static String renderSVGMapIdentifiers(ICanvasContext ctx) { + return renderSVGMapIdentifiers(ctx, -1, -1); + } + + /** + * Renders ICanvasContext into SVG by mapping the SVG id's into URI based + * GUID's + * + * @param ctx + * @return + */ + public static String renderSVGMapIdentifiers(ICanvasContext ctx, double width, double height) { + return renderSVG0(width, height, 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.Element)) { + Resource component = graph.getPossibleObject(element, MOD.ElementToComponent); + if (component != null) { + GUID identifier = graph.getPossibleRelatedValue(component, L0.identifier, GUID.BINDING); + if (identifier != null) { + return Arrays.toString(createURIBasedL0Identifier(graph, component)); + } else { + LOGGER.error("Component {} does not have GUID identifier!", component); + } + } else 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 { + // Ok, lets create a hashcode or either return empty string in cases where no ID is required + if (graph.hasStatement(element, L0.HasName)) + return graph.getURI(element).hashCode(); + return ""; + } + } + } else { + LOGGER.error("Parameter p {} is not resource but it is {}", p, p.getClass()); + } + return p; + } catch (DatabaseException e) { + throw new RuntimeDatabaseException(e); + } + })); + } + }); + } catch (DatabaseException e) { + LOGGER.error("Could not apply mappings", e); + throw new RuntimeDatabaseException(e); + } + } + }); + } + + static class RenderSVGContext { + + Map documents = new HashMap<>(); + + public void append(String[] keys, String svgText) { + for(String key : keys) append(key, svgText); + } + + public void append(String key, String svgText) { + StringBuilder builder = documents.get(key); + if(builder == null) { + builder = new StringBuilder(); + documents.put(key, builder); + } + builder.append(svgText); + } + + public void append(RenderSVGContext other) { + for(String key : other.documents.keySet()) { + append(key, other.get(key)); + } + } + + public String get(String key) { + StringBuilder builder = documents.get(key); + if(builder == null) return ""; + else return builder.toString(); + } + + } + + private static String renderSVG0(double width, double height, ICanvasContext ctx, Function1, Map> mappingFunction) { + + // Get a DOMImplementation. + DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); + + // Create an instance of org.w3c.dom.Document. + String svgNS = "http://www.w3.org/2000/svg"; + Document document = domImpl.createDocument(svgNS, "svg", null); + + // Create an instance of the SVG Generator. + SVGGraphics2D svgGenerator = new Generator(document); + + RenderSVGContext result = new RenderSVGContext(); + + double[] matrix = new double[6]; + try { + + Selection selection = ctx.getAtMostOneItemOfClass(Selection.class); + if (selection != null) { + // This prevents workbench selection from being left over. + // Also prevents scene graph crap from being left on the screen. + IDiagram d = ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); + selection.setSelection(0, d.getElements()); + } + + G2DSceneGraph sg = ctx.getSceneGraph(); + sg.performCleanup(); + G2DParentNode root = (G2DParentNode) sg.getRootNode(); + + // rtree is the actual content of the diagram + RTreeNode rtree = NodeUtil.getNearestChildByClass(root, RTreeNode.class); + Rectangle2D rtreeBounds = NodeUtil.getLocalBounds(rtree); + + // get the bounds of the content + Rectangle2D content = rtreeBounds; + + + if (content != null) { + // To account for dynamic padding of selection rectangles (5 units + stroke width) + int offset = 6; + + double scale = width < 0 || height < 0 ? 1.0 : Math.min((width - 2*offset) / content.getWidth(), (height - 2*offset) / content.getHeight()); + + AffineTransform tr = new AffineTransform(); + tr.translate(offset, offset); + tr.scale(scale, scale); + tr.translate(-content.getX(), -content.getY()); + tr.getMatrix(matrix); + svgGenerator.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); + } else { + svgGenerator.setSVGCanvasSize(new Dimension(100, 100)); + } + //svgGenerator.translate(offset, offset); + //svgGenerator.scale(scale, scale); + // translate svgGenerator to the x and y coordinates of current content + //svgGenerator.translate(-content.getX(), -content.getY()); + + + //svgGenerator.setClip(content); + + result.append(MAIN_SECTION, ""); + result.append(SELECTION_SECTION, ""); + result.append(SELECTION_MASK_SECTION, ""); + + KeyVisitor keyVisitor = new KeyVisitor(); + sg.accept(keyVisitor); + + Set keys = keyVisitor.getKeys(); + + Map mappings = mappingFunction.apply(keys); + + IG2DNodeVisitor visitor = new PrintingVisitor(svgGenerator, result, mappings); + + sg.accept(visitor); + + } catch (Throwable t) { + LOGGER.error("Problems rendering canvas context to SVG", t); + } + + + result.append(ALL_SECTIONS, ""); + + StringBuilder res = new StringBuilder(); + res.append(""); + res.append(""); + res.append(result.get(MAIN_SECTION)); + res.append(result.get(SELECTION_SECTION)); + res.append(result.get(SELECTION_MASK_SECTION)); + res.append(""); + res.append(""); + +// System.err.println(" == FINAL RESULT == "); +// System.err.println(res.toString()); + + return res.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 RenderSVGContext result; + private SVGGraphics2D svgGenerator; + + private Map mappings; + + public PrintingVisitor(SVGGraphics2D svgGenerator, RenderSVGContext result, Map mappings) { + this.result = result; + this.mappings = mappings; + this.svgGenerator = svgGenerator; + } + + private String getKey(SingleElementNode node) { + String key; + if(node.getKey() != null) { + if (mappings.containsKey(node.getKey())) + key = mappings.get(node.getKey()).toString(); + else + key = node.getKey().toString(); + } else { + key = Long.toString(node.getId()); + } + return key; + } + + @Override + public void enter(IG2DNode node) { + + RenderSVGContext 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(MAIN_SECTION, "\n"); + parentBuilder.append(SELECTION_SECTION, "\n"); + parentBuilder.append(SELECTION_MASK_SECTION, "\n"); + + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, svg); + + for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { + n.setIgnoreSelection(false); + } + + doc = renderSVGNode(svgGenerator, (IG2DNode)node); + svg = printSVGDocument(doc); + parentBuilder.append(SELECTION_SECTION, svg); + + BasicStroke bs = new BasicStroke(10f); + + for(RouteGraphNode n : NodeUtil.collectNodes(node, RouteGraphNode.class)) { + n.setDynamicStroke(bs); + } + + doc = renderSVGNode(svgGenerator, (IG2DNode)node); + svg = printSVGDocument(doc); + parentBuilder.append(SELECTION_MASK_SECTION, svg); + + parentBuilder.append(SELECTION_MASK_SECTION, "\n"); + parentBuilder.append(SELECTION_SECTION, "\n"); + parentBuilder.append(MAIN_SECTION, "\n"); + + } else if (isSelection0(node)) { + + SelectionNode n = (SelectionNode)node; + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null) { + + RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN); + + String key = getKey(parentSEN); + n.setIgnore(false); + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + n.setIgnore(true); + String svg = printSVGDocument(doc); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_SECTION, svg); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); + Rectangle2D rect = n.getRect(); + // NaN + if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { + parentBuilder2.append(SELECTION_MASK_SECTION,""); + } + parentBuilder2.append(SELECTION_MASK_SECTION,"\n"); + + } + } else if (node instanceof SelectionNode) { + + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, ""); + parentBuilder.append(MAIN_SECTION, svg); + parentBuilder.append(MAIN_SECTION, "\n"); + + } else if (node instanceof SVGNode) { + SVGNode svg = (SVGNode)node; + AffineTransform at = svg.getTransform(); + 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(MAIN_SECTION, "\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(MAIN_SECTION, "\n"); + } + } + parentBuilder.append(MAIN_SECTION, svg.getSVGText()); + if (!at.isIdentity()) { + parentBuilder.append(MAIN_SECTION, "\n"); + } + } else if (node instanceof G2DParentNode) { + AffineTransform at = node.getTransform(); + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; + String key = getKey(sen); + String typeClass = sen.getTypeClass(); + String clazz = "definedElement"; + if(typeClass != null && !typeClass.isEmpty()) + clazz = clazz + " " + typeClass; + + parentBuilder.append(MAIN_SECTION, "\n"); + senBuilders.put(sen, new RenderSVGContext()); + } + 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(ALL_SECTIONS, "\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(ALL_SECTIONS, "\n"); + } + } + } else if (!(node instanceof RouteGraphNode) && !(node instanceof LinkNode)){ + try { + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + if (doc.getElementsByTagName("g").getLength() == 0) + return; + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, ""); + parentBuilder.append(MAIN_SECTION, svg); + parentBuilder.append(MAIN_SECTION, "\n"); + } catch (Exception e) { + // TODO: There are nodes that do not behave well when rendered to SVG. For backwards compatibility, we don't handle the exceptions. + } + } + + //enters.put(node, b.length()); + + } + + private boolean isSelection0(IG2DNode node) { + + if(node instanceof SelectionNode) { + SelectionNode sn = (SelectionNode)node; + return sn.getSelectionId() == 0; + } else { + return false; + } + + } + + private RenderSVGContext getParentBuilder(IG2DNode node) { + + INode parentSEN = NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN instanceof G2DSceneGraph) return result; + + RenderSVGContext 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) { + + RenderSVGContext parentBuilder = getParentBuilder(node); + + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; + RenderSVGContext b = senBuilders.get(sen); + String content = b.get(MAIN_SECTION); + if(content.isEmpty()) { + if(sen.getKey() != null) { + + for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) { + n.setIgnore(true); + } + + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, svg); + } + } else { + parentBuilder.append(b); + } + } + + AffineTransform at = node.getTransform(); + if(!at.isIdentity()) { + parentBuilder.append(ALL_SECTIONS, ""); + } + if(node instanceof SingleElementNode) { + SingleElementNode sen = (SingleElementNode)node; + //if(sen.getKey() != null) { + parentBuilder.append(MAIN_SECTION, ""); + //} + } + } + indent --; + } + } } \ No newline at end of file