Browse Source

Merge branch 'ctkvtkthresholdwidget-use-sharpness'

* ctkvtkthresholdwidget-use-sharpness:
  Add ctkVTKThresholdWidget::UseSharpness
Julien Finet 13 years ago
parent
commit
384ae5553d

+ 149 - 47
Libs/Visualization/VTK/Widgets/ctkVTKThresholdWidget.cpp

@@ -50,9 +50,13 @@ public:
   
   vtkPiecewiseFunction* PiecewiseFunction;
   bool UserRange;
+  bool UseSharpness;
+
 protected:
   void setNodes(double nodeValues[4][4]);
+  void setNodes(double nodeValues[][4], const int nodeCount);
   void setNodeValue(int index, double* nodeValue);
+  double safeX(double value, double& previous)const;
 };
 
 // ----------------------------------------------------------------------------
@@ -65,6 +69,7 @@ ctkVTKThresholdWidgetPrivate::ctkVTKThresholdWidgetPrivate(
 {
   this->PiecewiseFunction = 0;
   this->UserRange = false;
+  this->UseSharpness = false;
 }
 
 // ----------------------------------------------------------------------------
@@ -135,6 +140,21 @@ void ctkVTKThresholdWidgetPrivate::setRange(double min, double max)
 }
 
 // ----------------------------------------------------------------------------
+double ctkVTKThresholdWidgetPrivate::safeX(double value, double& previous)const
+{
+  if (value < previous)
+    {
+    value = previous;
+    }
+  if (value == previous && !this->PiecewiseFunction->GetAllowDuplicateScalars())
+    {
+    value += 0.00000000000001;
+    }
+  previous = value;
+  return value;
+}
+
+// ----------------------------------------------------------------------------
 void ctkVTKThresholdWidgetPrivate::setThreshold(double min, double max, double opacity)
 {
   Q_Q(ctkVTKThresholdWidget);
@@ -146,82 +166,140 @@ void ctkVTKThresholdWidgetPrivate::setThreshold(double min, double max, double o
   double range[2];
   this->ThresholdSliderWidget->range(range);
 
+  int nodeCount = 0;
+  if (this->UseSharpness)
+    {
+    //        +------2
+    //        |      |
+    // 0------1      +----3
+    nodeCount = 4;
+    }
+  else 
+    {
+    //        2------3
+    //        |      |
+    // 0------1      4----5
+    nodeCount = 6;
+    }
+  double (*nodes)[4] = new double[nodeCount][4];
+
+  double previous = VTK_DOUBLE_MIN;
   // Start of the curve, always y=0
-  double nodes[4][4];
-  nodes[0][0] = range[0];
-  nodes[0][1] = 0.;
-  nodes[0][2] = 0.5; // midpoint
-  nodes[0][3] = 0.; // sharpness
+  int index = 0;
+  nodes[index][0] = this->safeX(range[0], previous);
+  nodes[index][1] = 0.;
+  nodes[index][2] = 0.5; // midpoint
+  nodes[index][3] = 0.; // sharpness
+  ++index;
 
   // Starting threshold point with a sharp slope that jumps to the opacity
   // which is set by the next point
-  nodes[1][0] = min  +
-    ((min == nodes[0][0] && !this->PiecewiseFunction->GetAllowDuplicateScalars()) ?
-      0.00000000000001 : 0.);
-  nodes[1][1] = 0.;
-  nodes[1][2] = 0.; // jumps directly
-  nodes[1][3] = 1.; // sharp
+  nodes[index][0] = this->safeX(min, previous);
+  nodes[index][1] = 0.;
+  nodes[index][2] = this->UseSharpness ? 0. : 0.5;
+  nodes[index][3] = this->UseSharpness ? 1. : 0.;
+  ++index;
+
+  if (!this->UseSharpness)
+    {
+    nodes[index][0] = this->safeX(min, previous);
+    nodes[index][1] = opacity;
+    nodes[index][2] = 0.5;
+    nodes[index][3] = 0.;
+    ++index;
+    }
 
   // Ending threshold point with a sharp slope that jumps back to a 0 opacity
-  nodes[2][0] = max +
-    ((max == nodes[1][0] && !this->PiecewiseFunction->GetAllowDuplicateScalars()) ?
-      0.00000000000001 : 0.);
-  nodes[2][1] = opacity;
-  nodes[2][2] = 0.;
-  nodes[2][3] = 1.;
+  nodes[index][0] = this->safeX(max, previous);;
+  nodes[index][1] = opacity;
+  nodes[index][2] = this->UseSharpness ? 0. : 0.5;
+  nodes[index][3] = this->UseSharpness ? 1. : 0.;
+  ++index;
+
+  if (!this->UseSharpness)
+    {
+    nodes[index][0] = this->safeX(max, previous);
+    nodes[index][1] = 0.;
+    nodes[index][2] = 0.5;
+    nodes[index][3] = 0.;
+    ++index;
+    }
 
   // End of the curve, always y = 0
-  nodes[3][0] = range[1] +
-   ((range[1] == nodes[2][0] && !this->PiecewiseFunction->GetAllowDuplicateScalars()) ?
-      0.00000000000001 : 0.);
-  nodes[3][1] = 0.;
-  nodes[3][2] = 0.5;
-  nodes[3][3] = 0.;
+  nodes[index][0] = this->safeX(range[1], previous);
+  nodes[index][1] = 0.;
+  nodes[index][2] = 0.5;
+  nodes[index][3] = 0.;
+  ++index;
 
   q->qvtkBlock(this->PiecewiseFunction, vtkCommand::ModifiedEvent, q);
-  this->setNodes(nodes);
+  this->setNodes(nodes, nodeCount);
   q->qvtkUnblock(this->PiecewiseFunction, vtkCommand::ModifiedEvent, q);
   q->updateFromPiecewiseFunction();
+  delete []nodes;
 }
 
 // ----------------------------------------------------------------------------
-void ctkVTKThresholdWidgetPrivate::setNodes(double nodeValues[4][4])
+void ctkVTKThresholdWidgetPrivate
+::setNodes(double nodeValues[][4], const int nodeCount)
 {
-  double lastX = VTK_DOUBLE_MIN;
-  double minX = nodeValues[0][0];
-  for(int i = 0; i < 4; ++i)
+  for(int i = 0; i < nodeCount; ++i)
     {
-    int index = this->PiecewiseFunction->GetSize();
-    bool alreadyEqual = false;
-    // search the node index to modify
-    for (int j = 0; j < this->PiecewiseFunction->GetSize(); ++j)
+    int index = i;
+    double node[4];
+    // Search the index where to write the node
+    for (int j = 0; j < i; ++j)
       {
-      double node[4];
       this->PiecewiseFunction->GetNodeValue(j, node);
-      if (node[0] < minX || node[0] > lastX)
+      bool different = node[0] != nodeValues[j][0] ||
+                       node[1] != nodeValues[j][1] ||
+                       node[2] != nodeValues[j][2] ||
+                       node[3] != nodeValues[j][3];
+      if (different)
         {
         index = j;
         break;
         }
-      else if (node[0] == lastX)
+      }
+    if (index >= this->PiecewiseFunction->GetSize())
+      {
+      node[0] = VTK_DOUBLE_MAX;
+      }
+    else if (index == i)
+      {
+      this->PiecewiseFunction->GetNodeValue(index, node);
+      }
+    // else we already have node correctly set
+    if (!this->UseSharpness)
+      {
+      // be smart in order to reduce "jumps"
+      // Here is the intermediate steps we try to avoid:
+      // Start
+      //     2---3
+      //     |   |
+      // 0---1   4--5
+      // After node1 is set:
+      //     1   _3
+      //     |\ / |
+      // 0---| 2  4--5
+      // After node1 is set:
+      //       2--3
+      //       |  |
+      // 0-----1  4--5
+      if ((i == 1 && node[0] < nodeValues[i][0]) ||
+          (i == 3 && node[0] < nodeValues[i][0]))
         {
-        if (alreadyEqual)
-          {
-          index = j;
-          break;
-          }
-        alreadyEqual = true;
+        this->setNodeValue(index + 1, nodeValues[i+1]);
         }
       }
     this->setNodeValue(index, nodeValues[i]);
-    lastX = nodeValues[i][0];
     }
 }
 
 // ----------------------------------------------------------------------------
 void ctkVTKThresholdWidgetPrivate::setNodeValue(int index, double* nodeValues)
 {
-  if (this->PiecewiseFunction->GetSize() <= index)
+  if (index >= this->PiecewiseFunction->GetSize())
     {
     this->PiecewiseFunction->AddPoint(
       nodeValues[0], nodeValues[1], nodeValues[2], nodeValues[3]);
@@ -301,14 +379,16 @@ void ctkVTKThresholdWidget::updateFromPiecewiseFunction()
   double maxThreshold = range[1];
   double opacity = d->OpacitySliderWidget->value();
   double node[4];
-  if (d->PiecewiseFunction->GetSize() > 1)
+  int minIndex = 1;
+  if (d->PiecewiseFunction->GetSize() > minIndex)
     {
-    d->PiecewiseFunction->GetNodeValue(1, node);
+    d->PiecewiseFunction->GetNodeValue(minIndex, node);
     minThreshold = node[0];
     }
-  if (d->PiecewiseFunction->GetSize() > 2)
+  int maxIndex = d->UseSharpness ? 2 : 3;
+  if (d->PiecewiseFunction->GetSize() > maxIndex)
     {
-    d->PiecewiseFunction->GetNodeValue(2, node);
+    d->PiecewiseFunction->GetNodeValue(maxIndex, node);
     maxThreshold = node[0];
     opacity = node[1];
     }
@@ -368,3 +448,25 @@ void ctkVTKThresholdWidget::setRange(double min, double max)
   d->UserRange = true;
   d->setRange(min, max);
 }
+
+// ----------------------------------------------------------------------------
+bool ctkVTKThresholdWidget::useSharpness()const
+{
+  Q_D(const ctkVTKThresholdWidget);
+  return d->UseSharpness;
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKThresholdWidget::setUseSharpness(bool use)
+{
+  Q_D(ctkVTKThresholdWidget);
+  if (use == d->UseSharpness)
+    {
+    return;
+    }
+  d->UseSharpness = use;
+
+  double min, max, value;
+  d->guessThreshold(min, max, value);
+  d->setThreshold(min, max, value);
+}

+ 6 - 0
Libs/Visualization/VTK/Widgets/ctkVTKThresholdWidget.h

@@ -40,6 +40,9 @@ class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKThresholdWidget
   /// Setting the opacity property has no effect when no piecewise function is
   /// set.
   Q_PROPERTY(double opacity READ opacity WRITE setOpacity DESIGNABLE false)
+  /// Use 2 (true) or 4 (false) control points to control the threshold.
+  /// 4 points by default (false).
+  Q_PROPERTY(bool useSharpness READ useSharpness WRITE setUseSharpness)
 public:
   ctkVTKThresholdWidget(QWidget* parent = 0);
   virtual ~ctkVTKThresholdWidget();
@@ -50,6 +53,9 @@ public:
   void thresholdValues(double* values)const;
   double opacity()const;
 
+  bool useSharpness()const;
+  void setUseSharpness(bool use);
+
 public slots:
   void setPiecewiseFunction(vtkPiecewiseFunction* function);
   void setRange(double min, double max);