VTK/Marks: Difference between revisions

From KitwarePublic
< VTK
Jump to navigationJump to search
No edit summary
 
(10 intermediate revisions by 2 users not shown)
Line 2: Line 2:
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.
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.


[http://www.protovis.org Protovis] is an API description for marks with a reference implementation in javascript. Some of the features that make this attractive include
[http://www.protovis.org 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.
* List
* Of
* Cool
* 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.
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.
Line 12: Line 8:
[[Image:Marks.png]]
[[Image:Marks.png]]


Alternately, we could move Qt up a few layers and include it as an alternative back-end to marks. We would want this if we determine it is not worth the effort to implement certain advanced types of drawing or interactivity in VTK itself. In this case, some views may require the QGraphicsView framework.
[[VTK/Marks/Design]] describes some API design considerations.


[[Image:MarksWithQt.png]]
== Initial Prototype ==


== Fleshing Out the vtkMark API ==
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 fundamental type would be vtkMark, the superclass of all mark types. Its interface would define how programmers would work with all marks in VTK. The following sections enumerate some possible use cases with code snippets showing what the functionality the feature would allow.


=== Possible Supported Parameter Types ===
The source code is currently available as part of the [http://titan.sandia.gov 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.


* Basic types (double, int, string, vtkVariant)
{| border="1"
<source lang="cpp">
! Protovis
vtkDotMark* m = vtkDotMark::New();
! C++
m->SetParameter("size", 10);
|-
|
<source lang="javascript">
var data = pv.range(10).map(function(d) { return Math.random() + .1; });
</source>
</source>
 
|
* Data object
<source lang="cpp">
<source lang="cpp">
vtkDirectedGraph* g = vtkDirectedGraph::New();
  std::vector<double> vec;
//...
  for (int i = 0; i < 10; ++i)
vtkDotMark* m = vtkDotMark::New();
    {
m->SetParameter("data", g);
    vec.push_back(vtkMath::Random() + 0.1);
    }
  VectorStorage* s = new VectorStorage(vec);
  Datum data(s);
</source>
</source>
 
|-
* Pipeline output port
|
<source lang="javascript">
// 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);</source>
|
<source lang="cpp">
<source lang="cpp">
vtkRandomGraphSource* s = vtkRandomGraphSource::New();
  // Sizing and scales.
//...
  int w = 400;
vtkDotMark* m = vtkDotMark::New();
  int h = 250;
m->SetParameter("data", s->GetOutputPort());
  LinearScale x(0, 1.1);
  x.range(0, w);
  OrdinalScale y(range(10));
  y.splitBanded(0, h, 4.0/5);
</source>
</source>
 
|-
* vtkArray
|
<source lang="cpp">
<source lang="javascript">
vtkDenseArray<double>* a = vtkDenseArray<double>::New();
// The root panel.
//...
var vis = new pv.Panel()
vtkDotMark* m = vtkDotMark::New();
    .width(w)
m->SetParameter("size", a);
    .height(h)
    .bottom(20)
    .left(20)
    .right(10)
    .top(5);
</source>
</source>
 
|
* vtkAbstractArray
<source lang="cpp">
<source lang="cpp">
vtkDoubleArray* a = vtkDoubleArray::New();
  // The root panel.
//...
  Panel vis;
vtkDotMark* m = vtkDotMark::New();
  vis.width(w);
m->SetParameter("size", a);
  vis.height(h);
  vis.bottom(20);
  vis.left(20);
  vis.right(10);
  vis.top(5);
</source>
</source>
 
|-
* Data object + field type + array name
|
<source lang="cpp">
<source lang="javascript">
vtkTable* t = vtkTable::New();
// The bars.
//...
var bar = vis.add(pv.Bar)
vtkDotMark* m = vtkDotMark::New();
    .data(data)
m->SetParameter("size", t, vtkDataObject::ROW, "columnName");
    .top(function() y(this.index))
    .height(y.range().band)
    .left(0)
    .width(x);
</source>
</source>
 
|
* Data object + field type + array name + component index
<source lang="cpp">
<source lang="cpp">
vtkTable* t = vtkTable::New();
  // The bars.
vtkDoubleArray* loc = vtkDoubleArray::New();
  Bar* bar = vis.addBar();
loc->SetNumberOfComponents(2);
  bar->data(data);
//...
  bar->top(bind(y, _index));
vtkDotMark* m = vtkDotMark::New();
  bar->height(y.band());
m->SetParameter("left", t, vtkDataObject::ROW, "location", 0);
  bar->left(0);
m->SetParameter("bottom", t, vtkDataObject::ROW, "location", 1);
  bar->width(x);
</source>
</source>
 
|-
* Function pointer
|
<source lang="cpp">
<source lang="javascript">
double MySize(vtkMark* m, int i) { return 10*i; }
// The value label.
//...
bar.anchor("right").add(pv.Label)
vtkDotMark* m = vtkDotMark::New();
    .textStyle("white")
m->SetParameter("size", &MySize);
    .text(function(d) d.toFixed(1));
</source>
</source>
 
|
* Functor (i.e. struct type with operator()) - requires SetParameter to be templated.
<source lang="cpp">
<source lang="cpp">
struct MySize {
  // The value label.
   double operator()(vtkMark* m, int i) { return 10*i; }
   Label* label = bar->anchor("right")->addLabel();
}
  label->textStyle(vtkColor4d(1, 1, 1, 1));
//...
  label->text(bind(toFixed, _d, 1));
vtkDotMark* m = vtkDotMark::New();
m->SetParameter("size", MySize());
</source>
</source>
 
|-
* Lambda function (boost::lambda)
|
 
<source lang="javascript">
* Parameter strategies
// The variable label.
<source lang="cpp">
bar.anchor("left").add(pv.Label)
class vtkDoubleParameter {
    .textMargin(5)
  virtual double Get(vtkMark* m, int i) = 0;
    .textAlign("right")
}
    .text(function() "ABCDEFGHIJK".charAt(this.index));
 
class MySize : public vtkDoubleParameter {
protected:
  double Get(vtkMark* m, int i) { return 10*i; }
}
//...
vtkDotMark* m = vtkDotMark::New();
m->SetParameter("size", MySize());
</source>
</source>
 
|
* Override virtual methods in mark
<source lang="cpp">
<source lang="cpp">
class vtkDotMark : public vtkMark {
  // The variable label.
protected:
  Label* label2 = bar->anchor("left")->addLabel();
   virtual double GetSize(int i) { return 10; }
   label2->textMargin(5);
}
   label2->textAlign(vtkStdString("right"));
 
  label2->text(bind(charAt, "ABCDEFGHIJK", _index));
class MyMark : public vtkDotMark {
protected:
   double GetSize(int i) { return 10*i; }
}
//...
MyMark* m = MyMark::New();
</source>
</source>
 
|-
* Inherit from parent mark
|
<source lang="cpp">
<source lang="javascript">
vtkDotMark* parent = vtkDotMark::New();
// X-axis ticks.
parent->SetParameter("size", 10);
vis.add(pv.Rule)
vtkDotMark* child = vtkDotMark::New();
    .data(x.ticks(5))
parent->Add(child); // Child has size 10
    .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);
</source>
</source>
 
|
=== Possible Parameter Storage and Access ===
 
* Named member variables
<source lang="cpp">
<source lang="cpp">
class vtkDotMark {
  // X-axis ticks.
public:
   Rule* rule = vis.addRule();
   ParamType GetSize() { return this->Size; }
  rule->data(x.ticks(5));
   void SetSize(ParamType p) { this->Size = p; }
   rule->left(x);
private:
   rule->strokeStyle(if_then_else_return(_d[0] > 0, vtkColor4d(1, 1, 1, 0.3), vtkColor4d(0, 0, 0, 1)));
   ParamType Size;
}
</source>


* Named member variables with import/export to name/value map
  Rule* rule2 = rule->addRule();
<source lang="cpp">
   rule2->bottom(0);
class vtkMark {
  rule2->height(5);
public:
   rule2->strokeStyle(vtkColor4d(0, 0, 0, 1));
  ParamType GetParameter(string key)
   { this->ExportSettings()->GetParameter(key); }
   void SetParameter(string key, ParamType p)
  { this->ImportSettings(this->ExportSettings()->SetParameter(key, p)); }
  virtual void ImportSettings(vtkSettings* s) { }
  virtual vtkSettings* ExportSettings() { return vtkSettings::New(); }
}


class vtkDotMark {
   Label* l = rule2->anchor("bottom")->addLabel();
public:
   l->text(bind(toFixed, _d, 1));
   ParamType GetSize() { return this->Size; }
  void SetSize(ParamType p) { this->Size = p; }
  virtual vtkSettings* ExportSettings()
   { return this->Parent->ExportSettings()->SetParameter("size", this->Size); }
  virutal void ImportSettings(vtkSettings* s)
  { this->Size = s->GetParameter("size"); }
private:
  ParamType Size;
}
</source>
</source>
 
|-
* Generic name/value map
|
<source lang="cpp">
<source lang="javascript">
class vtkMark {
// Render the scene.
public:
vis.render();
  ParamType GetParameter(string key)
  { return this->Parameters[key].Valid ? this->Parameters[key] : this->Parent->GetParameter(key); }
  void SetParameter(string key, ParamType p)
  { this->Parameters[key] = p; }
private:
  map<string, ParamType> Parameters;
}
</source>
</source>
 
|
* Generic name/value map with setter/getter syntactic sugar for discoverability
<source lang="cpp">
<source lang="cpp">
class vtkDotMark : public vtkMark {
// Render the scene.
public:
vis.render();
  ParamType GetSize() { return this->GetParameter("size"); }
  void SetSize(ParamType p) { this->SetParameter("size", p); }
}
</source>
</source>
 
|-
* vtkInformation/value map. The idea here is that valid parameter names/types/bounds are queryable at runtime, instead of in an opaque map.
| [[Image:ProtovisBarChart.png]]
<source lang="cpp">
| [[Image:MarksBarChart.png]]
class vtkMark {
|-
public:
|}
  // Keys to describe parameters
  static vtkInformationIntegerKey* PARAMETER_TYPE();
  static vtkInformationStringKey* PARAMETER_NAME();
  static vtkInformationIntegerKey* PARAMETER_NUMBER_OF_COMPONENTS();
  static vtkInformationIntegerVectorKey* PARAMETER_INTEGER_BOUNDS();
  static vtkInformationDoubleVectorKey* PARAMETER_REAL_BOUNDS();
 
  int GetNumberOfParameters() { return this->ParameterInfo->size(); }
  vtkInformation* GetParameterInfo(int i) { return this->ParameterInfo[i]; }
  int GetParameterHandle(const char* name)
  { /* search ParameterInfo for name, return index */ }
  void SetParameter(int handle, ParamType p)
  { this->ParameterValues[handle] = p; }
 
protected:
  virtual void SetupParameters() = 0;
  vector<vtkInformation*> ParameterInfo;
  vector<ParamType> ParameterValues;
}
 
class vtkDotMark : public vtkMark {
protected:
  void SetupParameters()
  {
    vtkInformation* sizeInfo = vtkInformation::New();
    double sizeBounds[2] = { 0., VTK_DOUBLE_MAX };
    sizeInfo->Set( vtkPointMark::PARAMETER_NAME(), "size" );
    sizeInfo->Set( vtkPointMark::PARAMETER_TYPE(), VTK_DOUBLE );
    sizeInfo->Set( vtkPointMark::PARAMETER_NUMBER_OF_COMPONENTS(), 1 );
    sizeInfo->Set( vtkPointMark::PARAMETER_REAL_BOUNDS(), sizeBounds, 2 );
    this->ParameterInfo.push_back(sizeInfo);
    this->ParameterValues.push_back(10);
  }
}
</source>
 
=== Possible Other Features ===
 
* Declarative API
* Iterator support

Latest revision as of 18:48, 22 October 2010

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++

<source lang="javascript"> var data = pv.range(10).map(function(d) { return Math.random() + .1; }); </source>

<source lang="cpp">

 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);

</source>

<source lang="javascript"> // 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);</source>

<source lang="cpp">

 // 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);

</source>

<source lang="javascript"> // The root panel. var vis = new pv.Panel()

   .width(w)
   .height(h)
   .bottom(20)
   .left(20)
   .right(10)
   .top(5);

</source>

<source lang="cpp">

 // The root panel.
 Panel vis;
 vis.width(w);
 vis.height(h);
 vis.bottom(20);
 vis.left(20);
 vis.right(10);
 vis.top(5);

</source>

<source lang="javascript"> // The bars. var bar = vis.add(pv.Bar)

   .data(data)
   .top(function() y(this.index))
   .height(y.range().band)
   .left(0)
   .width(x);

</source>

<source lang="cpp">

 // The bars.
 Bar* bar = vis.addBar();
 bar->data(data);
 bar->top(bind(y, _index));
 bar->height(y.band());
 bar->left(0);
 bar->width(x);

</source>

<source lang="javascript"> // The value label. bar.anchor("right").add(pv.Label)

   .textStyle("white")
   .text(function(d) d.toFixed(1));

</source>

<source lang="cpp">

 // The value label.
 Label* label = bar->anchor("right")->addLabel();
 label->textStyle(vtkColor4d(1, 1, 1, 1));
 label->text(bind(toFixed, _d, 1));

</source>

<source lang="javascript"> // The variable label. bar.anchor("left").add(pv.Label)

   .textMargin(5)
   .textAlign("right")
   .text(function() "ABCDEFGHIJK".charAt(this.index));

</source>

<source lang="cpp">

 // The variable label.
 Label* label2 = bar->anchor("left")->addLabel();
 label2->textMargin(5);
 label2->textAlign(vtkStdString("right"));
 label2->text(bind(charAt, "ABCDEFGHIJK", _index));

</source>

<source lang="javascript"> // 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);

</source>

<source lang="cpp">

 // 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));

</source>

<source lang="javascript"> // Render the scene. vis.render(); </source>

<source lang="cpp"> // Render the scene. vis.render(); </source>

ProtovisBarChart.png MarksBarChart.png