VTK/Marks

From KitwarePublic
< VTK
Jump to navigationJump to search

Overview

Marks can be thought of as highly-configurable vectorized graphics items, or graphics item factories. They are a mid-level API built on more basic rendering primitives like lines, circles, etc. You want to use marks to make it easy to configure a set of primitives, where each primitive's properties will change depending on the item.

Protovis is an API description for marks with a reference implementation in javascript. Take a look at the protovis site for details on their design decisions and features.

In VTK, we want to explore a similar interface for marks in C++. The following is a possible technology stack for using marks in VTK. Qt could be incorporated as an alternative to OpenGL for publication-quality images.

Marks.png

VTK/Marks/Design describes some API design considerations.

Initial Prototype

The purpose of this prototype was to see how closely C++ code can resemble Protovis Javascript code. The properties are defined as boost::function instances. boost::lambda expressions (lines using the special _index and _d variables) may be used to specify functions. Alternately, free/static functions or function objects may be used. The bottom images show the results from both pieces of code.

The source code is currently available as part of the Titan Toolkit under the Libraries/Marks subdirectory, and it is slated to be moved into VTK in late 2010 or early 2011. Examples may be found in the tests under Libraries/Marks/Testing/Cxx.

Protovis C++
var data = pv.range(10).map(function(d) { return Math.random() + .1; });
  std::vector<double> vec;
  for (int i = 0; i < 10; ++i)
    {
    vec.push_back(vtkMath::Random() + 0.1);
    }
  VectorStorage* s = new VectorStorage(vec);
  Datum data(s);
// Sizing and scales.
var w = 400,
    h = 250,
    x = pv.Scale.linear(0, 1.1).range(0, w),
    y = pv.Scale.ordinal(pv.range(10)).splitBanded(0, h, 4/5);
  // Sizing and scales.
  int w = 400;
  int h = 250;
  LinearScale x(0, 1.1);
  x.range(0, w);
  OrdinalScale y(range(10));
  y.splitBanded(0, h, 4.0/5);
// The root panel.
var vis = new pv.Panel()
    .width(w)
    .height(h)
    .bottom(20)
    .left(20)
    .right(10)
    .top(5);
  // The root panel.
  Panel vis;
  vis.width(w);
  vis.height(h);
  vis.bottom(20);
  vis.left(20);
  vis.right(10);
  vis.top(5);
// The bars.
var bar = vis.add(pv.Bar)
    .data(data)
    .top(function() y(this.index))
    .height(y.range().band)
    .left(0)
    .width(x);
  // The bars.
  Bar* bar = vis.addBar();
  bar->data(data);
  bar->top(bind(y, _index));
  bar->height(y.band());
  bar->left(0);
  bar->width(x);
// The value label.
bar.anchor("right").add(pv.Label)
    .textStyle("white")
    .text(function(d) d.toFixed(1));
  // The value label.
  Label* label = bar->anchor("right")->addLabel();
  label->textStyle(vtkColor4d(1, 1, 1, 1));
  label->text(bind(toFixed, _d, 1));
// The variable label.
bar.anchor("left").add(pv.Label)
    .textMargin(5)
    .textAlign("right")
    .text(function() "ABCDEFGHIJK".charAt(this.index));
  // The variable label.
  Label* label2 = bar->anchor("left")->addLabel();
  label2->textMargin(5);
  label2->textAlign(vtkStdString("right"));
  label2->text(bind(charAt, "ABCDEFGHIJK", _index));
// X-axis ticks.
vis.add(pv.Rule)
    .data(x.ticks(5))
    .left(x)
    .strokeStyle(function(d) d ? "rgba(255,255,255,.3)" : "#000")
  .add(pv.Rule)
    .bottom(0)
    .height(5)
    .strokeStyle("#000")
  .anchor("bottom").add(pv.Label)
    .text(x.tickFormat);
  // X-axis ticks.
  Rule* rule = vis.addRule();
  rule->data(x.ticks(5));
  rule->left(x);
  rule->strokeStyle(if_then_else_return(_d[0] > 0, vtkColor4d(1, 1, 1, 0.3), vtkColor4d(0, 0, 0, 1)));

  Rule* rule2 = rule->addRule();
  rule2->bottom(0);
  rule2->height(5);
  rule2->strokeStyle(vtkColor4d(0, 0, 0, 1));

  Label* l = rule2->anchor("bottom")->addLabel();
  l->text(bind(toFixed, _d, 1));
// Render the scene.
vis.render();
// Render the scene.
vis.render();
ProtovisBarChart.png MarksBarChart.png