]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java
Merge "Reading background color of a ICanvasContext with SCL"
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / SCLScenegraph.java
index 2591dc7d85dfd79ffa05dd208667d1b9caf5aa65..55ed12656131c0629595cd7ded70c4f4bd064234 100644 (file)
@@ -1,6 +1,7 @@
 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;
@@ -18,6 +19,7 @@ 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;
@@ -33,14 +35,18 @@ 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;
@@ -70,6 +76,7 @@ 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;
@@ -89,16 +96,47 @@ public class SCLScenegraph {
        
        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> T doWithICanvasSceneGraphProvider(Resource diagram, Function1<ICanvasSceneGraphProvider, T> func) throws DatabaseException {
+        return doWithICanvasSceneGraphProvider(diagram, (Function<ICanvasSceneGraphProvider, T>) provider -> func.apply(provider));
+    }
+
+    public static <T> T doWithICanvasSceneGraphProvider(Resource diagram, Function<ICanvasSceneGraphProvider, T> func) throws DatabaseException {
+        Tuple2 result = Simantics.getSession().syncRequest(new ResourceRead<Tuple2>(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> T doWithCanvasContext(Resource diagram, Function1<ICanvasContext, T> func) throws DatabaseException {
+        return doWithCanvasContext(diagram, (Function<ICanvasContext, T>) canvasContext -> func.apply(canvasContext));
+    }
+
+    public static <T> T doWithCanvasContext(Resource diagram, Function<ICanvasContext, T> func) throws DatabaseException {
+        return doWithICanvasSceneGraphProvider(diagram, (Function<ICanvasSceneGraphProvider, T>) provider -> func.apply(provider.getCanvasContext()));
+    }
+
        public static String getNodeTransform(ICanvasContext ctx, String name) {
                
                Set<TextNode> texts = NodeUtil.collectNodes(ctx.getSceneGraph(), TextNode.class);
@@ -240,6 +278,23 @@ public class SCLScenegraph {
        }
         return "No nodes in scenegraph!";
     }
+    
+    /**
+     * Returns background color of a canvasContext or null.
+     * @param ctx
+     * @return color in RGBA List<Integer> format, or null.
+     */
+    public static List<Integer> getBackgroundColor(ICanvasContext ctx) {
+       Color color = ctx.getDefaultHintContext().getHint(Hints.KEY_BACKGROUND_COLOR);
+       if (color == null)
+               return null;
+       ArrayList<Integer> 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) {
        
@@ -342,12 +397,14 @@ public class SCLScenegraph {
 
                public static final String svgNS = "http://www.w3.org/2000/svg";
 
-               public Generator(SVGGeneratorContext ctx, boolean joku) {
-                       super(ctx, joku);
+               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
@@ -386,20 +443,7 @@ public class SCLScenegraph {
 
        }
 
-       public static Element renderSVGNode(IG2DNode node) {
-
-               // 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);
-
-               SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
-               ctx.setComment(null);
-
-               // Create an instance of the SVG Generator.
-               SVGGraphics2D svgGenerator = new Generator(ctx, false);
+       public static Element renderSVGNode(SVGGraphics2D svgGenerator, IG2DNode node) {
 
                try {
 
@@ -456,7 +500,11 @@ public class SCLScenegraph {
        }
 
        public static String renderSVG3(ICanvasContext ctx) {
-        return renderSVG0(ctx, p0 -> p0.stream().collect(Collectors.toMap(p1 -> p1, p2 -> p2)));
+               return renderSVG3(ctx, -1, -1);
+       }
+
+       public static String renderSVG3(ICanvasContext ctx, double width, double height) {
+        return renderSVG0(width, height, ctx, p0 -> p0.stream().collect(Collectors.toMap(p1 -> p1, p2 -> p2)));
        }
 
     /**
@@ -477,11 +525,19 @@ public class SCLScenegraph {
      * Default no-op mapper
      */
     private static final Function1<Set<?>, 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 renderSVG0(ctx, mapper);
+       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
@@ -489,8 +545,8 @@ public class SCLScenegraph {
      * @param ctx
      * @return
      */
-    public static String renderSVGMapIdentifiers(ICanvasContext ctx) {
-        return renderSVG0(ctx, new Function1<Set<?>, Map<?, ?>>() {
+    public static String renderSVGMapIdentifiers(ICanvasContext ctx, double width, double height) {
+        return renderSVG0(width, height, ctx, new Function1<Set<?>, Map<?, ?>>() {
 
             @Override
             public Map<?, ?> apply(Set<?> p0) {
@@ -574,7 +630,7 @@ public class SCLScenegraph {
        
     }
     
-       private static String renderSVG0(ICanvasContext ctx, Function1<Set<?>, Map<?, ?>> mappingFunction) {
+       private static String renderSVG0(double width, double height, ICanvasContext ctx, Function1<Set<?>, Map<?, ?>> mappingFunction) {
 
                // Get a DOMImplementation.
                DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
@@ -588,6 +644,7 @@ public class SCLScenegraph {
 
                RenderSVGContext result = new RenderSVGContext();
 
+               double[] matrix = new double[6];
                try {
 
             Selection selection = ctx.getAtMostOneItemOfClass(Selection.class);
@@ -606,48 +663,31 @@ public class SCLScenegraph {
                        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);
-
-                       svgGenerator.scale(3,3);
-
+                       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(-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.translate(-content.getX(), -content.getY());
 
-                       // Set svgCanvasSize to the given size parameters
-                       svgGenerator.setSVGCanvasSize(new Dimension((int)(scale * content.getWidth()), (int)(scale * content.getHeight())));
-                       svgGenerator.setClip(content);
-
-                       double trX = -1 * content.getX();
-                       double trY = -1 * content.getY();
-                       
-                       // NaNs
-                       if(!Double.isFinite(trX)) trX = 0;
-                       if(!Double.isFinite(trY)) trY = 0;
+                       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, "<g class=\"symbols\">");
                        result.append(SELECTION_SECTION, "<g class=\"selections\">");
                        result.append(SELECTION_MASK_SECTION, "<g class=\"selectionMasks\">");
 
-                       result.append(ALL_SECTIONS, "<g transform=\"translate(");
-                       result.append(ALL_SECTIONS, "" + trX);
-                       result.append(ALL_SECTIONS, ", ");
-                       result.append(ALL_SECTIONS, "" + trY);
-                       result.append(ALL_SECTIONS, ")\">");
-
-                       
                        KeyVisitor keyVisitor = new KeyVisitor();
                        sg.accept(keyVisitor);
                        
@@ -655,7 +695,7 @@ public class SCLScenegraph {
                        
                        Map<?, ?> mappings = mappingFunction.apply(keys);
 
-                       IG2DNodeVisitor visitor = new PrintingVisitor(result, mappings);
+                       IG2DNodeVisitor visitor = new PrintingVisitor(svgGenerator, result, mappings);
                        
                        sg.accept(visitor);
 
@@ -664,14 +704,16 @@ public class SCLScenegraph {
                }
 
 
-               result.append(ALL_SECTIONS, "</g></g>");
+               result.append(ALL_SECTIONS, "</g>");
 
                StringBuilder res = new StringBuilder();
                res.append("<svg width=\"100%\" height=\"100%\" stroke=\"black\">");
+               res.append("<g transform=\"matrix(").append(matrix[0]).append(",").append(matrix[1]).append(",").append(matrix[2]).append(",").append(matrix[3]).append(",").append(matrix[4]).append(",").append(matrix[5]).append(")\">");
                res.append(result.get(MAIN_SECTION));
                res.append(result.get(SELECTION_SECTION));
                res.append(result.get(SELECTION_MASK_SECTION));
-               res.append(result.get("</svg>"));
+               res.append("</g>");
+               res.append("</svg>");
 
 //             System.err.println(" == FINAL RESULT == ");
 //             System.err.println(res.toString());
@@ -713,20 +755,26 @@ public class SCLScenegraph {
         HashMap<SingleElementNode,RenderSVGContext> senBuilders = new HashMap<>();
 
         private RenderSVGContext result;
+        private SVGGraphics2D svgGenerator;
 
         private Map<?, ?> mappings;
 
-        public PrintingVisitor(RenderSVGContext result, 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 (mappings.containsKey(node.getKey()))
-                key = mappings.get(node.getKey()).toString();
-            else
-                key = node.getKey().toString();
+               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;
         }
 
@@ -747,7 +795,7 @@ public class SCLScenegraph {
                 parentBuilder.append(SELECTION_SECTION, "\n<g style=\"visibility:hidden\" class=\"selection\" id=\"" + key + "\">");
                 parentBuilder.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" opacity=\"0.001\" id=\"" + key + "\">");
                 
-                Element doc = renderSVGNode((IG2DNode)node);
+                Element doc = renderSVGNode(svgGenerator, (IG2DNode)node);
                 String svg = printSVGDocument(doc);
                 parentBuilder.append(MAIN_SECTION, svg);
 
@@ -755,7 +803,7 @@ public class SCLScenegraph {
                     n.setIgnoreSelection(false);
                 }
 
-                doc = renderSVGNode((IG2DNode)node);
+                doc = renderSVGNode(svgGenerator, (IG2DNode)node);
                 svg = printSVGDocument(doc);
                 parentBuilder.append(SELECTION_SECTION, svg);
 
@@ -765,7 +813,7 @@ public class SCLScenegraph {
                     n.setDynamicStroke(bs);
                 }
 
-                doc = renderSVGNode((IG2DNode)node);
+                doc = renderSVGNode(svgGenerator, (IG2DNode)node);
                 svg = printSVGDocument(doc);
                 parentBuilder.append(SELECTION_MASK_SECTION, svg);
 
@@ -777,17 +825,19 @@ public class SCLScenegraph {
                 
                 SelectionNode n = (SelectionNode)node;
                 SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class);
-                if(parentSEN != null && parentSEN.getKey() != null) {
+                if(parentSEN != null) {
                     
                        RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN);
                     
                     String key = getKey(parentSEN);
-                    Element doc = renderSVGNode((IG2DNode)node);
+                    n.setIgnore(false);
+                    Element doc = renderSVGNode(svgGenerator, (IG2DNode)node);
+                    n.setIgnore(true);
                     String svg = printSVGDocument(doc);
                     parentBuilder2.append(SELECTION_SECTION, "\n<g style=\"visibility:hidden\" class=\"selection\" id=\"" + key + "\">");
                     parentBuilder2.append(SELECTION_SECTION, svg);
                     parentBuilder2.append(SELECTION_SECTION, "\n</g>");
-                    parentBuilder2.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" id=\"" + key + "\">");
+                    parentBuilder2.append(SELECTION_MASK_SECTION, "\n<g class=\"selectionMask\" id=\"" + key /*+ "\" transform=\"" + matrixString + "\"*/+ "\">");
                     Rectangle2D rect = n.getRect();
                     // NaN
                     if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) {
@@ -797,6 +847,7 @@ public class SCLScenegraph {
                            parentBuilder2.append(SELECTION_MASK_SECTION,"></rect>");
                     }
                     parentBuilder2.append(SELECTION_MASK_SECTION,"\n</g>");
+                   
                 }
             } else if (node instanceof SVGNode) {
                 SVGNode svg = (SVGNode)node;
@@ -805,15 +856,13 @@ public class SCLScenegraph {
                 AffineTransform at = node.getTransform();
                 if(node instanceof SingleElementNode) {
                     SingleElementNode sen = (SingleElementNode)node;
-                    if(sen.getKey() != null) {
-                        String key = getKey(sen);
-                        String typeClass = sen.getTypeClass();
-                        String clazz = "definedElement";
-                        if(typeClass != null && !typeClass.isEmpty())
-                               clazz = clazz + " " + typeClass;
-                        
-                        parentBuilder.append(MAIN_SECTION, "\n<g class=\""+clazz+"\" id=\"" + key + "\">");
-                    }
+                    String key = getKey(sen);
+                    String typeClass = sen.getTypeClass();
+                    String clazz = "definedElement";
+                    if(typeClass != null && !typeClass.isEmpty())
+                       clazz = clazz + " " + typeClass;
+
+                    parentBuilder.append(MAIN_SECTION, "\n<g class=\""+clazz+"\" id=\"" + key + "\">");
                     senBuilders.put(sen, new RenderSVGContext());
                 }
                 if(!at.isIdentity()) {
@@ -865,7 +914,7 @@ public class SCLScenegraph {
                                                n.setIgnore(true);
                                        }
 
-                                       Element doc = renderSVGNode((IG2DNode)node);
+                                       Element doc = renderSVGNode(svgGenerator, (IG2DNode)node);
                                        String svg = printSVGDocument(doc);
                                        parentBuilder.append(MAIN_SECTION, svg);
                                }
@@ -880,9 +929,9 @@ public class SCLScenegraph {
                 }
                 if(node instanceof SingleElementNode) {
                     SingleElementNode sen = (SingleElementNode)node;
-                    if(sen.getKey() != null) {
+                    //if(sen.getKey() != null) {
                         parentBuilder.append(MAIN_SECTION, "</g>");
-                    }
+                    //}
                 }
             }
             indent --;