From 181b3b454b2b6f02491ab082e9f3b0cea1587857 Mon Sep 17 00:00:00 2001 From: Reino Ruusu Date: Tue, 5 Dec 2017 13:15:06 +0200 Subject: [PATCH] Improved scaling of diagrams rendered into selectable SVG. (refs #7593) Merge of change 1184. Change-Id: I27ad59665d0be021a1fcd8d68e6d4eca0edc3fda --- .../scl/Simantics/Scenegraph.scl | 4 + .../org/simantics/modeling/SCLScenegraph.java | 121 ++++++++---------- 2 files changed, 58 insertions(+), 67 deletions(-) diff --git a/bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl b/bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl index 5bf540e35..6a1ecd8fc 100644 --- a/bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl +++ b/bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl @@ -60,6 +60,10 @@ importJava "org.simantics.modeling.SCLScenegraph" where renderSVG :: ICanvasContext -> String + "Render an SVG with known width and height in pixels: `renderScaledSVG context width height`" + @JavaName renderSVG + renderScaledSVG :: ICanvasContext -> Double -> Double -> String + getSceneGraphProvider :: Diagram -> ICanvasSceneGraphProvider getSceneGraphProvider diagram = do diagramName = syncRead(\() -> getSafeName diagram) 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 e0c198388..1e80b832d 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java @@ -422,20 +422,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 { @@ -492,7 +479,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))); } /** @@ -513,11 +504,19 @@ public class SCLScenegraph { * 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 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 @@ -525,8 +524,8 @@ public class SCLScenegraph { * @param ctx * @return */ - public static String renderSVGMapIdentifiers(ICanvasContext ctx) { - return renderSVG0(ctx, new Function1, Map>() { + public static String renderSVGMapIdentifiers(ICanvasContext ctx, double width, double height) { + return renderSVG0(width, height, ctx, new Function1, Map>() { @Override public Map apply(Set p0) { @@ -610,7 +609,7 @@ public class SCLScenegraph { } - private static String renderSVG0(ICanvasContext ctx, Function1, Map> mappingFunction) { + private static String renderSVG0(double width, double height, ICanvasContext ctx, Function1, Map> mappingFunction) { // Get a DOMImplementation. DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); @@ -642,48 +641,26 @@ 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()); + + 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, ""); result.append(SELECTION_SECTION, ""); result.append(SELECTION_MASK_SECTION, ""); - result.append(ALL_SECTIONS, ""); - - KeyVisitor keyVisitor = new KeyVisitor(); sg.accept(keyVisitor); @@ -691,7 +668,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); @@ -700,7 +677,7 @@ public class SCLScenegraph { } - result.append(ALL_SECTIONS, ""); + result.append(ALL_SECTIONS, ""); StringBuilder res = new StringBuilder(); res.append(""); @@ -749,12 +726,14 @@ public class SCLScenegraph { HashMap 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) { @@ -787,7 +766,7 @@ public class SCLScenegraph { parentBuilder.append(SELECTION_SECTION, "\n"); parentBuilder.append(SELECTION_MASK_SECTION, "\n"); - Element doc = renderSVGNode((IG2DNode)node); + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); String svg = printSVGDocument(doc); parentBuilder.append(MAIN_SECTION, svg); @@ -795,7 +774,7 @@ public class SCLScenegraph { n.setIgnoreSelection(false); } - doc = renderSVGNode((IG2DNode)node); + doc = renderSVGNode(svgGenerator, (IG2DNode)node); svg = printSVGDocument(doc); parentBuilder.append(SELECTION_SECTION, svg); @@ -805,7 +784,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); @@ -823,13 +802,17 @@ public class SCLScenegraph { String key = getKey(parentSEN); n.setIgnore(false); - Element doc = renderSVGNode((IG2DNode)node); + 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"); + AffineTransform transform = svgGenerator.getTransform(); + double[] matrix = new double[6]; + transform.getMatrix(matrix); + String matrixString = String.format("matrix(%f,%f,%f,%f,%f,%f)", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); Rectangle2D rect = n.getRect(); // NaN if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { @@ -900,12 +883,16 @@ public class SCLScenegraph { RenderSVGContext b = senBuilders.get(sen); String content = b.get(MAIN_SECTION); if(content.isEmpty()) { - for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) { - n.setIgnore(true); + 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); } - Element doc = renderSVGNode((IG2DNode)node); - String svg = printSVGDocument(doc); - parentBuilder.append(MAIN_SECTION, svg); } else { parentBuilder.append(b); } -- 2.43.2