From 96f889ca415c954b9701d435053f325697358637 Mon Sep 17 00:00:00 2001 From: tdhsmith Date: Tue, 4 Feb 2014 18:19:33 -0600 Subject: [PATCH] Added basic functionality for image nodes Extended the drawNode function to include basic image drawing. Adjusted the height and width functions to accommodate natural image dimensions as well as those set with node.data.image.width etc. --- springyui.js | 116 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 24 deletions(-) diff --git a/springyui.js b/springyui.js index ae2bf90..7c3470a 100755 --- a/springyui.js +++ b/springyui.js @@ -1,5 +1,5 @@ /** -Copyright (c) 2010 Dennis Hotson +Copyright (c) 2010 Dennis Hotson, 2014 Taylor Smith Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,6 +33,7 @@ jQuery.fn.springy = function(params) { var repulsion = params.repulsion || 400.0; var damping = params.damping || 0.5; var nodeSelected = params.nodeSelected || null; + var nodeImages = {}; var canvas = this[0]; var ctx = canvas.getContext("2d"); @@ -121,26 +122,62 @@ jQuery.fn.springy = function(params) { dragged = null; }); - Springy.Node.prototype.getWidth = function() { - var text = (this.data.label !== undefined) ? this.data.label : this.id; - if (this._width && this._width[text]) - return this._width[text]; + var getTextWidth = function(node) { + var text = (node.data.label !== undefined) ? node.data.label : node.id; + if (node._width && node._width[text]) + return node._width[text]; ctx.save(); - ctx.font = (this.data.font !== undefined) ? this.data.font : nodeFont; - var width = ctx.measureText(text).width + 10; + ctx.font = (node.data.font !== undefined) ? node.data.font : nodeFont; + var width = ctx.measureText(text).width; ctx.restore(); - this._width || (this._width = {}); - this._width[text] = width; + node._width || (node._width = {}); + node._width[text] = width; return width; }; - Springy.Node.prototype.getHeight = function() { - return 20; + var getTextHeight = function(node) { + return 16; + // In a more modular world, this would actually read the font size, but I think leaving it a constant is sufficient for now. + // If you change the font size, I'd adjust this too. }; + var getImageWidth = function(node) { + var width = (node.data.image.width !== undefined) ? node.data.image.width : nodeImages[node.data.image.src].object.width; + return width; + } + + var getImageHeight = function(node) { + var height = (node.data.image.height !== undefined) ? node.data.image.height : nodeImages[node.data.image.src].object.height; + return height; + } + + Springy.Node.prototype.getHeight = function() { + var height; + if (this.data.image == undefined) { + height = getTextHeight(this); + } else { + if (this.data.image.src in nodeImages && nodeImages[this.data.image.src].loaded) { + height = getImageHeight(this); + } else {height = 10;} + } + return height; + } + + Springy.Node.prototype.getWidth = function() { + var width; + if (this.data.image == undefined) { + width = getTextWidth(this); + } else { + if (this.data.image.src in nodeImages && nodeImages[this.data.image.src].loaded) { + width = getImageWidth(this); + } else {width = 10;} + } + return width; + } + var renderer = this.renderer = new Springy.Renderer(layout, function clear() { ctx.clearRect(0,0,canvas.width,canvas.height); @@ -173,11 +210,14 @@ jQuery.fn.springy = function(params) { // Figure out how far off center the line should be drawn var offset = normal.multiply(-((total - 1) * spacing)/2.0 + (n * spacing)); + var paddingX = 6; + var paddingY = 6; + var s1 = toScreen(p1).add(offset); var s2 = toScreen(p2).add(offset); - var boxWidth = edge.target.getWidth(); - var boxHeight = edge.target.getHeight(); + var boxWidth = edge.target.getWidth() + paddingX; + var boxHeight = edge.target.getHeight() + paddingY; var intersection = intersect_line_box(s1, s2, {x: x2-boxWidth/2.0, y: y2-boxHeight/2.0}, boxWidth, boxHeight); @@ -249,11 +289,18 @@ jQuery.fn.springy = function(params) { ctx.save(); - var boxWidth = node.getWidth(); - var boxHeight = node.getHeight(); + // Pulled out the padding aspect sso that the size functions could be used in multiple places + // These should probably be settable by the user (and scoped higher) but this suffices for now + var paddingX = 6; + var paddingY = 6; + + var contentWidth = node.getWidth(); + var contentHeight = node.getHeight(); + var boxWidth = contentWidth + paddingX; + var boxHeight = contentHeight + paddingY; // clear background - ctx.clearRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20); + ctx.clearRect(s.x - boxWidth/2, s.y - boxHeight/2, boxWidth, boxHeight); // fill background if (selected !== null && selected.node !== null && selected.node.id === node.id) { @@ -263,15 +310,36 @@ jQuery.fn.springy = function(params) { } else { ctx.fillStyle = "#FFFFFF"; } - ctx.fillRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20); - - ctx.textAlign = "left"; - ctx.textBaseline = "top"; - ctx.font = (node.data.font !== undefined) ? node.data.font : nodeFont; - ctx.fillStyle = "#000000"; - var text = (node.data.label !== undefined) ? node.data.label : node.id; - ctx.fillText(text, s.x - boxWidth/2 + 5, s.y - 8); + ctx.fillRect(s.x - boxWidth/2, s.y - boxHeight/2, boxWidth, boxHeight); + if (node.data.image == undefined) { + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + ctx.font = (node.data.font !== undefined) ? node.data.font : nodeFont; + ctx.fillStyle = "#000000"; + var text = (node.data.label !== undefined) ? node.data.label : node.id; + ctx.fillText(text, s.x - contentWidth/2, s.y - contentHeight/2); + } else { + // Currently we just ignore any labels if the image object is set. One might want to extend this logic to allow for both, or other composite nodes. + var src = node.data.image.src; // There should probably be a sanity check here too, but un-src-ed images aren't exaclty a disaster. + if (src in nodeImages) { + if (nodeImages[src].loaded) { + // Our image is loaded, so it's safe to draw + ctx.drawImage(nodeImages[src].object, s.x - contentWidth/2, s.y - contentHeight/2, contentWidth, contentHeight); + } + }else{ + // First time seeing an image with this src address, so add it to our set of image objects + // Note: we index images by their src to avoid making too many duplicates + nodeImages[src] = {}; + var img = new Image(); + nodeImages[src].object = img; + img.addEventListener("load", function () { + // HTMLImageElement objects are very finicky about being used before they are loaded, so we set a flag when it is done + nodeImages[src].loaded = true; + }); + img.src = src; + } + } ctx.restore(); } );