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.