Author: Christian Lopes
Date: 2010-10-26 16:28:16 -0700 (Tue, 26 Oct 2010)
New Revision: 22485

Added:
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/ImageCache.as
Modified:
   cytoscapeweb/trunk/cytoscapeweb/html-template/sample.html
   
cytoscapeweb/trunk/cytoscapeweb/src-test/org/cytoscapeweb/model/data/DiscreteVizMapperVOTest.as
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/data/DiscreteVizMapperVO.as
   cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Nodes.as
   cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/VisualProperties.as
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/components/GraphVis.as
   
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
Log:
Draft implementation of custom images on nodes (see feature request number 
2395).

Modified: cytoscapeweb/trunk/cytoscapeweb/html-template/sample.html
===================================================================
--- cytoscapeweb/trunk/cytoscapeweb/html-template/sample.html   2010-10-26 
23:08:37 UTC (rev 22484)
+++ cytoscapeweb/trunk/cytoscapeweb/html-template/sample.html   2010-10-26 
23:28:16 UTC (rev 22485)
@@ -33,6 +33,7 @@
                         size: { defaultValue: 20, continuousMapper: { 
attrName: "weight",  minValue: 20, maxValue: 40 } },
                         borderWidth: 2,
                         borderColor: "#707070",
+                        image: 
"http://upload.wikimedia.org/wikipedia/commons/2/2a/Wikipe-tan_in_navy_uniform2_transparent.png",//"http://chart.apis.google.com/chart?chs=300x300&cht=p&chd=e0:U-gh..bR";,
                         labelFontSize: { defaultValue: 12, continuousMapper: { 
attrName: "weight",  minValue: 10, maxValue: 24 } },
                         tooltipText: { customMapper: { functionName: 
"onNodeTooltip" } },
                         selectionGlowOpacity: 0,

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/data/DiscreteVizMapperVO.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/data/DiscreteVizMapperVO.as
      2010-10-26 23:08:37 UTC (rev 22484)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/model/data/DiscreteVizMapperVO.as
      2010-10-26 23:28:16 UTC (rev 22485)
@@ -39,6 +39,24 @@
 
                // ========[ PUBLIC PROPERTIES 
]============================================================
                
+               public function get distinctValues():Array {
+                   var values:Array = [];
+                   var lookup:Object = {};
+                   
+                   if (_entries != null) {
+                       for (var k:* in _entries) {
+                       var v:* = _entries[k];
+                       
+                    if (!lookup[v]) {
+                        values.push(v);
+                        lookup[v] = true;
+                    }
+                       }
+                   }
+                   
+                   return values;
+               }
+               
                // ========[ CONSTRUCTOR 
]==================================================================
                
                public function DiscreteVizMapperVO(attrName:String, 
propName:String) {

Modified: cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Nodes.as
===================================================================
--- cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Nodes.as  
2010-10-26 23:08:37 UTC (rev 22484)
+++ cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/Nodes.as  
2010-10-26 23:28:16 UTC (rev 22485)
@@ -82,6 +82,7 @@
                     lineColor: lineColor, 
                     lineWidth: lineWidth,
                     alpha: alpha,
+                    "props.imageUrl": imageUrl,
                     visible: visible,
                     buttonMode: true,
                     filters: filters,
@@ -156,6 +157,12 @@
 
             return style.getValue(propName, n.data);
         }
+
+        public static function imageUrl(n:NodeSprite):String {
+            var propName:String = VisualProperties.NODE_IMAGE;
+            // TODO: selected/mouseover images
+            return style.getValue(propName, n.data);
+        }
         
         public static function selectionAlpha(n:NodeSprite):Number {
             var propName:String = VisualProperties.NODE_ALPHA;

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/VisualProperties.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/VisualProperties.as   
    2010-10-26 23:08:37 UTC (rev 22484)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/util/VisualProperties.as   
    2010-10-26 23:28:16 UTC (rev 22485)
@@ -57,6 +57,7 @@
         public static const NODE_ALPHA:String = "nodes.opacity";
         public static const NODE_LINE_COLOR:String = "nodes.borderColor";
         public static const NODE_LINE_WIDTH:String = "nodes.borderWidth";
+        public static const NODE_IMAGE:String = "nodes.image";
 
         public static const NODE_TOOLTIP_TEXT:String = "nodes.tooltipText";
         public static const NODE_TOOLTIP_FONT:String = "nodes.tooltipFont";

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/components/GraphVis.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/components/GraphVis.as
    2010-10-26 23:08:37 UTC (rev 22484)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/components/GraphVis.as
    2010-10-26 23:28:16 UTC (rev 22485)
@@ -52,6 +52,7 @@
     import flash.display.DisplayObject;
     import flash.geom.Point;
     import flash.geom.Rectangle;
+    import flash.utils.setTimeout;
     
     import org.cytoscapeweb.model.data.ConfigVO;
     import org.cytoscapeweb.model.data.VisualStyleVO;
@@ -71,6 +72,7 @@
     import org.cytoscapeweb.view.layout.PresetLayout;
     import org.cytoscapeweb.view.layout.RadialTreeLayout;
     import org.cytoscapeweb.view.layout.physics.Simulation;
+    import org.cytoscapeweb.view.render.ImageCache;
     import org.cytoscapeweb.view.render.Labeler;
     
 
@@ -187,48 +189,64 @@
             var firstTime:Boolean = this._style == null;
             this._style = style;
             
-            // Nodes & Edges properties:
-            // ---------------------------------------------------------
-            data.nodes.setProperties(Nodes.properties);
-            data.edges.setProperties(Edges.properties);
+            // Preload images:
+            var imgCache:ImageCache = ImageCache.instance;
+            imgCache.loadImages(style);
             
-            // Node labels:
-            // ---------------------------------------------------------
-            nodeLabeler.cacheText = false;
-            nodeLabeler.textMode = TextSprite.DEVICE;
-
-            nodeLabeler.fontName = Labels.labelFontName;
-            nodeLabeler.fontColor = Labels.labelFontColor;
-            nodeLabeler.fontSize = Labels.labelFontSize;
-            nodeLabeler.fontWeight = Labels.labelFontWeight;
-            nodeLabeler.fontStyle = Labels.labelFontStyle;
-            nodeLabeler.hAnchor = Labels.labelHAnchor;
-            nodeLabeler.vAnchor = Labels.labelVAnchor;
-            nodeLabeler.xOffsetFunc = Labels.labelXOffset;
-            nodeLabeler.yOffsetFunc = Labels.labelYOffset;
-            nodeLabeler.filters = Labels.filters;
-            nodeLabeler.textFunction = Labels.text;
+            if (imgCache.isLoading()) waitForImages();
+            else doSetStyle();
             
-            // Edge labels:
-            // ---------------------------------------------------------
-            edgeLabeler.textMode = TextSprite.DEVICE;
-
-            edgeLabeler.fontName = Labels.labelFontName;
-            edgeLabeler.fontColor = Labels.labelFontColor;
-            edgeLabeler.fontSize = Labels.labelFontSize;
-            edgeLabeler.fontWeight = Labels.labelFontWeight;
-            edgeLabeler.fontStyle = Labels.labelFontStyle;
-            edgeLabeler.filters = Labels.filters;
-            edgeLabeler.textFunction = Labels.text;
-
-            if (!firstTime) {
-                if (_config.nodeLabelsVisible) updateLabels(Data.NODES);
-                if (_config.edgeLabelsVisible) updateLabels(Data.EDGES);
+            function waitForImages():void {trace("%% waiting for images...");
+                setTimeout(function():void {
+                    if (imgCache.isLoading()) waitForImages();
+                    else doSetStyle();
+                }, 50);
             }
-
-            // Tooltips:
-            // ---------------------------------------------------------
-            tooltipControl.showDelay = 
_style.getValue(VisualProperties.TOOLTIP_DELAY) as Number;
+            
+            function doSetStyle():void {
+                // Nodes & Edges properties:
+                // ---------------------------------------------------------
+                data.nodes.setProperties(Nodes.properties);
+                data.edges.setProperties(Edges.properties);
+                
+                // Node labels:
+                // ---------------------------------------------------------
+                nodeLabeler.cacheText = false;
+                nodeLabeler.textMode = TextSprite.DEVICE;
+    
+                nodeLabeler.fontName = Labels.labelFontName;
+                nodeLabeler.fontColor = Labels.labelFontColor;
+                nodeLabeler.fontSize = Labels.labelFontSize;
+                nodeLabeler.fontWeight = Labels.labelFontWeight;
+                nodeLabeler.fontStyle = Labels.labelFontStyle;
+                nodeLabeler.hAnchor = Labels.labelHAnchor;
+                nodeLabeler.vAnchor = Labels.labelVAnchor;
+                nodeLabeler.xOffsetFunc = Labels.labelXOffset;
+                nodeLabeler.yOffsetFunc = Labels.labelYOffset;
+                nodeLabeler.filters = Labels.filters;
+                nodeLabeler.textFunction = Labels.text;
+                
+                // Edge labels:
+                // ---------------------------------------------------------
+                edgeLabeler.textMode = TextSprite.DEVICE;
+    
+                edgeLabeler.fontName = Labels.labelFontName;
+                edgeLabeler.fontColor = Labels.labelFontColor;
+                edgeLabeler.fontSize = Labels.labelFontSize;
+                edgeLabeler.fontWeight = Labels.labelFontWeight;
+                edgeLabeler.fontStyle = Labels.labelFontStyle;
+                edgeLabeler.filters = Labels.filters;
+                edgeLabeler.textFunction = Labels.text;
+    
+                if (!firstTime) {
+                    if (_config.nodeLabelsVisible) updateLabels(Data.NODES);
+                    if (_config.edgeLabelsVisible) updateLabels(Data.EDGES);
+                }
+    
+                // Tooltips:
+                // ---------------------------------------------------------
+                tooltipControl.showDelay = 
_style.getValue(VisualProperties.TOOLTIP_DELAY) as Number;
+            }
         }
 
         public function applyLayout(layoutObj:Object):Transition {

Added: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/ImageCache.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/ImageCache.as  
                            (rev 0)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/ImageCache.as  
    2010-10-26 23:28:16 UTC (rev 22485)
@@ -0,0 +1,161 @@
+/*
+  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.view.render {
+    import flash.display.BitmapData;
+    import flash.display.Loader;
+    import flash.events.Event;
+    import flash.events.IOErrorEvent;
+    import flash.net.URLRequest;
+    import flash.utils.Dictionary;
+    
+    import mx.utils.StringUtil;
+    
+    import org.cytoscapeweb.model.data.DiscreteVizMapperVO;
+    import org.cytoscapeweb.model.data.VisualPropertyVO;
+    import org.cytoscapeweb.model.data.VisualStyleVO;
+    import org.cytoscapeweb.util.VisualProperties;
+    import org.cytoscapeweb.util.methods.$each;
+    
+    
+    public class ImageCache {
+        
+        // ========[ CONSTANTS 
]====================================================================
+        
+        private const IMG_PROPS:Array = [ VisualProperties.NODE_IMAGE ];
+        
+        // ========[ PRIVATE PROPERTIES 
]===========================================================
+        
+        private var _images:Dictionary = new Dictionary();
+        
+        // ========[ PUBLIC METHODS 
]===============================================================
+        
+        public function isLoading():Boolean {
+            for (var url:String in _images) {
+                if (_images[url] === null) return true;
+            }
+            return false;
+        }
+        
+        public function isLoaded(url:String):Boolean {
+            url = normalize(url);
+            return _images[url] != null;
+        }
+        
+        public function contains(url:String):Boolean {
+            url = normalize(url);
+            return _images[url] !== undefined;
+        }
+        
+        public function loadImages(style:VisualStyleVO):void {
+            // First, clean the cache:
+            _images = new Dictionary();
+            
+            // Then load all distinct URL values:
+            $each(IMG_PROPS, function(i:uint, pname:String):Boolean { 
+                if (style.hasVisualProperty(pname)) {
+                    var vp:VisualPropertyVO = style.getVisualProperty(pname);
+                    // Default value:
+                    if (!contains(vp.defaultValue)) loadImage(vp.defaultValue);
+                    
+                    // Discrete Mapper values:
+                    var mapper:DiscreteVizMapperVO = vp.vizMapper as 
DiscreteVizMapperVO;
+                    if (mapper != null) {
+                        var values:Array = mapper.distinctValues;
+                        
+                        $each(IMG_PROPS, function(j:uint, url:String):Boolean {
+                           if (!contains(url)) loadImage(url);
+                           return false; 
+                        });
+                    }
+                }
+                return false;
+            });
+        }
+        
+        public function getImage(url:String):BitmapData {
+            return _images[normalize(url)];
+        }
+        
+        public function loadImage(url:String):void {
+            url = normalize(url);
+            var bmp:BitmapData;
+            
+            if (url.length > 0) {
+                _images[url] = null; // this flags the image state as "loading"
+                
+                var urlRequest:URLRequest = new URLRequest(url);
+                var loader:Loader = new Loader();
+                
+                loader.contentLoaderInfo.addEventListener(Event.COMPLETE, 
function(e:Event):void {trace(">> IMG LOADED: " + url);
+                    bmp = e.target.content.bitmapData;
+                    _images[url] = bmp;
+                });
+                
+                
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, 
function(e:IOErrorEvent):void {
+                    trace("NodeRenderer - Error loading image: " + e);
+                    // TODO: Do something else?
+                });
+                
+                loader.load(urlRequest);
+            }
+        }
+        
+        // ========[ PRIVATE METHODS 
]==============================================================
+        
+        /**
+         * It actually just trims the URL string or return an empty string if 
it is null;
+         */
+        private function normalize(url:String):String {
+            return url != null ? StringUtil.trim(url) : "";
+        }
+        
+        // ========[ SINGLETON STUFF 
]==============================================================
+
+        private static var _instance:ImageCache;
+        
+        public function ImageCache(lock:SingletonLock) {
+            if (lock == null) throw new Error("Invalid Singleton access. Use 
ImageCache.instance().");
+        }
+        
+        public static function get instance():ImageCache {
+            if (_instance == null) _instance = new ImageCache(new 
SingletonLock());
+            return _instance;
+        }
+
+    }
+}
+
+/**
+ * This is a private class declared outside of the package that is only 
accessible 
+ * to classes inside of the Model.as file.
+ * Because of that, no outside code is able to get a reference to this class 
to pass
+ * to the constructor, which enables us to prevent outside instantiation.
+ */
+class SingletonLock { }
\ No newline at end of file

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
    2010-10-26 23:08:37 UTC (rev 22484)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src/org/cytoscapeweb/view/render/NodeRenderer.as
    2010-10-26 23:28:16 UTC (rev 22485)
@@ -32,22 +32,34 @@
        import flare.vis.data.DataSprite;
        import flare.vis.data.render.ShapeRenderer;
        
+       import flash.display.BitmapData;
        import flash.display.Graphics;
+       import flash.geom.Matrix;
        import flash.geom.Rectangle;
+       import flash.utils.setTimeout;
        
+       import mx.utils.StringUtil;
+       
        import org.cytoscapeweb.util.NodeShapes;
        
 
     public class NodeRenderer extends ShapeRenderer {
        
         private static var _instance:NodeRenderer = new NodeRenderer();
-        /** Static AppNodeRenderer instance. */
-        public static function get instance():NodeRenderer { return _instance; 
}        
+        public static function get instance():NodeRenderer { return _instance; 
}
 
+        // ========[ CONSTRUCTOR 
]==================================================================
+
         public function NodeRenderer(defaultSize:Number = 6) {
             this.defaultSize = defaultSize;
         }
         
+        // ========[ PRIVATE PROPERTIES 
]===========================================================
+        
+        private var _imgCache:ImageCache = ImageCache.instance;
+        
+        // ========[ PUBLIC METHODS 
]===============================================================
+        
         /** @inheritDoc */
         public override function render(d:DataSprite):void {
             var lineAlpha:Number = d.lineAlpha;
@@ -56,16 +68,30 @@
             
             var g:Graphics = d.graphics;
             g.clear();
+            
+            if (lineAlpha > 0 && d.lineWidth > 0) {
+                var pixelHinting:Boolean = d.shape === 
NodeShapes.ROUND_RECTANGLE;
+                g.lineStyle(d.lineWidth, d.lineColor, lineAlpha, pixelHinting);
+            }
+            
             if (fillAlpha > 0) {
+                // 1. Draw the background color:
                 // Using a bit mask to avoid transparent mdes when 
fillcolor=0xffffffff.
                 // See https://sourceforge.net/forum/message.php?msg_id=7393265
                 g.beginFill(0xffffff & d.fillColor, fillAlpha);
+                drawShape(d, size);
+                g.endFill();
+                
+                // 2. Draw an image on top:
+                drawImage(d, size);
             }
-            if (lineAlpha > 0 && d.lineWidth > 0) {
-                var pixelHinting:Boolean = d.shape === 
NodeShapes.ROUND_RECTANGLE;
-                g.lineStyle(d.lineWidth, d.lineColor, lineAlpha, pixelHinting);
-            }
-
+        }
+        
+        // ========[ PRIVATE METHODS 
]==============================================================
+        
+        private function drawShape(d:DataSprite, size:Number):void {
+            var g:Graphics = d.graphics;
+            
             switch (d.shape) {
                 case null:
                     break;
@@ -89,8 +115,47 @@
                 default:
                     Shapes.drawCircle(g, size/2);
             }
+        }
+        
+        private function drawImage(d:DataSprite, size:Number):void {
+            var url:String = d.props.imageUrl;
             
-            if (fillAlpha > 0) g.endFill();
-        }       
+            if (size > 0 && url != null && StringUtil.trim(url).length > 0) {
+                // Load the image into the cache first?
+                if (!_imgCache.contains(url)) {trace("Will load IMAGE...");
+                    _imgCache.loadImage(url);
+                }
+                if (_imgCache.isLoaded(url)) {trace("% LOADED :-)"); 
+                    draw();
+                } else {trace("% NOT loaded :-("); 
+                    drawWhenLoaded();
+                }
+
+                function drawWhenLoaded():void {
+                    setTimeout(function():void {trace("TIMEOUT: Checking 
again...");
+                        if (_imgCache.isLoaded(url)) draw();
+                        else drawWhenLoaded();
+                    }, 50);
+                }
+                
+                function draw():void {trace("Will DRAW...");
+                    // Get the image from cache:
+                    var bmp:BitmapData = _imgCache.getImage(url);
+                    
+                    if (bmp != null) {
+                        var maxBmpSize:Number = Math.max(bmp.height, 
bmp.width);
+                        var scale:Number =  size/maxBmpSize;
+    
+                        var m:Matrix = new Matrix();
+                        m.scale(scale, scale);
+                        m.translate(-size/2, -size/2);
+    
+                        d.graphics.beginBitmapFill(bmp, m, false, true);
+                        drawShape(d, size);
+                        d.graphics.endFill();
+                    }
+                }
+            }
+        }
     }
 }
\ No newline at end of file

Modified: 
cytoscapeweb/trunk/cytoscapeweb/src-test/org/cytoscapeweb/model/data/DiscreteVizMapperVOTest.as
===================================================================
--- 
cytoscapeweb/trunk/cytoscapeweb/src-test/org/cytoscapeweb/model/data/DiscreteVizMapperVOTest.as
     2010-10-26 23:08:37 UTC (rev 22484)
+++ 
cytoscapeweb/trunk/cytoscapeweb/src-test/org/cytoscapeweb/model/data/DiscreteVizMapperVOTest.as
     2010-10-26 23:28:16 UTC (rev 22485)
@@ -30,7 +30,6 @@
 package org.cytoscapeweb.model.data {
        import flexunit.framework.TestCase;
        
-       import org.cytoscapeweb.model.methods.error;
        import org.cytoscapeweb.util.VisualProperties;
        
        
@@ -79,6 +78,20 @@
             assertUndefined(_mapper1.getValue({}));
         }
         
+        public function testDistinctValues():void {
+            var m:DiscreteVizMapperVO = new DiscreteVizMapperVO("attrName", 
VisualProperties.NODE_IMAGE);
+            m.addEntry(1, "img1");
+            m.addEntry(2, "IMG1");
+            m.addEntry(3, "img2");
+            m.addEntry(4, "img3");
+            m.addEntry(5, "img3");
+            m.addEntry(6, "img1");
+            m.addEntry(7, "img1");
+            
+            var distinct:Array = m.distinctValues;
+            assertEquals(4, distinct.length);
+        }
+        
         public function testToObject():void {
             _mapper1.addEntry("A", 
VisualProperties.parseValue(_mapper1.propName, "#ff0000"));
             _mapper1.addEntry("B", 
VisualProperties.parseValue(_mapper1.propName, "0000ff"));

-- 
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