Author: Christian Lopes
Date: 2010-11-16 15:15:32 -0800 (Tue, 16 Nov 2010)
New Revision: 22875

Added:
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/converters/SVGExporter.as
   cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Images.as
Modified:
   cytoscapeweb/trunk/cytoscapeweb/html-template/js/cytoscapeweb.js
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ApplicationMediator.as
   cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ExternalMediator.as
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/EdgeRenderer.as
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
Log:
Added SVG Exporter.

Modified: cytoscapeweb/trunk/cytoscapeweb/html-template/js/cytoscapeweb.js
===================================================================
--- cytoscapeweb/trunk/cytoscapeweb/html-template/js/cytoscapeweb.js    
2010-11-16 21:47:02 UTC (rev 22874)
+++ cytoscapeweb/trunk/cytoscapeweb/html-template/js/cytoscapeweb.js    
2010-11-16 23:15:32 UTC (rev 22875)
@@ -1238,6 +1238,19 @@
         },
         
         /**
+         * <p>Return an SVG image.</p>
+         * @param {Object} [options] Additional options:
+         *                           <ul class="options">
+         *                               <li><code>width</code>:</strong> The 
desired width of the image in pixels.</li>
+         *                               <li><code>height</code>:</strong> The 
desired height of the image in pixels.</li>
+         *                           </ul>
+         * @return {String} The SVG image.
+         */
+        svg: function (options) {
+               return this.swf().getNetworkAsImage("svg", options);
+        },
+        
+        /**
          * <p>Return the network as a PNG image.</p>
          * @return {String} The PNG binary data encoded to a Base64 string.
          */

Added: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/converters/SVGExporter.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/converters/SVGExporter.as
                                (rev 0)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/converters/SVGExporter.as
        2010-11-16 23:15:32 UTC (rev 22875)
@@ -0,0 +1,546 @@
+/*
+  This file is part of Cytoscape Web.
+  Copyright (c) 2009, The Cytoscape Consortium (www.cytoscape.org)
+
+  The Cytoscape Consortium is:
+    - Agilent Technologies
+    - Institut Pasteur
+    - Institute for Systems Biology
+    - Memorial Sloan-Kettering Cancer Center
+    - National Center for Integrative Biomedical Informatics
+    - Unilever
+    - University of California San Diego
+    - University of California San Francisco
+    - University of Toronto
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+package org.cytoscapeweb.model.converters { 
+    import flare.display.TextSprite;
+    import flare.vis.data.Data;
+    import flare.vis.data.DataList;
+    import flare.vis.data.DataSprite;
+    import flare.vis.data.EdgeSprite;
+    import flare.vis.data.NodeSprite;
+    
+    import flash.display.DisplayObject;
+    import flash.filters.BitmapFilter;
+    import flash.filters.GlowFilter;
+    import flash.geom.Point;
+    import flash.geom.Rectangle;
+    import flash.text.TextField;
+    
+    import org.cytoscapeweb.model.data.ConfigVO;
+    import org.cytoscapeweb.model.data.VisualStyleVO;
+    import org.cytoscapeweb.util.Anchors;
+    import org.cytoscapeweb.util.ArrowShapes;
+    import org.cytoscapeweb.util.Fonts;
+    import org.cytoscapeweb.util.NodeShapes;
+    import org.cytoscapeweb.util.Utils;
+    import org.cytoscapeweb.util.VisualProperties;
+    import org.cytoscapeweb.view.components.GraphView;
+        
+    /**
+     * Class that generates an SGV image from the network.
+     */
+    public class SVGExporter {
+        
+        // ========[ CONSTANTS 
]====================================================================
+
+        private static const GLOW_WIDTH:Number = 3;
+
+        // ========[ PRIVATE PROPERTIES 
]===========================================================
+        
+        private var _graphView:GraphView;
+        private var _style:VisualStyleVO;
+        private var _scale:Number;
+        private var _shiftX:Number;
+        private var _shiftY:Number;
+        
+        // ========[ PUBLIC PROPERTIES 
]============================================================
+
+        public var margin:Number = 10;
+
+        // ========[ CONSTRUCTOR 
]==================================================================
+        
+        public function SVGExporter(view:GraphView) {
+            this._graphView = view;         
+        }
+
+        // ========[ PUBLIC METHODS 
]===============================================================
+
+        /**
+         * @param graphData
+         * @param scale The zooming scale applied to the graph.
+         * @param showLabels Whether or not labels will be included. 
+         * @param width The desired image width in pixels.
+         * @param height The desired image height in pixels.
+         */
+        public function export(graphData:Data,
+                               style:VisualStyleVO,
+                               config:ConfigVO,
+                               scale:Number=1, 
+                               width:Number=0, height:Number=0):String {
+            _style = style;
+            _scale = scale;
+            var bounds:Rectangle = _graphView.getRealBounds();
+            
+            // Width and height depends on the current zooming and
+            // we also add a margin to the image:
+            var w:Number = bounds.width/_scale + 2*margin;
+            var h:Number = bounds.height/_scale + 2*margin;
+            
+            var hPad:Number = 0
+            
+            if (width > 0 || height > 0) {
+                // If the client asked a custom size image, we need a new 
scale factor:
+                _scale = calculateNewScale(w,  h,  width,  height);
+                // Center image horizontally:
+                if (width/_scale > w) hPad = (width/_scale - w)/2;
+                h = height;
+                w = width;
+            } else {
+                // Otherwise, the other graphics elements don't need to be 
scaled: 
+                _scale = 1;
+            }
+            
+            // Create the root element:
+            var svg:String = '<?xml version="1.0" standalone="no"?>' +
+                             '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>' +
+                             '<svg width="'+w+'px" height="'+h+'px" x="0px" 
y="0px" viewBox="0 0 '+w+' '+h+'" version="1.1" 
xmlns="http://www.w3.org/2000/svg";>';
+            
+            // Draw the background:
+            var bgColor:String = 
Utils.rgbColorAsString(_style.getValue(VisualProperties.BACKGROUND_COLOR));
+            svg += '<rect x="0" y="0" width="100%" height="100%" 
fill="'+bgColor+'"/>';
+            
+            // Get the shift, in case one or more nodes were dragged or the 
graph view is not at [0,0]:
+            var sp:Point = _graphView.vis.globalToLocal(new Point(bounds.x, 
bounds.y));
+            _shiftX = sp.x - margin - hPad ;
+            _shiftY = sp.y - margin;
+            
+            // Draw edges and nodes:
+            svg += drawEdges(graphData.edges);
+            if (config.edgeLabelsVisible) svg += drawLabels(graphData.edges);
+            svg += drawNodes(graphData.nodes);
+            if (config.nodeLabelsVisible) svg += drawLabels(graphData.nodes);
+    
+            // Close the root element:
+            svg += '</svg>';
+    
+            return svg;
+        }
+        
+        // ========[ PRIVATE METHODS 
]==============================================================
+        
+        private function drawEdges(edges:DataList):String {
+            var svg:String = '';
+            var c:String, a:Number;
+            var sortedEdges:Array = sortByZOrder(edges);
+            
+            for each (var e:EdgeSprite in sortedEdges) {
+                if (!e.visible || e.lineAlpha === 0 || e.lineWidth === 0) 
continue;
+                
+                // Edge points:
+                var start:Point, end:Point, c1:Point, c2:Point;
+                if (e.props.$points != null) {
+                    start = e.props.$points.start;
+                    end = e.props.$points.end;
+                    c1 = e.props.$points.c1;
+                    c2 = e.props.$points.c2;
+                }
+                
+                if (start != null && end != null) {
+                    start = toImagePoint(start, e);
+                    end = toImagePoint(end, e);
+                    
+                    if (c1 != null) c1 = toImagePoint(c1, e);
+                    if (c2 != null) c2 = toImagePoint(c2, e);
+                    
+                    // Arrows points:
+                    var sArrowPoints:Array = 
toImagePointsArray(e.props.$points.sourceArrow, e);
+                    var tArrowPoints:Array = 
toImagePointsArray(e.props.$points.targetArrow, e);
+                    var sJointPoints:Array = 
toImagePointsArray(e.props.$points.sourceArrowJoint, e);
+                    var tJointPoints:Array = 
toImagePointsArray(e.props.$points.targetArrowJoint, e);
+                    
+                    var saStyle:Object = ArrowShapes.getArrowStyle(e, 
e.props.sourceArrowShape, e.props.sourceArrowColor);
+                    var taStyle:Object = ArrowShapes.getArrowStyle(e, 
e.props.targetArrowShape, e.props.targetArrowColor);
+                    
+                    var w:Number = e.lineWidth * _scale;
+                    var loop:Boolean = e.source === e.target;
+                    
+                    // First let's draw any glow (e.g. for selected edges):
+                    // -----------------------------------------------------
+                    var filters:Array = e.filters;
+                    
+                    for each (var f:BitmapFilter in filters) {
+                        if (f is GlowFilter) {
+                            var glow:GlowFilter = f as GlowFilter;
+                            var gw:Number = w + (GLOW_WIDTH * _scale);
+                            c = Utils.rgbColorAsString(glow.color);
+                            a = Math.min(glow.alpha, e.alpha);
+                            
+                            // The current version of AlivePDF does not 
support glows, gradients, etc.
+                            // So we just draw a bigger shape behind the node:
+                            svg += '<g stroke-linejoin="round" 
stroke-width="'+gw+'" stroke-linecap="butt" fill="none" stroke-opacity="'+a+'" 
stroke="'+c+'">';
+                            svg += drawEdgeShaft(start, end, c1, c2, loop);
+                            svg += '</g>';
+                            
+                            // Arrow glow:
+                            gw = GLOW_WIDTH * _scale;
+                            
+                            svg += '<g fill="'+c+'" fill-opacity="'+a+'" 
stroke-linejoin="round" stroke-width="'+gw+'" stroke-linecap="butt" 
stroke-opacity="'+a+'" stroke="'+c+'">';
+                            svg += drawEdgeArrow(saStyle.shape, sArrowPoints, 
saStyle.height*_scale);
+                            svg += drawEdgeArrowJoint(sJointPoints, 
saStyle.shape);
+                            svg += drawEdgeArrow(taStyle.shape, tArrowPoints, 
taStyle.height*_scale);
+                            svg += drawEdgeArrowJoint(tJointPoints, 
taStyle.shape);
+                            svg += '</g>';
+                        }
+                    }
+                    
+                    c = Utils.rgbColorAsString(e.lineColor);
+                    a = e.alpha;
+                    
+                    // Draw the edge's line and joints:
+                    // -----------------------------------------------------
+                    svg += '<g stroke-linejoin="round" stroke-width="'+w+'" 
stroke-linecap="butt" fill="none" stroke-opacity="'+a+'" stroke="'+c+'">';
+                    svg += drawEdgeShaft(start, end, c1, c2, loop);
+                    svg += '</g>';
+                    
+                    // Draw arrow joints:
+                    // -----------------------------------------------------
+                    svg += '<g fill="'+c+'" fill-opacity="'+a+'" 
stroke="none">';
+                    svg += drawEdgeArrowJoint(sJointPoints, saStyle.shape);
+                    svg += drawEdgeArrowJoint(tJointPoints, taStyle.shape);
+                    svg += '</g>';
+                    
+                    // Draw arrows:
+                    // -----------------------------------------------------
+                    c = Utils.rgbColorAsString(saStyle.color);
+                    svg += '<g fill="'+c+'" fill-opacity="'+a+'" 
stroke="none">';
+                    svg += drawEdgeArrow(saStyle.shape, sArrowPoints, 
saStyle.height*_scale);
+                    svg += '</g>';
+                    
+                    c = Utils.rgbColorAsString(taStyle.color);
+                    svg += '<g fill="'+c+'" fill-opacity="'+a+'" 
stroke="none">';
+                    svg += drawEdgeArrow(taStyle.shape, tArrowPoints, 
taStyle.height*_scale);
+                    svg += '</g>';
+                }
+            }
+            
+            return svg;
+        }
+        
+        private function drawNodes(nodes:DataList):String {
+            var svg:String = '';
+            
+            // First, sort nodes by their z-order,
+            // so overlapping nodes will have the same position in the 
generated image:
+            var sortedNodes:Array = sortByZOrder(nodes);
+            var c:String, lc:String, a:Number, lw:Number;
+            var w:Number, h:Number;
+            
+            for each (var n:NodeSprite in sortedNodes) {
+                if (!n.visible || n.alpha === 0) continue;
+                
+                // Get the Global node point (relative to the stage):
+                var np:Point = toImagePoint(new Point(n.x, n.y), n);
+                
+                // First let's draw any node glow (e.g. for selected nodes):
+                var filters:Array = n.filters;
+                for each (var f:BitmapFilter in filters) {
+                    if (f is GlowFilter) {
+                        var glow:GlowFilter = f as GlowFilter;
+                        lw = GLOW_WIDTH * _scale;
+                        lc = Utils.rgbColorAsString(glow.color);
+                        a = Math.min(glow.alpha, n.alpha);
+                        w = (n.width + GLOW_WIDTH) * _scale;
+                        h = (n.height + GLOW_WIDTH) * _scale;
+                        
+                        // The current version of AlivePDF does not support 
glows, gradients, etc.
+                        // So we just draw a bigger shape behind the node:
+                        svg += '<g fill="none" stroke="'+lc+'" 
stroke-linejoin="round" stroke-width="'+lw+'" stroke-linecap="butt" 
stroke-opacity="'+a+'">';
+                        svg += drawNodeShape(n.shape, np.x, np.y, w, h);
+                        svg += '</g>';
+                    }
+                }
+                
+                // Then draw the node:
+                lw = n.lineWidth*_scale;
+                lc = Utils.rgbColorAsString(n.lineColor);
+                c = Utils.rgbColorAsString(n.fillColor);
+                a = n.alpha;
+                w = (n.width - n.lineWidth) * _scale;
+                h = (n.height - n.lineWidth) * _scale;
+                
+                svg += '<g fill="'+c+'" fill-opacity="'+a+'" stroke="'+lc+'" 
stroke-linejoin="round" stroke-width="'+lw+'" stroke-linecap="butt" 
stroke-opacity="'+a+'">';
+                svg += drawNodeShape(n.shape, np.x, np.y, w, h);
+                svg += '</g>';
+            }
+            
+            return svg;
+        }
+        
+        private function drawLabels(data:DataList):String {
+            var svg:String = '';
+            
+            for each (var d:DataSprite in data) {
+                var lbl:TextSprite = d.props.label;
+                
+                if (lbl != null && lbl.visible && lbl.alpha > 0) {
+                    var text:String = lbl.text;
+                    var lblSize:int = Math.round(lbl.size*_scale);
+                    
+                    if (text == null || text === "" || lblSize < 1) continue;
+                    var field:TextField = lbl.textField;
+
+                    // ATTENTION!!!
+                    // It seems that Flash does not convert points to pixels 
correctly. 
+                    // See: - http://alarmingdevelopment.org/?p=66
+                    //      - 
http://www.actionscript.org/forums/showthread.php3?p=821842
+                    // I found out that the text height is usually 28% smaller 
than the label size.
+                    var textHeight:Number = lbl.size * 0.72;
+                    var textWidth:Number = field.textWidth;
+
+                    // Get the Global label point (relative to the stage):
+                    var p:Point = toImagePoint(new Point(lbl.x, lbl.y), lbl);
+                    var hAnchor:String = Anchors.CENTER;
+                    var vAnchor:String = Anchors.MIDDLE;
+                    
+                    if (d is NodeSprite) {
+                        hAnchor = 
_style.getValue(VisualProperties.NODE_LABEL_HANCHOR, d.data);
+                        vAnchor = 
_style.getValue(VisualProperties.NODE_LABEL_VANCHOR, d.data);
+                    }
+
+                    var hpad:Number = 2;
+                    switch (hAnchor) {
+                        case Anchors.LEFT:   p.x += hpad * _scale; break;
+                        case Anchors.CENTER: p.x -= (textWidth/2)*_scale; 
break;
+                        case Anchors.RIGHT:  p.x -= (textWidth + hpad)*_scale; 
break;
+                    }
+                    // Vertical anchor:
+                    // The label height is different from the real text 
height, because
+                    // there is a margin between the text and the text field 
border:
+                    var vpad:Number = 2;
+                    switch (vAnchor) {
+                        case Anchors.TOP:    p.y += (field.height - 
textHeight)/2 * _scale; break;
+                        case Anchors.MIDDLE: p.y -= textHeight/2 * _scale; 
break;
+                        case Anchors.BOTTOM: p.y -= (vpad + textHeight) * 
_scale; break;
+                    }
+                    
+                    // Flare's label cordinates is relative to the label's 
upper-left corner (x,y)=(0,0),
+                    // but AlivePDF uses the bottom-left corner instead 
(x,y)=(0,fonSize):
+                    p.y += textHeight*_scale;
+
+                    var style:String = lbl.italic ? 'italic': 'normal';
+                    var weight:String = lbl.bold ? 'bold' : 'normal';
+                    
+                    // Choose the most similar font:
+                    var family:String = lbl.font;
+                    if (family == Fonts.SANS_SERIF) family = 'sans-serif';
+                    else if (family == Fonts.SERIF) family = 'serif';
+                    else if (family == Fonts.TYPEWRITER) family = 'courier';
+                    
+                    var c:String = Utils.rgbColorAsString(lbl.color);
+                    var a:Number = lbl.alpha;
+                    
+                    svg += '<text font-family="'+family+'" 
font-style="'+style+'" font-weight="'+weight+'" stroke="none" fill="'+c+'"' +
+                                ' fill-opacity="'+a+'" font-size="'+lblSize+'" 
x="'+p.x+'" y="'+p.y+'">' + text+ '</text>';
+                }
+            }
+            
+            return svg;
+        }
+        
+        private function drawNodeShape(shape:String, x:Number, y:Number, 
w:Number, h:Number):String {
+            var svg:String = '';
+            var r:Rectangle = new Rectangle(x-w/2, y-h/2, w, h);
+            
+            switch (shape) {
+                case NodeShapes.ELLIPSE:
+                    svg += '<circle cx="'+x+'" cy="'+y+'" r="'+(h/2)+'"/>';
+                    break;
+                case NodeShapes.RECTANGLE:
+                    svg += '<rect x="'+(x-w/2)+'" y="'+(y-h/2)+'" 
width="'+w+'" height="'+h+'"/>';
+                    break;
+                case NodeShapes.ROUND_RECTANGLE:
+                    // corners (and control points), clockwise:
+                    var x1:Number = x - w/2, y1:Number = y - h/2;
+                    var x2:Number = x + w/2, y2:Number = y1;
+                    var x3:Number = x2,      y3:Number = y + h/2;
+                    var x4:Number = x1,      y4:Number = y3;
+                    // rounded corner width/height:
+                    var w4:Number = w/4, h4:Number = h/4;
+                    
+                    svg += '<path d="M'+(x1+w4)+','+(y1) +
+                                   ' L'+(x2-w4)+','+(y2) +
+                                   ' Q'+(x2)+','+(y2)+' '+(x2)+','+(y2+h4) +
+                                   ' L'+(x3)+','+(y4-h4) +
+                                   ' Q'+(x3)+','+(y3)+' '+(x3-w4)+','+(y3) +
+                                   ' L'+(x4+w4)+','+(y4) +
+                                   ' Q'+(x4)+','+(y4)+' '+(x4)+','+(y4-h4) +
+                                   ' L'+(x1)+','+(y1+h4) +
+                                   ' Q'+(x1)+','+(y1)+' 
'+(x1+w4)+','+(y1)+'"/>';
+                    break;
+                default:
+                    var points:Array = NodeShapes.getDrawPoints(r, shape);
+                    var pp:String = '';
+                    for (var i:int = 0; i < points.length; i += 2) pp += 
(points[i]+','+points[i+1]+' ');
+                    svg += '<polygon points="'+pp+'"/>';
+            }
+            
+            return svg;
+        }
+        
+        private function drawEdgeShaft(start:Point, end:Point, c1:Point, 
c2:Point, loop:Boolean):String {
+            var svg:String = '<path d="M'+start.x+','+start.y+' ';
+            
+            if (c1 != null) {
+                // Curve:
+                if (c2 != null) {
+                    // Cubic bezier:
+                    if (loop) {
+                        // Invert control points:
+                        var p:Point = c1.clone();
+                        c1 = c2.clone();
+                        c2 = p;
+                    }
+                    svg += 'C'+c1.x+","+c1.y+" "+c2.x+","+c2.y+" 
"+end.x+','+end.y;
+                } else {
+                    // Quadratic bezier:
+                    svg += 'Q'+c1.x+","+c1.y+" "+end.x+','+end.y;
+                }
+            } else {
+                // Line:
+                svg += 'L'+end.x+','+end.y;
+            }
+            svg += '"/>';
+            
+            return svg;
+        }
+        
+        private function drawEdgeArrow(shape:String, points:Array, 
diameter:Number=0):String {
+            var svg:String = '';
+            
+            if (points != null && points.length > 0) {
+                if (shape === ArrowShapes.CIRCLE) {
+                    var center:Point = points[0];
+                    svg += '<circle cx="'+center.x+'" cy="'+center.y+'" 
r="'+(diameter/2)+'"/>';
+                } else if (shape === ArrowShapes.ARROW) {
+                    var p1:Point = points[0];
+                    var c1:Point = points[1];
+                    var p2:Point = points[2];
+                    var p3:Point = points[3];
+                    var c2:Point = points[4];
+                    svg += '<path d="M'+p1.x+','+p1.y +
+                                   ' Q'+c1.x+','+c1.y+' '+p2.x+','+p2.y +
+                                   ' L'+p3.x+','+p3.y +
+                                   ' Q'+c2.x+','+c2.y+' '+p1.x+','+p1.y+'"/>';
+                } else {
+                    // Draw a polygon:
+                    var pp:String = '';
+                    for each (var p:Point in points) pp += (p.x+','+p.y+' ');
+                    svg += '<polygon points="'+pp+'"/>';
+                }
+            }
+            
+            return svg;
+        }
+        
+        private function drawEdgeArrowJoint(points:Array, 
arrowShape:String):String {
+            var svg:String = '';
+            
+            if (points != null && points.length > 0) {
+                switch (arrowShape) {
+                    case ArrowShapes.CIRCLE:
+                        if (points.length > 4) {
+                            svg += '<path d="M'+points[0].x+','+points[0].y +
+                                           ' L'+points[1].x+','+points[1].y +
+                                           ' L'+points[2].x+','+points[2].y +
+                                           ' L'+points[3].x+','+points[2].y +
+                                           ' Q'+points[4].x+','+points[4].y+' 
'+points[0].x+','+points[0].y+'"/>';
+                        }
+                        break;
+                    default:
+                        var pp:String = '';
+                        for each (var p:Point in points) pp += (p.x+','+p.y+' 
');
+                        svg += '<polygon points="'+pp+'"/>';
+                        break;
+                }
+            }
+            
+            return svg;
+        }
+        
+         private function sortByZOrder(list:DataList):Array {
+            var arr:Array = new Array();
+            
+            for each (var sp:DataSprite in list) arr.push(sp);
+            
+            arr.sort(function(a:DataSprite, b:DataSprite):int {
+                var z1:int = a.parent.getChildIndex(a);
+                var z2:int = b.parent.getChildIndex(b);
+                
+                return z1 < z2 ? -1 : (z1 > z2 ? 1 : 0);
+            });
+            
+            return arr;
+        }
+        
+        /**
+         * It converts a sprite coordinate to its correspondent element in the 
PDF.
+         */
+        private function toImagePoint(p:Point, display:DisplayObject):Point {
+            // Get the Global point (relative to the stage):
+            var ip:Point = display.parent.localToGlobal(p);
+            // Get the local point, relative to the graph container:
+            ip = _graphView.vis.globalToLocal(ip);
+            // Remove the shift:
+            ip.x -= _shiftX;
+            ip.y -= _shiftY;
+            ip.x *= _scale;
+            ip.y *= _scale;
+            
+            return ip;
+        }
+        
+        private function toImagePointsArray(points:Array, 
display:DisplayObject):Array {
+            var arr:Array;
+            
+            if (points != null) {
+                arr = [];
+                for each (var p:Point in points) {
+                   arr.push(toImagePoint(p, display));
+                }
+            }
+            
+            return arr;
+        }
+
+        private function calculateNewScale(w:Number, h:Number, newW:Number, 
newH:Number):Number {
+            if (newW == 0) newW = w;
+            if (newH == 0) newH = h;
+            
+            var graphEdge:Number = w; 
+            var pageEdge:Number = newW;
+            
+            if (h/w > newH/newW) {
+                graphEdge = h;
+                pageEdge = newH;
+            }
+     
+            return pageEdge / graphEdge;
+        }
+    }
+}
\ No newline at end of file

Added: cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Images.as
===================================================================
--- cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Images.as         
                (rev 0)
+++ cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Images.as 
2010-11-16 23:15:32 UTC (rev 22875)
@@ -0,0 +1,90 @@
+/*
+  This file is part of Cytoscape Web.
+  Copyright (c) 2009, The Cytoscape Consortium (www.cytoscape.org)
+
+  The Cytoscape Consortium is:
+    - Agilent Technologies
+    - Institut Pasteur
+    - Institute for Systems Biology
+    - Memorial Sloan-Kettering Cancer Center
+    - National Center for Integrative Biomedical Informatics
+    - Unilever
+    - University of California San Diego
+    - University of California San Francisco
+    - University of Toronto
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+package org.cytoscapeweb.util {
+    import flash.display.Bitmap;
+    import flash.display.BitmapData;
+    import flash.display.PixelSnapping;
+    import flash.geom.Matrix;
+    
+    public class Images {
+        
+        // ========[ CONSTRUCTOR 
]==================================================================
+        
+        /**
+         * This constructor will throw an error, as this is an abstract class. 
+         */
+        public function Images() {
+            throw new Error("This is an abstract class.");
+        }
+
+        // ========[ PUBLIC METHODS 
]===============================================================
+
+        public static function resizeBitmapToFit(bd:BitmapData, nw:Number, 
nh:Number):BitmapData {
+            if (bd.width > 0 && bd.height > 0) {
+                var w:Number = bd.width;
+                var h:Number = bd.height;
+                var originalRatio:Number = w/h;
+                var maxRatio:Number = nw/nh;
+                var scale:Number;
+                
+                if (originalRatio > maxRatio) { // scale by width
+                    scale = nw/w;
+                } else { // scale by height
+                    scale = nh/h;
+                }
+                
+                var m:Matrix = new Matrix();
+                m.scale(scale, scale);
+                m.translate(nw/2-(w*scale)/2, nh/2-(h*scale)/2);
+                
+                var bd2:BitmapData = new BitmapData(nw, nh, true, 0x000000);
+                bd2.draw(bd, m, null, null, null, true);
+    
+                var bmp:Bitmap = new Bitmap(bd2, PixelSnapping.NEVER, true);
+                
+                return bmp.bitmapData;
+            }
+            
+            return bd;
+        }
+    
+//        private function resizeBitmap(bd:BitmapData, 
scale:Number):BitmapData {   
+//            var matrix:Matrix = new Matrix();
+//            matrix.scale(scale, scale);
+//            
+//            var bd2:BitmapData = new BitmapData(bd.width * scale, bd.height 
* scale, true, 0x000000);
+//            bd2.draw(bd, matrix, null, null, null, true);
+//
+//            var bmp:Bitmap = new Bitmap(bd2, PixelSnapping.NEVER, true);
+//            
+//            return bmp.bitmapData;
+//        }
+    }
+}
\ No newline at end of file

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ApplicationMediator.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ApplicationMediator.as
    2010-11-16 21:47:02 UTC (rev 22874)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ApplicationMediator.as
    2010-11-16 23:15:32 UTC (rev 22875)
@@ -51,6 +51,7 @@
        
        import org.cytoscapeweb.ApplicationFacade;
        import org.cytoscapeweb.model.converters.PDFExporter;
+       import org.cytoscapeweb.model.converters.SVGExporter;
        import org.cytoscapeweb.model.data.VisualStyleVO;
        import org.cytoscapeweb.model.methods.$;
        import org.cytoscapeweb.util.ExternalFunctions;
@@ -201,9 +202,10 @@
             application.graphBox.setStyle("backgroundColor", bgColor);
         }
         
-        public function getGraphImage(type:String="png", width:Number=0, 
height:Number=0):ByteArray {
-            var bytes:ByteArray;
+        public function getGraphImage(type:String="png", width:Number=0, 
height:Number=0):* {
+            var image:*;
             var scale:Number = graphProxy.zoom;
+            type = type != null ? type.toLowerCase() : "png";
 
             // Otherwise, it may draw the shapes incorrectly, or labels might 
have wrong alignment:
             if (scale !== 1) graphView.zoomTo(1);
@@ -221,22 +223,29 @@
                 source.draw(graphView.vis, matrix);
 
                 var encoder:PNGEncoder = new PNGEncoder();
-                bytes = encoder.encode(source);
-            } else {
-                // PDF:
-                var pdfConv:PDFExporter = new PDFExporter(graphView);
-                bytes = pdfConv.export(graphProxy.graphData,
+                image = encoder.encode(source);
+            } else if (type === "svg") {
+                var svgConv:SVGExporter = new SVGExporter(graphView);
+                image = svgConv.export(graphProxy.graphData,
                                        configProxy.visualStyle,
                                        configProxy.config,
                                        graphProxy.zoom,
                                        width,
                                        height);
+            } else {
+                // PDF:
+                var pdfExp:PDFExporter = new PDFExporter(graphView);
+                image = pdfExp.export(graphProxy.graphData,
+                                      configProxy.visualStyle,
+                                      configProxy.config,
+                                      graphProxy.zoom,
+                                      width,
+                                      height);
             }
             
             // Set previous scale:
             if (scale != graphProxy.zoom) graphView.zoomTo(scale);
-            
-            return bytes;
+            return image;
         }
         
         public function showPanZoomControl(visible:Boolean):void {

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ExternalMediator.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ExternalMediator.as   
    2010-11-16 21:47:02 UTC (rev 22874)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/ExternalMediator.as   
    2010-11-16 23:15:32 UTC (rev 22875)
@@ -428,12 +428,15 @@
             if (options == null) options = {};
             // TODO: Refactor - proxy should NOT use a mediator!!!
             var appMediator:ApplicationMediator = 
facade.retrieveMediator(ApplicationMediator.NAME) as ApplicationMediator;
-            var ba:ByteArray = appMediator.getGraphImage(format, 
options.width, options.height);
+            var img:* = appMediator.getGraphImage(format, options.width, 
options.height);
             
-            var encoder:Base64Encoder = new Base64Encoder();
-            encoder.encodeBytes(ba);
+            if (img is ByteArray) {
+                var encoder:Base64Encoder = new Base64Encoder();
+                encoder.encodeBytes(img);
+                img = encoder.toString();
+            }
 
-            return encoder.toString();
+            return "" + img;
         }
         
         private function exportNetwork(format:String, url:String, 
options:Object=null):void {

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/EdgeRenderer.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/EdgeRenderer.as
    2010-11-16 21:47:02 UTC (rev 22874)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/EdgeRenderer.as
    2010-11-16 23:15:32 UTC (rev 22875)
@@ -234,7 +234,7 @@
             points.start = sShaft.clone();
             points.end = eShaft.clone();
             points.c1 = e.shape != Shapes.LINE && op1 != null ? op1.clone() : 
null; // First control point of cubic bezier OR control point of quadratic 
bezier
-            points.c2 = e.shape != Shapes.LINE && op2 != null ? op2.clone() : 
null; // Second control point of cubic bezier
+            points.c2 = e.shape != Shapes.LINE && op2 != null && op2 != op1 ? 
op2.clone() : null; // Second control point of cubic bezier
             points.sourceArrow = saPoints != null ? saPoints.arrow : null;
             points.targetArrow = taPoints != null ? taPoints.arrow : null;
             points.sourceArrowJoint = saPoints != null ? saPoints.joint : null;

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
    2010-11-16 21:47:02 UTC (rev 22874)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
    2010-11-16 23:15:32 UTC (rev 22875)
@@ -35,7 +35,6 @@
        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Graphics;
-       import flash.display.PixelSnapping;
        import flash.display.Sprite;
        import flash.geom.Matrix;
        import flash.geom.Rectangle;
@@ -46,6 +45,7 @@
        import org.cytoscapeweb.ApplicationFacade;
        import org.cytoscapeweb.model.ConfigProxy;
        import org.cytoscapeweb.model.GraphProxy;
+       import org.cytoscapeweb.util.Images;
        import org.cytoscapeweb.util.NodeShapes;
        
 
@@ -163,7 +163,6 @@
                     var bd:BitmapData = _imgCache.getImage(url);
                     
                     if (bd != null) {
-                        var bmpSize:Number = Math.max(bd.height, bd.width);
                         var maxZoom:Number = configProxy.maxZoom;
                         
                         // Reduce the image, if it is too large, to avoid some 
rendering issues:
@@ -176,8 +175,9 @@
 //                            bmpSize = Math.max(bd.height, bd.width);
 //                        }
                         
-                        bd = resizeBitmapToFit(bd, size*maxZoom, size*maxZoom);
-                        bmpSize = Math.max(bd.height, bd.width);
+                        // TODO: only if "tofit_cropping" option:
+//                        bd = Images.resizeBitmapToFit(bd, size*maxZoom, 
size*maxZoom);
+                        var bmpSize:Number = Math.min(bd.height, bd.width);
                         
                         var scale:Number =  size/bmpSize;
 
@@ -194,45 +194,5 @@
                 }
             }
         }
-        
-        private function resizeBitmapToFit(bd:BitmapData, nw:Number, 
nh:Number):BitmapData {
-            if (bd.width > 0 && bd.height > 0) {
-                var w:Number = bd.width;
-                var h:Number = bd.height;
-                var originalRatio:Number = w/h;
-                var maxRatio:Number = nw/nh;
-                var scale:Number;
-                
-                if (originalRatio > maxRatio) { // scale by width
-                    scale = nw/w;
-                } else { // scale by height
-                    scale = nh/h;
-                }
-                
-                var m:Matrix = new Matrix();
-                m.scale(scale, scale);
-                m.translate(nw/2-(w*scale)/2, nh/2-(h*scale)/2);
-                
-                var bd2:BitmapData = new BitmapData(nw, nh, true, 0x000000);
-                bd2.draw(bd, m, null, null, null, true);
-    
-                var bmp:Bitmap = new Bitmap(bd2, PixelSnapping.NEVER, true);
-                return bmp.bitmapData;
-            }
-            
-            return bd;
-        }
-        
-//        private function resizeBitmap(bd:BitmapData, 
scale:Number):BitmapData {   
-//            var matrix:Matrix = new Matrix();
-//            matrix.scale(scale, scale);
-//            
-//            var bd2:BitmapData = new BitmapData(bd.width * scale, bd.height 
* scale, true, 0x000000);
-//            bd2.draw(bd, matrix, null, null, null, true);
-//
-//            var bmp:Bitmap = new Bitmap(bd2, PixelSnapping.NEVER, true);
-//            
-//            return bmp.bitmapData;
-//        }
     }
 }
\ No newline at end of file

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to