Canvas Parallel Coordinates

5,193 views
Skip to first unread message

Kai Chang

unread,
Apr 17, 2012, 6:25:00 PM4/17/12
to d3...@googlegroups.com
Here's the parallel coordinates example, using canvas rendering rather
than SVG. It still uses SVG for the brush component, axes and labels:

http://bl.ocks.org/2409451

Ian Johnson

unread,
Apr 17, 2012, 7:50:39 PM4/17/12
to d3...@googlegroups.com
Very cool! So that one little function is the canvas equivalent of the d3.svg.path function (minus interpolation)?
--
Ian Johnson

Kai Chang

unread,
Apr 17, 2012, 8:36:04 PM4/17/12
to d3...@googlegroups.com

Another difference is the foreground and background variables refer to canvas rendering contexts rather than a group of SVG paths.

Canvas has some built-in inerpolators too. Check out the paths section:

http://simon.html5.org/dump/html5-canvas-cheat-sheet.html

On Apr 17, 2012 4:50 PM, "Ian Johnson" <enj...@gmail.com> wrote:

Kai Chang

unread,
Apr 18, 2012, 4:20:54 AM4/18/12
to d3...@googlegroups.com
Here's the canvas version loaded with 7637 rows from the USDA
Nutrition Database:

http://bl.ocks.org/2411910

The interaction can bog down based on how many rows are selected. The
foreground is completely redrawn every brush event. Rendering paths is
still a bottleneck. This depends on the size of the canvas too. A
canvas with half the height will be 30-50% faster, it seems.

Firefox throws long-running script alerts. Perhaps
requestAnimationFrame can be used to chunk together the drawing of
paths, so that only 50-100 lines are drawn at a time. This way the
user might get more immediate feedback, even if it takes a moment for
the chart to fill in.

Kai Chang

unread,
Apr 18, 2012, 4:27:03 AM4/18/12
to d3...@googlegroups.com
Better performance on the iPad too. SVG performance is just awful on
that device.

Kai Chang

unread,
Apr 18, 2012, 5:26:22 AM4/18/12
to d3...@googlegroups.com
Updated the 7637 version to use requestAnimationFrame when filtering.
Much snappier, and finally usable on Firefox and iPad, after the
initial load. Chrome is incredibly smooth. It could use a progress bar
to give the user feedback on how many lines have yet to be rendered.

I hope this will be able to deal well with 30k+ lines, possibly up to 100k.

Unfortunately with this technique, the interface is prone to
"flashing", since it will show the canvas state after it's been
cleared but before most lines have been drawn. It's annoying to see
lines re-render after small changes to the filter.

Mike Bostock

unread,
Apr 18, 2012, 12:44:03 PM4/18/12
to d3...@googlegroups.com
Combining Canvas and SVG is a promising technique if you need to eek
out a bit more performance, at least when you have cacheable elements;
you can same them to a pixel buffer rather than redrawing.

I have been working on a new time-series visualization library (part
of Cube 0.2.0) that combines SVG and Canvas to render real-time
metrics. The nice thing about Canvas is that when new data comes in, I
can simply blit the old data to the left by one pixel, expending very
little effort drawing new data. This approach lets the library scale
to hundreds of metrics by thousands of samples (~300,000 data
points!), updating every ten seconds.

Many assume (speaking in the abstract here) that Canvas is magically
faster than SVG in the general case. This is not true, of course; the
real benefit of Canvas is being able to control what gets drawn on
update. In some cases, you can achieve a similar effect with SVG by
applying CSS3 transforms, which cause the SVG to be rendered as a
texture, much like Canvas. For example, try clicking and dragging in
the middle to rotate:

http://mbostock.github.com/d3/talk/20111116/bundle.html

My hope is that browser vendors continue to make SVG renderers smarter
and faster. There has been some promising work at NVidia to push the
entire SVG down to the GPU, for example:

http://developer.nvidia.com/nv-path-rendering

Unrelated: I seem have to problems using the brush component in your
demos, Kai. For example, if I brush an axis, I can't click on the
background of the axis to clear the brush. Is it just me?

Mike

Mike Bostock

unread,
Apr 18, 2012, 9:34:12 PM4/18/12
to d3...@googlegroups.com
> Unrelated: I seem have to problems using the brush component in your
> demos, Kai. For example, if I brush an axis, I can't click on the
> background of the axis to clear the brush. Is it just me?

OK, this was a bug in release 2.9.0, and has now been fixed in 2.9.1 Oops!

Mike

Kai Chang

unread,
Apr 18, 2012, 10:13:34 PM4/18/12
to d3...@googlegroups.com
Thanks for the fix, the clear event's working on all the examples again.

I tried caching the pixel data, but I couldn't figure out how to
squeeze more performance out of it. The cache is contained in the
"foreground_cache" variable, and the "staging" canvas is used to
rerender lines and draw them to the foreground:

http://bl.ocks.org/2417758

Paths are rendered only once, and image data is cached for redrawing.
Redrawing a single path requires one clearRect, putImageData and
drawImage call. The net effect seems much slower than simply
rerendering paths with moveTo/lineTo.

Transparency doesn't work with putImageData, which is why the staging
canvas is required.

I also tried avoiding putImageData by storing Image elements instead
of pixel arrays, but that was even slower:

http://stackoverflow.com/questions/3952856/why-is-putimagedata-so-slow/4282335#4282335

Any ideas on improving this caching/redrawing?

Kai Chang

unread,
Apr 18, 2012, 10:48:46 PM4/18/12
to d3...@googlegroups.com
Added coloring to the 7637 example. Quite hyperactive when filtering,
but reveals the normally occluded back layers for a few milliseconds.

http://bl.ocks.org/2411910

Rich Morin

unread,
Apr 18, 2012, 11:40:40 PM4/18/12
to d3...@googlegroups.com
On Apr 18, 2012, at 01:27, Kai Chang wrote:
> Better performance on the iPad too. SVG performance is just awful
> on that device.


Some D3 demos that definitely run slowly on the iPad 3. If we could
characterize the specific kinds of D3 features that run slowly, we
could (a) send in useful bug reports and (b) know which features to
avoid. Seems like something worth tracking and investigating...

-r

--
http://www.cfcl.com/rdm Rich Morin
http://www.cfcl.com/rdm/resume r...@cfcl.com
http://www.cfcl.com/rdm/weblog +1 650-873-7841

Software system design, development, and documentation


Jason Davies

unread,
Apr 19, 2012, 6:03:13 AM4/19/12
to d3...@googlegroups.com

And here's the bug-free fix for IE9:

https://github.com/mbostock/d3/pull/618

--
Jason Davies, http://www.jasondavies.com/

Kai Chang

unread,
Apr 19, 2012, 6:38:40 AM4/19/12
to d3...@googlegroups.com
Another update which uses a Fisher-Yates shuffle to randomize the
rendering order. The general shape of the data is visible after only a
few hundred lines have been rendered, and the experience is far less
jarring.

A further improvement might be to base the rendering order on the
user's last interaction. For instance, if brushed from 30 -> 50 g of
fat, the selection sorts by fat (increasing) and renders in that
order. Removing a filter doesn't require clearing the canvas and
existing lines don't need to be re-rendered. For now the shuffled
render order is a simpler solution though.

Added tick hiding, dynamic opacity, bezier curves, a dark theme:

http://bl.ocks.org/2420080

Compare to sequential rendering order here:

http://bl.ocks.org/2411910

Kai Chang

unread,
Jul 31, 2012, 7:12:55 PM7/31/12
to d3...@googlegroups.com
I've updated the canvas example with some additional features:

Invert Axis (click axis title)
Reorder Axes (drag axis title)
Remove Axis (drag axis title to left edge)
Filter groups (click group in legend to toggle on/off)
Dynamic legend (count of selected data)
Bi-directional linking (hover over food sample rows)
Text search (use textbox in sample)
Scales to screen size (resize window)
Rescale axes to selected data (Keep button - check out the axes flying
all over the place)
Exclude selected data (Exclude button)
CSV Export of selected data
Better visual brush feedback (hidden ticks outside of range)
Faster rendering (adjusts rendering speed to ~30fps, which will do
hundreds of polylines per frame on fast machines)

http://bl.ocks.org/d/3150059/

CSV upload version. It's not quite as up-to-date or featureful, but it
will let you play with your own data. Plots numeric columns, based on
the first row of data (nulls are treated as zeros).

http://bl.ocks.org/d/3156751/

These may look similar to exposedata.com/parallel, but they scales
better to a large number of rows (10-200k) and doesn't use as many
dependencies. I prefer the cardinal interpolation and of course the
SVG version doesn't "flash", re-rendering with every interaction

I plan to work on factoring this into a reusable chart, and adding
more advanced features of parallel coordinates (new brush modes,
variable column spacing, etc). Any suggestions on clever rendering
techniques would be appreciated. I can probably pillage some
optimizations from CrossFilter too.

I'm always looking for feedback for parallel coordinates. Either post
on the list, or the d3js subreddit:

http://www.reddit.com/r/d3js/comments/xh2ks/nutrition_parallel_coordinates_canvas/

Kai

Kai Chang

unread,
Aug 5, 2012, 7:50:18 PM8/5/12
to d3...@googlegroups.com
Here's a version with 60 dimensions! Looks best at width 1200px+. For
smaller widths, hide the ticks.

http://bl.ocks.org/d/3267951/

If you're looking for a rich, high-dimensional dataset to prototype
with, I've derived a single CSV of the whole USDA database (7637 rows)
with all 146 nutrient dimensions (plus name, food group, and id).

https://gist.github.com/3260401

Kai Chang

unread,
Aug 5, 2012, 7:50:53 PM8/5/12
to d3...@googlegroups.com
That last gist has moved here. My mistake:

https://gist.github.com/3267988

Ian Johnson

unread,
Aug 5, 2012, 9:40:59 PM8/5/12
to d3...@googlegroups.com
I like the linking with the samples! nice touch :)
Reply all
Reply to author
Forward
0 new messages