A graphical toolkit for visualization
Protovis
Overview
Examples
Documentation
Download
Index

Inheritance

Protovis uses inheritance to simplify the specification of related marks: a new mark can be derived from an existing mark, inheriting its properties. The new mark can then override properties to specify new behavior, potentially in terms of the old behavior. In this way, the old mark serves as the prototype for the new mark. Most mark types share the same basic properties for consistency and to facilitate inheritance.

To define a new mark that inherits from an existing mark, call add. For example, here we derive labels for a rule and a bar:

var vis = new pv.Panel()
    .width(150)
    .height(150);

vis.add(pv.Rule)
    .data(pv.range(0, 2, .5))
    .bottom(function(d) d * 80 + .5)
  .add(pv.Label); // inherits from rule

vis.add(pv.Bar)
    .data([1, 1.2, 1.7, 1.5, .7])
    .width(20)
    .height(function(d) d * 80)
    .bottom(0)
    .left(function() this.index * 25 + 25)
  .anchor("bottom") // inherits from bar
  .add(pv.Label); // inherits from anchor

vis.render();

The rule's label inherits the data and bottom property, causing it to appear on the rule and render the value (datum) as text. The bar's label uses the bottom anchor to tweak positioning, so that the label is centered at the bottom of the bar.

Inheritance is dynamic, in the sense that property functions are inherited, rather than property values. This means that properties are evaluated separately for each mark. For example, you can derive a new mark, and override the data property; properties on the new mark will be evaluated separately from the prototype (potentially generating a different number of rendered marks).

Interleaving multiple data series produces a grouped bar chart:

new pv.Panel()
    .width(150)
    .height(150)
  .add(pv.Bar)
    .data([1, 1.2, 1.7, 1.5, .7])
    .bottom(0)
    .width(10)
    .height(function(d) d * 80)
    .left(function() this.index * 25)
    .fillStyle("steelblue")
  .add(pv.Bar)
    .data([.5, 1, .8, 1.1, 1.3])
    .left(function() this.index * 25 + 10)
    .fillStyle("lightsteelblue")
  .root.render();

This demonstrates inheritance: the second bar inherits all of the properties of the first bar, and then overrides the data, the left margin, and the fillStyle. Copying the multiply-by-index and offsetting by 10 pixels is probably the most explicit method, but copying can sometimes make it difficult for you to change a visualization in the future.

Similarly, for overlapping area charts:

new pv.Panel()
    .width(150)
    .height(150)
  .add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(0)
    .height(function(d) d * 80)
    .fillStyle("rgba(30, 120, 180, .4)")
    .left(function() this.index * 25)
  .add(pv.Area)
    .data([.4, .2, .8, 1.2, 1.5, 1.1, .8])
    .fillStyle("rgba(30, 180, 120, .4)")
  .root.render();

For more examples of inheritance, learn about anchors.

Defaults

Marks also inherit from type-specific defaults.

Off-Screen Inheritance

You can use off-screen marks (marks that are not directly part of the visualization) to define default properties that are shared by other marks. In effect, this is a lightweight way of defining a new class of mark. For example, if you needed to create multiple labels:

var label = new pv.Label()
    .font("bold 14px sans-serif")
    .left(function() this.parent.width() / 2)
    .top(function() this.parent.height() / 2)
    .textAlign("center")
    .textBaseline("middle")
    .text(function() this.parent.width() + "\u00d7" + this.parent.height())
    .textStyle(pv.Colors.category10().by(pv.child));

Then, you can create a new label which extends the above, inheriting its properties:

vis.add(pv.Label)
    .extend(label);

You can also override specific properties:

vis.add(pv.Label)
    .extend(label)
    .font("10px sans-serif")
    .text("A smaller label!");
Copyright 2010 Stanford Visualization Group