Embedding RGraph

141 views
Skip to first unread message

Chris Adams

unread,
Nov 4, 2008, 7:53:57 PM11/4/08
to JavaScript Information Visualization Toolkit
First, I'd just like to thank Nick for producing such a handy bit of
code. I'm using it to visualize relationships between some of our data
and this is definitely going to be quite popular.

I'm embedding an RGraph instance in a YUI Layout Manager (http://
developer.yahoo.com/yui/layout/). This works really well for our
application with various expandable side panels but broke the node
label positioning code, apparently because the content is now nested
inside an absolute positioned div. To get the positioning to work
correctly I had to edit the labelPos calculation in plotLabel() to
remove the "+ cpos.x|y" because otherwise the labels were offset by
the #absolute div's position on the page. Since it's working now I
have time to discuss the question of what the cleanest way of doing
this is - to me, it's not immediately clear that this code was
necessary since it should be possible to position relative to the
canvas object but I haven't spent much time looking at the code.

Similarly, plotLabel also had some code which set the label div's
width / height using the node's radius. In my case this produced some
nasty bleed-through from the underlying lines. I'm not sure why this
code exists:
tag.style.width = size + 'px';
tag.style.height = size + 'px';

So - the last question regarded the biggest problem: updating the
graph when its container changes size. Currently this has an ugly
hack: in the Canvas function I added a ctx.save() immediately
before .setPosition() so I can call restore() to restore the
untranslated coordinates. My resize event handler looks something like
this:

var cc = this.layout.getUnitById('content_col');
EmbeddedGraph.canvas.canvas.height = cc.get('height');
EmbeddedGraph.canvas.canvas.width = cc.get('width');

EmbeddedGraph.canvas.ctx.restore();
EmbeddedGraph.canvas.setPosition();
EmbeddedGraph.canvas.translateToCenter();
EmbeddedGraph.canvas.clear();
EmbeddedGraph.canvas.ctx.strokeStyle =
EmbeddedGraph.canvas.strokeStyle;
EmbeddedGraph.canvas.ctx.fillStyle =
EmbeddedGraph.canvas.fillStyle;

This works fairly well but it seems like this logic should move into
RGraph and that gets to a stylistic question: does it make sense to
continue restoring to the pristine original or perhaps switching a re-
basing approach which would simply work out the relative offset
between the old and new canvas rect and .translate() appropriately?
That might avoid a redraw but I'm inclined to say any performance
benefit isn't worth it.

Nico Garcia Belmonte

unread,
Nov 5, 2008, 1:24:29 PM11/5/08
to javascript-information...@googlegroups.com
Hi,

Thanks for you message.

One of the reasons why this toolkit is still in alpha it's because I'm
not convinced about the "Canvas" class implementation.
I already had a similar conversation with the user "rakugo" in this
group, and he suggest me to add relative positioning for labels in
visualizations: that way changing the canvas position would also
change the labels position, without having to refresh the
visualization.
You also mentioned another problem, which is resizing.

One of the thoughts I had, was to make a Canvas implementation that
would be used like this:

var canvas = new Canvas('canvas_id', {
background:false,
style: { some style properties },
id: 'some div element where this object would be appended'
});

This constructor would generate a canvas instance and some DOM code
that would look like this:
<div id='container_canvas_id'>
<div id='label_canvas_id'></div>
<canvas id='canvas_id'></canvas>
</div>

This would also add relative positioning to the container_canvas_id,
set height=0 for the label container and same width to the canvas
object and to the label container.
The container_canvas_id would also have same width and height as the
canvas (or not, depending if later on one would like to add the
feature of a scrolling canvas inside a div, although I haven't though
enough about that, so a first version would set same width height for
canvas_id and container_canvas_id).

That code would be automatically appended to the id: 'some div
element where this object would be appended' DOM object.

This would require some changes in the visualizations, like changing
absolute positioning calculations with relative positioning
calculations for labels.

Also, if "background" setted to true, a second canvas object would be
appended, and this object would plot a background figure (like the
concentric circles). I propose this because I've noticed that there
are a lot of performance improvements if you turn off the
"drawConcentricCircles" property for the Config object.

So, one of the things that has been holding me back in order to
implement this, is that I've read in the excanvas group that excanvas
wouldn't work if the canvas element wasn't already appended in the DOM
at start.
On the other hand, this ""design"" might solve the positioning/moving
problem, but it might not solve the "resizing/scrolling" issue.

I still think that reseting the canvas and replotting the
visualization is still the best way to handle resizing. I still have
to think how to abstract this into the canvas class and make it more
usable.

It's definitely going to be something to work on the next release, and
any comment/suggestion/idea that you may have in order to handle this
problem (being on the design or implementation side) will be very well
appreciated.

Bye!

--
I would never die for my beliefs because I might be wrong.

Bertrand Russell

Chris Adams

unread,
Nov 5, 2008, 3:18:18 PM11/5/08
to JavaScript Information Visualization Toolkit
On Nov 5, 1:24 pm, "Nico Garcia Belmonte" <phil...@gmail.com> wrote:
> One of the thoughts I had, was to make a Canvas implementation that
> would be used like this:
...
> That code would be automatically appended to  the id: 'some div
> element where this object would be appended' DOM object.

From my perspective this would definitely be an improvement - it'd be
really nice if you could simply position the element on the page where
you want it have have a simple way to say "Draw stuff in #target". One
question I've been having is whether it might make sense to switch the
actual graphics code to something like dojox.gfx to get the free
support for VML, SVG, Silverlight, etc. and hopefully avoid the
excanvas support / compatibility issues which aren't interesting in
the context of working on the actual layout algorithm which is the
most interesting part.

On the subject of drawing, I just squashed a minor bug in Rgraph.js:
Canvas.clear() doesn't completely clear the canvas if x < y because it
uses .x for both the x and y values:

clear: function () {
this.ctx.clearRect(-this.getSize().x / 2, -this.getSize().x / 2,
this.getSize().x, this.getSize().x);
},

I replaced that with this:

clear: function () {
var s = this.getSize();
this.ctx.clearRect(-.5 * s.x, -.5 * s.y, s.x, s.y);
},

Regards,
Chris

rakugo

unread,
Nov 6, 2008, 5:01:10 AM11/6/08
to JavaScript Information Visualization Toolkit
Yep I've done something similar to allow me to associate a div with
the RGraph (code below).
On a side note Nico, have you considered popping the config values
into the controller? It seems to make a lot more sense to me, as I can
change them easily but it also allows you to have multiple graphs on
a page with different properties. You need to run the IE canvas hack
however in IE to make sure the newly created canvas is usable.

_init_html_elements: function(wrapperID){
if(!document.getElementById(wrapperID)){ throw (wrapperID + " html
element doesn't exist");}
this.wrapperID = wrapperID;
this.canvasID = wrapperID + "_canvas"; //the canvas object ID
this.labelContainer = wrapperID + "_label_container";
this.nodeLabelPrefix = this.canvasID +"_";
/*setup the divs */
var wrapper = document.getElementById(wrapperID);
wrapper.onmousedown = this.wrapperonmousedown;

if(!wrapper.style.height){wrapper.style.height = "200px";}
if(!wrapper.style.width){wrapper.style.width = "200px";}

var labelContainer = document.createElement("div");
labelContainer.id=this.labelContainer;

var canvas = document.createElement("canvas");
canvas.id = this.canvasID;
canvas.width = parseInt(wrapper.style.width);
canvas.height =parseInt(wrapper.style.height);

wrapper.appendChild(labelContainer);
wrapper.appendChild(canvas);
if(config.browser.isIE && G_vmlCanvasManager)
{G_vmlCanvasManager.init_(document);} //ie hack - needs changing to
work outside tw
},

Nico Garcia Belmonte

unread,
Nov 6, 2008, 4:11:18 PM11/6/08
to javascript-information...@googlegroups.com
@Chris,

Thanks for your bug fix.
I agree that it would be more fun to just implement and solve
visualization problems than to solve problems concerning browser
support.
However, I do believe that for the moment Dojo would provide a huge
abstraction layer, and that layer would introduce a couple of things I
don't like, like:

- Fully depend on Dojo for doing everything. Until now, the JIT has
been framework free, meaning that you can use most of the
visualizations together with other frameworks like Mootools, JQuery,
Prototype, Dojo, etc.
- Dojo would add an abstraction layer that probably would need a
redesign of the library. Also, I wouldn't feel good with that
abstraction layer, since this library needs to have good performance,
and the only way of achieving good performance is by hacking low level
javascript.

However, a couple of well tested low level functions for common
manipulations and a Class system would be very nice. I'm thinking
about Mootools Core.
With each release of the library I'm changing little portions of the
code to make that transition easier when the time comes. That's why,
for example, the RGraph and Hypertree codes are very similar.

I think that I'll change this for version 1.1. Until then, I'll focus
on stability, testing and usability, and not adding more features
(which is very tempting indeed).

@Rakugo,

I thought about that indeed. The Canvas and the Config issues should
be solved in next releases.
The main issue with the Config object is that objects like GraphUtil
use it, and passing the config object as parameter when calling those
methods isn't a good pattern.
That problem is solved in GraphPlot and GraphOp, since those objects
take the visualization object as formal parameter and not the graph.
Perhaps I should implement something similar to GraphPlot for
GraphUtil.

--

Reply all
Reply to author
Forward
0 new messages