package org.simantics.modeling; import java.awt.BasicStroke; 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.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DataElementMap; 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.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 }; @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 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); for (TextNode text : texts) { String nodeName = NodeUtil.getNodeName(text); if (nodeName.equals(name)) { String transform = text.getTransform().toString(); return transform; } } return "No node found"; } public static String getNodeText(ICanvasContext ctx, String name) { Set texts = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class); for (TextNode text : texts) { String nodeName = NodeUtil.getNodeName(text); if (nodeName.equals(name)) { String texti = text.getText(); return texti; } } return "No node found"; } public static String getNodeCount(ICanvasContext ctx) { G2DSceneGraph g2 = ctx.getSceneGraph(); int amount = NodeUtil.countTreeNodes(g2); return "Node count: " + amount; } public static String getAllNodes (ICanvasContext ctx) { Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), G2DSceneGraph.class); int amount = g2.size() +1; return "All nodes: " + amount; } public static String getBoundsNodes (ICanvasContext ctx) { Set bn = NodeUtil.collectNodes(ctx.getSceneGraph(), BoundsNode.class); int amount = bn.size(); return "BoundsNodes: " + amount; } public static String getBackgroundNodes (ICanvasContext ctx) { Set bg = NodeUtil.collectNodes(ctx.getSceneGraph(), BackgroundNode.class); int amount = bg.size(); return "BackgroundNodes: " + amount; } public static String getDataNodes (ICanvasContext ctx) { Set dn = NodeUtil.collectNodes(ctx.getSceneGraph(), DataNode.class); int amount = dn.size(); return "DataNodes: " + amount; } public static String getNavigationNodes (ICanvasContext ctx) { Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), NavigationNode.class); int amount = g2.size(); return "NavigationNodes: " + amount; } public static String getParentNodes (ICanvasContext ctx) { Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), G2DParentNode.class); int amount = g2.size(); return "ParentNodes: " + amount; } public static String getDecorationNodes (ICanvasContext ctx) { Set deco = NodeUtil.collectNodes(ctx.getSceneGraph(), DecorationSVGNode.class); int amount = deco.size(); return "DecorationNodes: " + amount; } public static String getSingleElementNodes (ICanvasContext ctx) { Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), SingleElementNode.class); int amount = g2.size(); return "SingleElementNodes: " + amount; } public static String getConnectionNodes (ICanvasContext ctx) { Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), ConnectionNode.class); int amount = g2.size(); return "ConnectionNodes: " + amount; } public static String getTextNodes (ICanvasContext ctx) { Set tn = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class); Set tgn = NodeUtil.collectNodes(ctx.getSceneGraph(), TextGridNode.class); int amount = tn.size() + tgn.size(); return "TextNodes: " + amount; } public static String getItemNodes (ICanvasContext ctx) { Set item = NodeUtil.collectNodes(ctx.getSceneGraph(), ItemNode.class); int amount = item.size(); return "ItemNodes: " + amount; } public static String editNodeText (ICanvasContext ctx, String module, String previous_value, String new_value) { Set textGridNodes = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class); for (TextNode modulenode : textGridNodes) { if (module.equals(modulenode.getText())) { //System.out.println("Module what we were looking for: " + module); //System.out.println("Modulenode: " + modulenode.getText()); ParentNode parentnode = modulenode.getParent(); //System.out.println("Parentnode: " + parentnode); Collection textnodes = (Collection) parentnode.getNodes(); for (TextNode valuenode : textnodes) { if (previous_value.equals(valuenode.getText())) { //System.out.println("Value what we were looking for: " + previous_value); //System.out.println("Valuenode: " + valuenode.getText()); //valuenode.setEditMode(true); valuenode.activateEdit(0, null, ctx); valuenode.setText(new_value); valuenode.fireTextEditingEnded(); //System.out.println("valuenode modified: " + valuenode); return "Modified module " + module + " with value " + new_value; } } return "Not found module : " + module; } } return "No nodes in scenegraph!"; } public static String sceneGraphTest (ICanvasContext ctx, String module, String value) { boolean module_founded = false; boolean value_founded = false; Set g2 = NodeUtil.collectNodes(ctx.getSceneGraph(), G2DSceneGraph.class); System.out.println("Total amount of nodes: " + g2.size() + 1); Set grid = NodeUtil.collectNodes(ctx.getSceneGraph(), TextGridNode.class); Integer textGridNodeAmount = grid.size(); System.out.println("Amount of TextGridNodes " + textGridNodeAmount); Set texts = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class); Integer textNodeAmount = grid.size(); System.out.println("Amount of TextNodes " + textNodeAmount); for (TextNode node : texts) { if (module.equals(node.getText())) { module_founded = true; System.out.println("Correct module " + module + " founded."); } if (value.equals(node.getText())) { value_founded = true; System.out.println("Correct value " + value + " founded."); } } if (value_founded == true && module_founded == true) { return "Found both correct module " + module + " and value " + value; } if (value_founded == false && module_founded == true) { return "Found only correct module " + module + " but not value " + value; } if (value_founded == true && module_founded == false) { return "Found only correct value " + value + " but not module " + module; } else { return "Didn't found either module " + module + " or value " + value; } } 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); DataElementMap dem = idiagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class); if (dem != null) { final Collection newSelection = new ArrayList(); for (Resource module : modules) { IElement element = dem.getElement(idiagram, module); if (element != null) { newSelection.add(element); } else { throw new DatabaseException("Could not find IElement for " + element); } } ThreadUtils.syncExec(source_ctx.getThreadAccess(), new Runnable() { @Override public void run() { if (source_ctx.isDisposed()) return; Selection selection = source_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. selection.setSelection(0, newSelection); } CanvasUtils.sendCommand(source_ctx, Commands.COPY); CanvasUtils.sendCommand(target_ctx, Commands.PASTE); } }); //} while(source_ctx.getEventQueue().size() > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { throw new DatabaseException(e); } } ThreadUtils.syncExec(source_ctx.getThreadAccess(), new Runnable() { @Override public void run() { } }); } 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; // 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.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.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); //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 (node instanceof SelectionNode) { 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 SVGNode) { SVGNode svg = (SVGNode)node; parentBuilder.append(MAIN_SECTION, svg.getSVGText()); } 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"); } } } //enters.put(node, b.length()); } 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 --; } } }