Monday, October 3, 2011

Dygraphs Coordinate Systems, 2/3

Update: 8:30AM Oct 4: to clarify several items.

This is the second of three posts about the coordinate systems used throughout Dygraphs.
  • Part 1: Introduction and the Dygraph Data Coordinate System
  • Part 2: DOM Coordinate System
  • Part 3: Percentage Coordinate System

DOM Coordinate System

Another coordinate system is the DOM coordinate system, which maps to coordinates of the underlying canvas.

(DOM coordinates are useful for mouse event handling. drawing on the underlay canvas, to name two things.)

DOM coordinates typically have (0,0) in the upper-left corner. While you might expect the corner to be in the drawing area, you’ll be surprised where that corner precisely is.


The Dygraph canvas coordinates don’t begin and end in what you would think of as the drawing area. No, in fact the canvas is widened to take up the space of the entire parent div element.

(People not familiar with Dygraphs may not realize, but the labels themselves are not canvas-drawn. They're part of the HTML DOM. As someone who had to understand these coordinate systems the DOM coordinate origin was a surprise. Other graphing systems draw their labels right on the canvas, e.g. http://code.google.com/apis/chart/, Dygraphs' does not.)

Note: A demo of a Dygraph without axis, using the full parent div element for its drawing area can be found at http://dygraphs.com/tests/unboxed-spark.html.

Clipping and other hackery

Here's an example that will help bring home home the difference between the full canvas area and the area exposed to the user, by way of a fun example, by showing how the image above was drawn:


g.updateOptions({
 underlayCallback: function(ctx) {
   ctx.canvas.width = ctx.canvas.width; // 1
   for (var y = 0; y <= 350; y += 50) {
     ctx.moveTo(0, y);
     ctx.lineTo(500, y);
     ctx.stroke();
   }
   for (var x = 0; x <= 500; x += 50) {
     ctx.moveTo(x, 0);
     ctx.lineTo(x, 350);
     ctx.stroke();
   }
}});


The line marked // 1, above is important. When the underlayCallback is called, it hands the caller a pre-initialized canvas context such that anything outside the graph area is clipped. (Having a clipping area is useful for graphing lines that exceed the boundaries of the clipped viewport.) A statement like // 1 reads like a no-op, but it has a side-effect of resetting canvas context, clearing the viewport, making that drawing possible. This is what the graph looks like without line // 1:

I hope you see now that (0, 0) doesn't represent the corner of the drawing area, but some spot which is fundamentally inaccessible to the user.

Note: Please don’t use this trick in graphs and applications you care about. This is only a hack for demonstrative purposes.

Conversion methods

The Dygraphs API comes with methods for converting between Data coordinates and Canvas coordinates

toDomXCoord converts from data X coordinates to DOM X coordinates


for (x = 1; x <= 9; x++) {
 console.log(x, '=>', g.toDomXCoord(x));
}
1 "=>56
2 "=>110.875
3 "=>165.75
4 "=>220.625
5 "=>275.5
6 "=>330.375
7 "=>385.25
8 "=>440.125
9 "=>495


toDomYCoord converts from data Y coordinats to DOM Y coordinates

for (y = 0; y <= 120; y+=20) {
 console.log(y, '=>', g.toDomYCoord(y));
}
0 "=>330
20 "=>279.618320610687
40 "=>229.23664122137407
60 "=>178.85496183206106
80 "=>128.4732824427481
100 "=>78.09160305343512
120 "=>27.709923664122137

and toDataXCoord and toDataYCoord convert X and Y values from the DOM coordinate system to the Data coordinate system.


g.toDataXCoord(56)
1
g.toDataYCoord(330)
0

No comments:

Post a Comment