Announcing Bluff, plus a few other project updates

I put out a few new software releases over the last few days, and thought I’d gather them into one post rather than lots of little ones. Let’s start with the biggest.

Update: for those wishing to contribute bug reports (as failing test cases) and patches, Bluff is hosted on Github.

In what can only be described as a colossal case of wheel reinvention, I’ve ported Geoffrey Grosenbach’s Gruff graphing library to JavaScript and called it Bluff. My major gripe with current JavaScript graphing libraries is that they either have too large a dependency requirement (i.e. they depend on one of the large JS frameworks or on Flash), or they are large themselves and kinda unwieldy to use. Bluff only requires a copy of JS.Class to work, and that’s only 2kb on the wire. Bluff itself is 8kb, plus 4kb for ExCanvas if you want your graphs to work in IE. I actually went to the trouble of writing a compiler that would take JS.Class-based code and generate ‘raw’ JavaScript from it, but the output from that ended up being twice the size of the JS.Class version and didn’t run fast enough to make up the difference so I’m keeping the JS.Class requirement for now.

Why did I do this? About a year ago I wrote an app called DesalData for my then employer, and it has a section that lets you search for water plants from a database of ~15,000 entries using a stack of filters and graph data from them in real time, and it lets you save those graphs to your machine (you can’t see it without a login, sorry). I was doing the site in Rails so I figured I’d use Gruff, but we started it off on shared hosting, so I didn’t want the server to deal with generating all those images on top of the database load. So, I had to generate graphs on the client but keep the ability to generate identical graphs on the server for download if required. So, I stuck with Gruff and reimplemented it on the client — the client’s machine handles most of the rendering so the server’s workload is a little lighter and the app is more responsive.

(Yes, 15,000 entries doesn’t sound like much but the data model for this thing involves some very wide tables and lots of joins. Getting this to perform decently involved patching Rails to support multiple SQL calculations on one query, but that’s another story.)

Anyway, this release is a complete rewrite (my original version for DesalData is closed-source and incomplete), and is pretty much equivalent to Gruff 0.3.4, the current release. A few graph types (the ones that use images) are missing, but they’re kinda niche and I’d rather keep this lightweight. Give it a whirl and let me know what you think.

The second announcement concerns JS.Class, in that version 2.0.1 is out. It’s a very minor bug fix release and should be a drop-in replacement for 2.0.0, so go ahead and upgrade straight away. The fixes include use of strict equality (=== rather than ==) throughout, and a fix in Module#include that fixes this very rare edge case:

M = new JS.Module({
  extend: {
    included: function(base) {
      // Redefine include() on the including class
      base.extend({ include: function() { /* ... */ } });
    }
  }
});

C = new JS.Class({
  include: M,
  foo: function() { /* ... */ }
});

2.0.1 makes sure that the M.included hook’s redefinition of base.include takes effect before the class’ own methods are added to it — like I said, edge case but I ran into it recently to now it’s fixed.

My final announcements are a couple of new Ruby gems that I’m using to build my JavaScript projects these days. oyster is a really simple commandline option parser, inspired by Trollop but with some subtle differences, and jake is an automated build tool that merges and compresses JavaScript files using PackR and lets you use ERB snippets and helper functions in source files. I won’t say much about either, as I’m only really releasing them so my colleagues can build my projects on their own machines, but do feel free to consult their READMEs and gem install either to have a play around. Any bug reports are, as always, gratefully received.







31 Responses to “Announcing Bluff, plus a few other project updates”

I’m flattered!

Great work. It’s a great time for client-side graphs now that more browsers are beginning to support the canvas element.

Geoffrey Grosenbach added these pithy words on Sep 16 08 at 5:30 pm

Bluff is AWESOME.

I did notice one bug (which I worked around by clever use of minimum_value/maximum_value) that appears to be a rounding issue.

See how hippos datapoints hover around 1.0, and giraffes around 2.0? The graphs show this correctly, but the y-axis labels seem to have been rounded, making it appear that everything is shifted 0.5 (hippos to 1.5 and giraffes to 2.5):

var g = new Bluff.Line(’animals’, 600);
g.theme_keynote();
g.minimum_value = 0.5;
g.maximum_value = 4.5;
g.markers = 4;
g.title = ‘Animals’;

g.data(’giraffes’, [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
2.0, 2.0, 2.08, 2.04, 1.77, 2.08, 2.12]);
g.data(’monkeys’, [3.97, 2.24, 2.81, 3.2, 1.11, 2.98,
2.33, 1.89, 3.82, 1.34, 1.87, 1.49, 1.54, 2.45, 1.97]);
g.data(’hippos’, [0.88, 1.00, 1.00, 1.0, 1.0, 1.0, 1.0, 1.06,
1.17, 1.02, 1, 1.01, 1.14, 1.09]);

g.draw();

Jack added these pithy words on Sep 16 08 at 10:33 pm

Very cool, I’ve been looking for something like this for ages. I too had the problem that rendering graphs serverside was taking up way too much resources.

Props for a job well done, this beats all the other JS graphing scripts due to its relatively small size.

One suggestion: mouseover values at each “plot” of the graph?

marcel added these pithy words on Sep 17 08 at 10:42 pm

Actually I just found out that you can’t set the height of the graph area? This, unfortunately makes bluff useless to me because it doesn’t integrate into my layouts at all (i need wide, thin graphs, i.e. 800×300).

PLEASE add a height attribute!

marcel added these pithy words on Sep 18 08 at 12:12 am

Actually, you can set the height, I just seem to have forgotten to put it in the docs… I’ll update ASAP. You can set the height like so:

g = new Bluff.Line(’example’, ‘800×300′);

James added these pithy words on Sep 18 08 at 6:57 am

Oh, how excellent! Thanks!

marcel added these pithy words on Sep 18 08 at 11:44 pm

Found another bug:

var g = new Bluff.Pie(’example’, 400);
g.theme_37signals();
g.title = ‘My Graph’;

g.data(”Apples”, 0);
g.data(”Oranges”, 2);
g.data(”Watermelon”, 8);
g.data(”Peaches”, 10);
g.draw();

Results in a pie chart with peaches filling up the whole circle. The zero for apples is being interpreted as an empty value at line 436:

data_points = data_points || [];

This empty data set is causing _sums_for_pie to calculate a NaN total sum at line 1487.

Workaround using brackets: g.data(”Apples”, [0]);

This Ruby idiom doesn’t travel so well in the javascript world when you are using dynamic parameters, unfortunately.

Dylan B added these pithy words on Sep 18 08 at 11:46 pm

Other than that, great little library :) I was just looking for a good graphing library and stumbled on this; thanks for saving me the work of having to write it myself.

Dylan B added these pithy words on Sep 18 08 at 11:47 pm

Here are some suggestions for new features, I hope you’ll implement at least a few of them ;)

-option to show dots on “area” graph!
-option for a top-line on “area” graph (basically overlay line graph on area graph)
-option for smaller dots
-option for thinner lines on “line” graph
-option for vertical grid (y axis grid)
-option for “dashed” grid lines
-equal grid thickness (some are thicker and a bit blurry)
-mouseover y values

marcel added these pithy words on Sep 19 08 at 1:15 am

Two bugs that are quite serious:

1) Y-axis and x-axis labels are for some reason offset by ~5 pixels to in my graph, not on par with the grid-lines.

2) When drawing another graph in the same canvas, the old y-axis and x-axis labels are NOT wiped and the new ones are just put right on top, creating an ugly text mess (it becomes unreadable).

Tested in Firefox 3 and Safari, it doesn’t not seem to be browser related.

Fix please!

marcel added these pithy words on Sep 21 08 at 1:57 am

Marcel,

1) Could you send me a link to the page you’re running the graph on so I can take a look? I can’t fix a bug if I can’t reproduce it.

2) You need to call clear() on the original graph to remove all the text nodes. There’s no way for the second graph to know that there’s already a graph on that canvas, so you need to clear the first one yourself.

James added these pithy words on Sep 21 08 at 9:38 am

Hi,

1) My apologies, turns out this was layout related. I had a padding-top on the canvas and the entire graph except the y-axis text was moved down.

2) I did just that, I called it right after the object was declared, but it had no effect, so maybe g.clear() is broken? Also, the “graph” part of the canvas IS cleared by default, why wouldn’t the text be cleared as well before it is rendered?

marcel added these pithy words on Sep 21 08 at 5:06 pm

Could you post the code you’re using? My suspicion is that you’re clearing the wrong object. Basically, each graph instance stores a list of references to the text nodes it creates, so that they can be removed. Any other graph objects, and the canvases they use, have no references to the text nodes and so cannot do anything about them (though I may make an attempt to work around this in a future release). You need to do something like this:

// First graph
var g = new Bluff.Line('example', 400);
// data, settings...
g.draw();

// Clear, then create a new graph
g.clear();
g = new Bluff.StackedBar('example', 400);
// continue as usual
James added these pithy words on Sep 21 08 at 5:26 pm

The problem is, I can’t reference the object again because I am dynamically drawing graphs (draw one, on click of a button draw a new one).

Yes I could probably work around this by using the window[] object to store a reference to it, but thats way too complicated considering that I just want to wipe the canvas!

Here’s what I’m using (simplified):

//draw graph
function drawGraph(interval) {
var g = new Bluff.Area(’graph’, ‘800×180′);

g.title = ‘Stock Performance’;
g.hide_title = ‘True’;
g.hide_legend = ‘True’;
g.marker_font_size = ‘11′;
g.minimum_value = ‘0′;
g.no_data_message = ‘No data’;

g.data(’Stock Value’, hourly.reverse());

g.labels = x_labels;
g.draw();
}

Buttons => drawGraph(interval);

It would be very helpful if Bluff just cleared the entire canvas before drawing (or if there was a function/option to do that).

marcel added these pithy words on Sep 21 08 at 5:58 pm

As I explained, this is currently not possible. The text is created using a stack of divs, they are not part of the canvas and need to be managed separately. The only object that can remove them is the one that created them, since that’s where the references are stored. Therefore, you’re going to need to keep a reference to the original graph. Your situation is easily fixed without creating extra global vars:

var Graph = {
    draw: function(interval) {
        if (this._currentGraph) this._currentGraph.clear();

        var g = new Bluff.Area('graph', '800x180');
        this._currentGraph = g;

        g.title = 'Stock Performance';
        g.hide_title = 'True';
        g.hide_legend = 'True';
        g.marker_font_size = '11';
        g.minimum_value = '0';
        g.no_data_message = 'No data';

        g.data('Stock Value', hourly.reverse());

        g.labels = x_labels;
        g.draw();
    }
}

Just call Graph.draw() and if a graph already exists, it will be cleared.

James added these pithy words on Sep 21 08 at 6:10 pm

Unfortunately that does not seem to work:

http://www.n8t.net/test.html

clear() is never called. I’ve never used self-made javascript classes before, so maybe I implemented it wrong?

marcel added these pithy words on Sep 21 08 at 8:53 pm

Thank you for pointing that out, that worked great!

I didn’t know you could assign a function/class to a variable and still call it normally, you javascript wiz you :)

marcel added these pithy words on Sep 21 08 at 10:11 pm

Hi James,

I’m currently working on a Drupal module for Bluff integration and while working on it I noticed a bit of an issue with the way Bluff injects the labels onto the page.

The labels are being injected outside of the canvas tags they apply to and position themselves via position: absolute. The problem this causes is that if you have any dynamic elements (such as collapsible fieldsets) or the users uses one of the many browser zoom functions the labels will no longer be in the correct place.

Is it at all possible that you inject the labels inside the canvases and just a combination of position: absolute and position: relative so that if the canvas moves so does the labels?

I’d be more than happy to help with this if you do so require it.

Thanks for the good work so far.

Cheers,
Decipher.

Decipher added these pithy words on Sep 24 08 at 3:06 am

I just tried that approach and the text does not display if added inside the canvas tag. I think I might be able to solve this by wrapping the canvas in an extra div to hold the text nodes, so they’re positioned relative to the wrapper rather than relative to the whole page.

I’ll post something here if and when I fix this.

James added these pithy words on Sep 24 08 at 8:49 am

Are you planning on releasing future versions that incorporate some of the features that were suggested?

And maybe a bug tracker and possibly releasing the source code (i.e. the file in readable format).

This project has SO much potential!

Jordan added these pithy words on Sep 25 08 at 4:22 am

I’ve already fixed some of the reported bugs from the first release, and there will probably be a bug fix release in the next week or so. I’ll post here when that happens. Some feature requests depend on whether I can add them to Gruff as well, since Bluff is supposed to be a faithful mirror of Gruff.

There’s no official bug tracker, but the project is on Github. Feel free to fork it and add failing test cases and fixes.

http://github.com/jcoglan/bluff

There’s a readable version in the download (bluff-src.js), and you can also browse the source through the git repo.

James added these pithy words on Sep 25 08 at 8:43 am

Hi,
i got a hidden Div on my page the canvas is in that. so when i building graph than the labels created calculate wrong offset.
How to create them after loading the page

AeUser added these pithy words on Oct 08 08 at 11:10 am

Hi,

Here is the link,

http://www.kailashyadav.com/bluff.php

My problem is that i am using two div’s and i am hiding them on clicking. The problem is that the Labels are not cleared when i am hiding the div in which the canvas is shown.

AeUser added these pithy words on Oct 08 08 at 12:57 pm

Hey James,

Did you able to review my problem? Actually i have to use these maps?if this problem can be resolved then tell me?
we can do it one way if you can tell how to add a classname to all the Div labels

AeUser added these pithy words on Oct 10 08 at 5:22 am

Hey,
Bluff is really nice. I like it.
Here’s a little suggestion:
add this line to Bluff.Renderer._text_node:
div.style.zIndex = this._canvas.style.zIndex;

this would be really usefull in some designs ;)

Ralf added these pithy words on Oct 14 08 at 7:57 am

Hey James,
i also figured out a little bug.
If a label for a data set is a longer string, the legend items will overlap.
The error occurred at a canvas size of 400 px and a label length of about 33 chars.

Regards

Ralf added these pithy words on Oct 15 08 at 8:00 am

Wonderful to see a JS implementation for charting! This has been a long time in the making and I was somewhat baulking at using Flex or Google Chart to undertake simple charts…

A quick question, I noticed a reference to “background_image” in the bluff-min.js. Is is possible to use an image as the background for a chart?

Regards!

aidee added these pithy words on Oct 24 08 at 12:39 am

@aidee Background images are not yet implemented. I’m not sure if they’re supported by ExCanvas, but it might make sense to implement them for more capable browsers. I’ll put it on my list for the next release. In the meantime, if you find any bugs, don’t hesitate to get in touch.

James added these pithy words on Oct 27 08 at 9:48 pm

@Ralf As of the latest release, text rendered by Bluff is contained in divs with a class of “bluff-text”. Use that in your stylesheet if you need to modify presentation of the text labels.

James added these pithy words on Oct 27 08 at 9:51 pm

Hi there!

I just discovered JS.Class last night and I am totally impressed by your work. This is one of the best if not the best Javascript libraries I have seen. I have started using it right away.

The best thing is that is totally encapsulated and doesn’t interfere with other libraries, so it can be used with Prototype, jQuery or even Mootools. I just love it.

Also, from what I have seen, Bluff is very promising.

Eneko Alonso added these pithy words on Nov 29 08 at 4:02 am

Great work! Amazing.

Did you think about using http://typeface.neocracy.org/ for the text? It would make the images copy-able, it would also allow people to use custom typography.

Do you see a future in Javascript for something like:
http://flare.prefuse.org/

I checked out some physics and it looks totally possible, see:
http://blobsallad.se/

Patrick Keenan added these pithy words on Dec 09 08 at 6:56 pm

Leave a Reply