]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Support SVG generation from scenegraph. 37/337/2 feature/svgdiagram
authorAntti Villberg <antti.villberg@semantum.fi>
Mon, 27 Feb 2017 05:50:14 +0000 (07:50 +0200)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 7 Mar 2017 16:08:20 +0000 (18:08 +0200)
refs #7054

Change-Id: Id7543adbb65f938e4478c1a095579407dfe02c9a

bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java
bundles/org.simantics.modeling/META-INF/MANIFEST.MF
bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl
bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DHints.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/ConnectionNode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/UnboundedNode.java

index 1eb15f61c2132ece167643ee6af6638ef4cb6e9b..674f1c45739f87aae1b804d791e5353e1142a4d9 100644 (file)
@@ -557,6 +557,7 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos
             ConnectionNode holder = e.getHint(sgKey);
             if (holder == null) {
                 holder = parentNode.addNode(ElementUtils.generateNodeId(e), ConnectionNode.class);
+                holder.setKey(e.getHint(ElementHints.KEY_OBJECT));
                 holder.setTransferableProvider(new ElementTransferableProvider(getContext(), e));
                 e.setHint(sgKey, holder);
                 holder.setZIndex(parentNode.getNodeCount() + 1);
@@ -566,7 +567,9 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos
 
             SingleElementNode holder = e.getHint(sgKey);
             if (holder == null) {
-                holder = parentNode.addNode(ElementUtils.generateNodeId(e), SingleElementNode.class);
+               String id = ElementUtils.generateNodeId(e);
+                holder = parentNode.addNode(id, SingleElementNode.class);
+                holder.setKey(e.getHint(ElementHints.KEY_OBJECT));
                 holder.setTransferableProvider(new ElementTransferableProvider(getContext(), e));
                 e.setHint(sgKey, holder);
                 holder.setZIndex(parentNode.getNodeCount() + 1);
index 5245ed47e7665fb1abe384033ad087367844f446..7f81e56f2300bc0511473436e2494932e4b8af15 100644 (file)
@@ -37,7 +37,8 @@ Require-Bundle: org.simantics.simulation;bundle-version="1.0.0",
  org.simantics.scl.db;bundle-version="0.1.3",
  org.simantics.selectionview.ontology;bundle-version="1.2.0",
  org.simantics.scl.ui;bundle-version="0.5.0",
- org.slf4j.api
+ org.slf4j.api,
+ org.apache.batik
 Export-Package: org.simantics.modeling,
  org.simantics.modeling.actions,
  org.simantics.modeling.adapters,
index fa1d2193fde51181ade6b5dc66bd79e256ff2cc4..5e0e830752c5a3869655a6c3101b4311e4c182cf 100644 (file)
@@ -51,12 +51,14 @@ importJava "org.simantics.modeling.SCLScenegraph" where
     editNodeText :: ICanvasContext -> String -> String -> String -> <Proc> String
     
     copyPaste :: ICanvasContext -> ICanvasContext -> [Resource] -> <Proc> Boolean
+    
+    renderSVG :: ICanvasContext -> <Proc> String
 
 getSceneGraphProvider :: Diagram -> <Proc> ICanvasSceneGraphProvider
 getSceneGraphProvider diagram = do
     diagramName = syncRead(\() -> getSafeName diagram)
     diagramRVI = "/" + diagramName
-    model = syncRead(\() -> getPossibleModel diagram)
+    model = syncRead(\() -> fromJust $ possibleIndexRoot diagram)
     composite = syncRead(\() -> compositeToDiagram' diagram)
     getICanvasSceneGraphProvider model composite diagramRVI
 
index 33d7f06290e109e69fdc4ceff7afb00c8b7cd00a..3fbfa2304a062f67983ad8a62199057fe360acb7 100644 (file)
@@ -1,10 +1,21 @@
 package org.simantics.modeling;
 
+import java.awt.Dimension;
+import java.awt.RenderingHints.Key;
+import java.awt.geom.Rectangle2D;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGraphics2D;
 import org.simantics.db.Resource;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.diagram.elements.DiagramNodeUtil;
@@ -19,6 +30,7 @@ import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;
 import org.simantics.g2d.utils.CanvasUtils;
 import org.simantics.scenegraph.ParentNode;
+import org.simantics.scenegraph.g2d.G2DHints;
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.G2DSceneGraph;
 import org.simantics.scenegraph.g2d.events.command.Commands;
@@ -29,9 +41,13 @@ import org.simantics.scenegraph.g2d.nodes.DataNode;
 import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode;
 import org.simantics.scenegraph.g2d.nodes.NavigationNode;
 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.trend.impl.ItemNode;
 import org.simantics.utils.threads.ThreadUtils;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 public class SCLScenegraph {
        
@@ -285,6 +301,128 @@ public class SCLScenegraph {
                }
                return true;
     }
+     
+   
+     
+     
+     static class Generator extends SVGGraphics2D {
+        
+        int elemLevel = 0;
+        String newElementId = null;
+        ArrayList<Element> elements = new ArrayList<Element>();
+
+        public static final String svgNS = "http://www.w3.org/2000/svg";
+        
+               public Generator(Document document) {
+                       super(document);
+               }
+               
+               @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(G2DHints.KEY_BEGIN_ELEMENT == arg0) {
+                               elemLevel++;
+                       }
+                       if(G2DHints.KEY_ELEMENT_ID == arg0) {
+                               if(arg1 != null)
+                                       newElementId = arg1.toString();
+                               else
+                                       newElementId = UUID.randomUUID().toString();
+                       }
+                       if(G2DHints.KEY_END_ELEMENT == arg0) {
+                               elemLevel--;
+                               if(elemLevel == 0) {
+                                       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 String renderSVG(ICanvasContext ctx) {
+
+         // 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);
+
+         try {
+                
+             G2DSceneGraph sg = ctx.getSceneGraph();
+             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);
+             
+             // nav is a node that has zooming functionalities
+             NavigationNode nav = NodeUtil.getNearestChildByClass(root, NavigationNode.class);
+             nav.setZoomEnabled(true);
+             
+             // fit view with the contents of rtreeBounds
+             nav.zoomTo(rtreeBounds);
+             
+             // get the bounds of the content
+             Rectangle2D content = NodeUtil.getLocalBounds(nav);
+             
+             // translate svgGenerator to the x and y coordinates of current content
+             svgGenerator.translate(-1 * content.getX(), -1 * content.getY());
+
+             Rectangle2D destination = new Rectangle2D.Double(0,0,1000,1000);
+             double sx = destination.getWidth() / content.getWidth();
+             double sy = destination.getHeight() / content.getHeight();
+             double scale = sx < sy ? sx : sy;
+             
+             svgGenerator.scale(scale, scale);
+             
+             // Set svgCanvasSize to the given size parameters
+             svgGenerator.setSVGCanvasSize(new Dimension((int)(scale * content.getWidth()), (int)(scale * content.getHeight())));
+             svgGenerator.setClip(content);
+             
+             sg.render(svgGenerator);
+             
+         } finally {
+         }
+
+         String result = "";
+
+         // Finally, stream out SVG to the standard output using
+         // UTF-8 encoding.
+         boolean useCSS = true; // we want to use CSS style attributes
+         try {
+             ByteArrayOutputStream os = new ByteArrayOutputStream();
+             Writer out = new OutputStreamWriter(os, "UTF-8");
+             svgGenerator.stream(out, useCSS);
+             os.flush();
+             os.close();
+             result = new String(os.toByteArray());//Base64.encode(os.toByteArray());
+         } catch (UnsupportedEncodingException e) {
+             e.printStackTrace();
+         } catch (IOException e) {
+             e.printStackTrace();
+         }
+         
+         return result;
+        
+     }
 
 
 }
\ No newline at end of file
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DHints.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DHints.java
new file mode 100644 (file)
index 0000000..e960348
--- /dev/null
@@ -0,0 +1,36 @@
+package org.simantics.scenegraph.g2d;
+
+import java.util.Map;
+
+public final class G2DHints {
+
+    private G2DHints() {
+    }
+    
+    public static final Key KEY_BEGIN_ELEMENT = new G2DHints.Key(0);
+    public static final Key KEY_END_ELEMENT = new G2DHints.Key(1);
+    public static final Key KEY_ELEMENT_ID = new G2DHints.Key(2);
+
+    public static class Key extends java.awt.RenderingHints.Key {
+
+        public Key(int privateKey) {
+            super(privateKey);    
+        }
+    
+        @Override
+        public boolean isCompatibleValue(Object val) {
+            switch (intKey()) {
+                case 0:
+                    return val == null || val instanceof String 
+                            || val instanceof Map;
+                case 1:
+                    return val == null || val instanceof Object;
+                case 2:
+                    return val == null || val instanceof Object;
+                default:
+                    throw new RuntimeException("Not possible!");
+            }
+        }
+    }
+    
+}
\ No newline at end of file
index a83ca52647719d4dd3d4001605f9a3086336bdb9..60c222f6d5b5d4ff348829eda1cea0c9cba3225c 100644 (file)
@@ -13,11 +13,13 @@ package org.simantics.scenegraph.g2d.nodes;
 
 import java.awt.Color;
 import java.awt.Composite;
+import java.awt.Graphics2D;
 import java.awt.Stroke;
 import java.awt.geom.Point2D;
 
 import org.simantics.diagram.connection.RouteGraph;
 import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DHints;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.events.MouseEvent;
 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
@@ -150,4 +152,12 @@ public class ConnectionNode extends SingleElementNode implements InitValueSuppor
         return false;
     }
 
+    public void beforeRender(Graphics2D g) {
+        g.setRenderingHint(G2DHints.KEY_BEGIN_ELEMENT, "connection");
+    }
+    
+    public void afterRender(Graphics2D g) {
+        g.setRenderingHint(G2DHints.KEY_END_ELEMENT, "connection");
+    }
+    
 }
index 00ca6b8663f59c984097508f9cc9a5dba1491fc5..9495a644084d2c83690a6b31a0769af0dd70a935 100644 (file)
@@ -17,6 +17,8 @@ import java.awt.Graphics2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 
+import org.simantics.scenegraph.ParentNode;
+import org.simantics.scenegraph.g2d.G2DHints;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.events.EventTypes;
 import org.simantics.scenegraph.g2d.events.MouseEvent;
@@ -28,11 +30,20 @@ public class SingleElementNode extends TransformNode implements InitValueSupport
 
     private static final long serialVersionUID = -4982578347433716440L;
 
+    private Object key;
     private TransferableProvider transferableProvider;
     protected Composite composite;
     protected Boolean visible = Boolean.TRUE;
     protected Boolean hidden = Boolean.FALSE;
 
+    public void setKey(Object key) {
+       this.key = key;
+    }
+    
+    public Object getKey() {
+       return key;
+    }
+    
     public void setTransferableProvider(TransferableProvider transferableProvider) {
         if (transferableProvider != this.transferableProvider) {
             if (this.transferableProvider != null)
@@ -71,11 +82,22 @@ public class SingleElementNode extends TransformNode implements InitValueSupport
         return hidden;
     }
 
+    public void beforeRender(Graphics2D g) {
+        g.setRenderingHint(G2DHints.KEY_BEGIN_ELEMENT, "definedElement");
+    }
+    
+    public void afterRender(Graphics2D g) {
+        g.setRenderingHint(G2DHints.KEY_ELEMENT_ID, getKey());
+        g.setRenderingHint(G2DHints.KEY_END_ELEMENT, "definedElement");
+    }
+
     @Override
     public void render(Graphics2D g) {
         if (!visible || hidden)
             return;
 
+        beforeRender(g);
+        
         Composite oldComposite = null;
         if(composite != null) {
             oldComposite = g.getComposite();
@@ -91,6 +113,9 @@ public class SingleElementNode extends TransformNode implements InitValueSupport
 
         if (oldComposite != null)
             g.setComposite(oldComposite);
+
+        afterRender(g);
+
     }
 
     @Override
index 4cacc70f99bb2c23859095d7fa7df8889d9c1a9e..9b35b33a9e9da3cf4648e0572f3502774fda5a81 100644 (file)
@@ -11,6 +11,7 @@
  *******************************************************************************/
 package org.simantics.scenegraph.g2d.nodes;
 
+import java.awt.Graphics2D;
 import java.awt.geom.Rectangle2D;
 
 public class UnboundedNode extends SingleElementNode {
@@ -21,5 +22,13 @@ public class UnboundedNode extends SingleElementNode {
     public Rectangle2D getBoundsInLocal() {
         return null;
     }
+    
+    @Override
+    public void beforeRender(Graphics2D g) {
+    }
+    
+    @Override
+    public void afterRender(Graphics2D g) {
+    }
 
 }