Browse Source

Improve color table widget

Tristan 8 years ago
parent
commit
83c2651e36

+ 147 - 55
Libs/Visualization/VTK/Core/vtkDiscretizableColorTransferChart.cpp

@@ -33,6 +33,8 @@
 #include <vtkTable.h>
 #include <vtkTransform2D.h>
 
+//#define DEBUG_RANGE
+
 // ----------------------------------------------------------------------------
 class vtkHistogramMarker : public vtkObject
 {
@@ -140,22 +142,32 @@ vtkDiscretizableColorTransferChart::vtkDiscretizableColorTransferChart()
 
   this->DataRange[0] = VTK_DOUBLE_MAX;
   this->DataRange[1] = VTK_DOUBLE_MIN;
+  this->CurrentRange[0] = VTK_DOUBLE_MAX;
+  this->CurrentRange[1] = VTK_DOUBLE_MIN;
+  this->OriginalRange[0] = VTK_DOUBLE_MAX;
+  this->OriginalRange[1] = VTK_DOUBLE_MIN;
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE chart data range = " << this->DataRange[0]
+            << " " << this->DataRange[1] << std::endl;
+  std::cout << "DEBUG_RANGE current range = " << this->CurrentRange[0]
+            << " " << this->CurrentRange[1] << std::endl;
+  std::cout << "DEBUG_RANGE original range = " << this->OriginalRange[0]
+            << " " << this->OriginalRange[1] << std::endl;
+#endif
 }
 
 // ----------------------------------------------------------------------------
 void vtkDiscretizableColorTransferChart::SetColorTransferFunction(
   vtkDiscretizableColorTransferFunction* function)
 {
-  if (function == CTK_NULLPTR)
+  double range[2] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN };
+  if (function != CTK_NULLPTR)
   {
-    vtkSmartPointer<vtkDiscretizableColorTransferFunction> emptyCtf =
-      vtkSmartPointer<vtkDiscretizableColorTransferFunction>::New();
-    this->SetColorTransferFunction(emptyCtf, 0, 255);
-    return;
+    range[0] = function->GetRange()[0];
+    range[1] = function->GetRange()[1];
   }
-
-  this->SetColorTransferFunction(function,
-    function->GetRange()[0], function->GetRange()[1]);
+  this->SetColorTransferFunction(function, range[0], range[1]);
 }
 
 // ----------------------------------------------------------------------------
@@ -166,6 +178,34 @@ void vtkDiscretizableColorTransferChart::SetColorTransferFunction(
   this->ColorTransferFunction = function;
   this->ClearPlots();
 
+  this->OriginalRange[0] = rangeMin;
+  this->OriginalRange[1] = rangeMax;
+
+  this->CurrentRange[0] = rangeMin;
+  this->CurrentRange[1] = rangeMax;
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE original range = " << this->OriginalRange[0]
+            << " " << this->OriginalRange[1] << std::endl;
+  std::cout << "DEBUG_RANGE current range = " << this->CurrentRange[0]
+            << " " << this->CurrentRange[1] << std::endl;
+#endif
+
+  if (function == CTK_NULLPTR)
+  {
+    this->CompositeHiddenItem = CTK_NULLPTR;
+    this->ControlPoints = CTK_NULLPTR;
+    this->DataRange[0] = VTK_DOUBLE_MAX;
+    this->DataRange[1] = VTK_DOUBLE_MIN;
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE original range = " << this->DataRange[0]
+            << " " << this->DataRange[1] << std::endl;
+#endif
+
+    return;
+  }
+
   ///Build the histogram chart
   this->CompositeHiddenItem =
     vtkSmartPointer<vtkCompositeTransferFunctionItem>::New();
@@ -188,9 +228,6 @@ void vtkDiscretizableColorTransferChart::SetColorTransferFunction(
   this->ControlPoints->SetPointsFunction(
     vtkCompositeControlPointsItem::ColorAndOpacityPointsFunction);
 
-  this->OriginalRange[0] = rangeMin;
-  this->OriginalRange[1] = rangeMax;
-
   ///Add the Min/Max Markers
   this->MinPlot = AddPlot(vtkChart::LINE);
   this->MaxPlot = AddPlot(vtkChart::LINE);
@@ -214,10 +251,7 @@ void vtkDiscretizableColorTransferChart::SetColorTransferFunction(
   this->MaxLinePlot->SetColor(255, 255, 255, 255);
   this->MaxLinePlot->SetWidth(1.0);
 
-  this->CurrentRange[0] = rangeMin;
   this->MinMarker->SetPosition(this->CurrentRange[0]);
-
-  this->CurrentRange[1] = rangeMax;
   this->MaxMarker->SetPosition(this->CurrentRange[1]);
 
   this->AddPlot(this->CompositeHiddenItem);
@@ -238,48 +272,36 @@ void vtkDiscretizableColorTransferChart::UpdateMarkerPosition(
     pos.GetData(), 1);
 
   double limitRange[2];
-  limitRange[0] = std::min(this->OriginalRange[0], this->DataRange[0]);
-  limitRange[1] = std::max(this->OriginalRange[1], this->DataRange[1]);
+  limitRange[0] = this->OriginalRange[0]; //std::min(this->OriginalRange[0], this->DataRange[0]);
+  limitRange[1] = this->OriginalRange[1]; //std::max(this->OriginalRange[1], this->DataRange[1]);
 
   if (rangeMoving == RangeMoving_MIN)
   {
     double newValue = static_cast<double>(pos.GetX());
     if (newValue < limitRange[0])
     {
-      this->CurrentRange[0] = limitRange[0];
+      this->SetCurrentRange(limitRange[0], this->CurrentRange[1]);
     }
     else if (newValue < this->CurrentRange[1])
     {
-      this->CurrentRange[0] = newValue;
+      this->SetCurrentRange(newValue, this->CurrentRange[1]);
     }
     this->MinMarker->SetPosition(this->CurrentRange[0]);
-    if (this->ColorTransferFunction != CTK_NULLPTR)
-    {
-      this->ControlPoints->StartProcessing();
-      ctk::remapColorScale(this->ColorTransferFunction, this->CurrentRange[0],
-        this->CurrentRange[1]);
-      this->ControlPoints->EndProcessing();
-    }
+    this->RemapColorTransferFunction();
   }
   else if (rangeMoving == RangeMoving_MAX)
   {
     double newValue = static_cast<double>(pos.GetX());
     if (newValue > limitRange[1])
     {
-      this->CurrentRange[1] = limitRange[1];
+      this->SetCurrentRange(this->CurrentRange[0], limitRange[1]);
     }
     else if (newValue > this->CurrentRange[0])
     {
-      this->CurrentRange[1] = newValue;
+      this->SetCurrentRange(this->CurrentRange[0], newValue);
     }
     this->MaxMarker->SetPosition(this->CurrentRange[1]);
-    if (this->ColorTransferFunction)
-    {
-      this->ControlPoints->StartProcessing();
-      ctk::remapColorScale(this->ColorTransferFunction, this->CurrentRange[0],
-        this->CurrentRange[1]);
-      this->ControlPoints->EndProcessing();
-    }
+    this->RemapColorTransferFunction();
   }
 }
 
@@ -403,44 +425,84 @@ void vtkDiscretizableColorTransferChart::SetCurrentControlPointColor(
 // ----------------------------------------------------------------------------
 void vtkDiscretizableColorTransferChart::SetDataRange(double min, double max)
 {
+  if (min == this->DataRange[0]
+   && max == this->DataRange[1])
+  {
+    return;
+  }
+
   this->DataRange[0] = min;
   this->DataRange[1] = max;
-}
 
-// ----------------------------------------------------------------------------
-double* vtkDiscretizableColorTransferChart::GetDataRange()
-{
-  return this->DataRange;
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE chart data range = " << this->DataRange[0]
+            << " " << this->DataRange[1] << std::endl;
+#endif
 }
 
 // ----------------------------------------------------------------------------
 void vtkDiscretizableColorTransferChart::SetCurrentRange(
   double min, double max)
 {
-  double limitRange[2];
-  limitRange[0] = std::min(this->OriginalRange[0], this->DataRange[0]);
-  limitRange[1] = std::max(this->OriginalRange[1], this->DataRange[1]);
+  if (min == this->GetCurrentRange()[0]
+   && max == this->GetCurrentRange()[1])
+  {
+    return;
+  }
+
+  if (this->OriginalRange[0] < this->OriginalRange[1])
+  {
+    min = vtkMath::ClampValue(min, this->OriginalRange[0], this->OriginalRange[1]);
+    max = vtkMath::ClampValue(max, this->OriginalRange[0], this->OriginalRange[1]);
+  }
 
-  ///check if min < max;
-  min = vtkMath::ClampValue(min, limitRange[0], limitRange[1]);
-  max = vtkMath::ClampValue(max, limitRange[0], limitRange[1]);
-  if (min < max)
+  if (min <= max)
   {
-    this->CurrentRange[0] = 
-      min < limitRange[0] ? limitRange[0] : min;
-    this->CurrentRange[1] =
-      max > limitRange[1] ? limitRange[1] : max;
+    this->CurrentRange[0] = min;
+    this->CurrentRange[1] = max;
     this->MinMarker->SetPosition(this->CurrentRange[0]);
     this->MaxMarker->SetPosition(this->CurrentRange[1]);
+    this->RemapColorTransferFunction();
+
+#ifdef DEBUG_RANGE
+    std::cout << "DEBUG_RANGE current range = " << this->CurrentRange[0]
+              << " " << this->CurrentRange[1] << std::endl;
+#endif
+  }
+}
+
+// ----------------------------------------------------------------------------
+void vtkDiscretizableColorTransferChart::RemapColorTransferFunction()
+{
+  if (this->ColorTransferFunction == CTK_NULLPTR)
+  {
+    return;
+  }
+
+  double newRange[2];
+  newRange[0] = this->CurrentRange[0];
+  newRange[1] = this->CurrentRange[1];
+
+  if (newRange[0] == newRange[1])
+  {
+    newRange[1] += std::numeric_limits<double>::epsilon();
+  }
+  else if (this->CurrentRange[0] > this->CurrentRange[1])
+  {
+    newRange[0] = 0.;
+    newRange[1] = 255.;
   }
 
-  if (this->ColorTransferFunction != CTK_NULLPTR)
+  double* oldRange = this->ColorTransferFunction->GetRange();
+  if (oldRange[0] == newRange[0]
+   && oldRange[1] == newRange[1])
   {
-    this->ControlPoints->StartProcessing();
-    ctk::remapColorScale(this->ColorTransferFunction,
-      this->CurrentRange[0], this->CurrentRange[1]);
-    this->ControlPoints->EndProcessing();
+    return;
   }
+
+  this->ControlPoints->StartProcessing();
+  ctk::remapColorScale(this->ColorTransferFunction, newRange[0], newRange[1]);
+  this->ControlPoints->EndProcessing();
 }
 
 // ----------------------------------------------------------------------------
@@ -450,6 +512,32 @@ double* vtkDiscretizableColorTransferChart::GetCurrentRange()
 }
 
 // ----------------------------------------------------------------------------
+void vtkDiscretizableColorTransferChart::SetOriginalRange(double min, double max)
+{
+  if (min == this->OriginalRange[0]
+   && max == this->OriginalRange[1])
+  {
+    return;
+  }
+
+  this->OriginalRange[0] = min;
+  this->OriginalRange[1] = max;
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE original range = " << this->OriginalRange[0]
+            << " " << this->OriginalRange[1] << std::endl;
+#endif
+
+  if (this->OriginalRange[0] <= this->OriginalRange[1])
+  {
+    double currentRange[2];
+    currentRange[0] = vtkMath::ClampValue(this->CurrentRange[0], this->OriginalRange[0], this->OriginalRange[1]);
+    currentRange[1] = vtkMath::ClampValue(this->CurrentRange[1], this->OriginalRange[0], this->OriginalRange[1]);
+    this->SetCurrentRange(currentRange[0], currentRange[1]);
+  }
+}
+
+// ----------------------------------------------------------------------------
 void vtkDiscretizableColorTransferChart::CenterRange(double center)
 {
   double width = this->CurrentRange[1] - this->CurrentRange[0];
@@ -476,6 +564,10 @@ vtkDiscretizableColorTransferChart::GetControlPointsItem()
 // ----------------------------------------------------------------------------
 bool vtkDiscretizableColorTransferChart::IsProcessingColorTransferFunction() const
 {
+  if (this->ControlPoints == CTK_NULLPTR)
+  {
+    return false;
+  }
   return this->ControlPoints->IsProcessing();
 }
 

+ 4 - 1
Libs/Visualization/VTK/Core/vtkDiscretizableColorTransferChart.h

@@ -54,13 +54,16 @@ public:
 
   ///Set/Get the data range
   void SetDataRange(double min, double max);
-  double* GetDataRange();
 
   /// Set/Get the current range
   ///
   /// Set will clamp values into the current dataRange
   void SetCurrentRange(double min, double max);
   double* GetCurrentRange();
+  void RemapColorTransferFunction();
+
+  ///Set/Get the original range
+  void SetOriginalRange(double min, double max);
 
   /// Center the current position to the given point
   void CenterRange(double center);

+ 87 - 71
Libs/Visualization/VTK/Core/vtkScalarsToColorsContextItem.cpp

@@ -70,8 +70,10 @@ vtkScalarsToColorsContextItem::vtkScalarsToColorsContextItem()
 
   this->LastSceneSize = vtkVector2i(0, 0);
 
-  this->LimitRange[0] = VTK_DOUBLE_MIN;
-  this->LimitRange[1] = VTK_DOUBLE_MAX;
+  this->DataRange[0] = VTK_DOUBLE_MAX;
+  this->DataRange[1] = VTK_DOUBLE_MIN;
+  this->VisibleRange[0] = VTK_DOUBLE_MAX;
+  this->VisibleRange[1] = VTK_DOUBLE_MIN;
 
   vtkSmartPointer<vtkBrush> b = vtkSmartPointer<vtkBrush>::New();
   b->SetOpacityF(0);
@@ -93,7 +95,15 @@ vtkScalarsToColorsContextItem::vtkScalarsToColorsContextItem()
     vtkSmartPointer<vtkScalarsToColorsPreviewChart>::New();
   AddItem(this->PreviewChart.GetPointer());
 
-  this->SetColorTransferFunction(CTK_NULLPTR);
+  vtkSmartPointer<vtkPiecewiseFunction> opacityFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
+  opacityFunction->AddPoint(0.0, 0.0);
+  opacityFunction->AddPoint(255.0, 1.0);
+
+  this->ColorTransferFunction = vtkSmartPointer<vtkDiscretizableColorTransferFunction>::New();
+  this->ColorTransferFunction->SetScalarOpacityFunction(opacityFunction);
+  this->ColorTransferFunction->EnableOpacityMappingOn();
+
+  this->BuildColorTransferFunction();
 }
 
 // ----------------------------------------------------------------------------
@@ -105,78 +115,66 @@ vtkScalarsToColorsContextItem::~vtkScalarsToColorsContextItem()
 }
 
 // ----------------------------------------------------------------------------
-void vtkScalarsToColorsContextItem::SetColorTransferFunction(
+void vtkScalarsToColorsContextItem::CopyColorTransferFunction(
   vtkScalarsToColors* ctf)
 {
+  this->ResetColorTransferFunction();
+
   if (ctf == CTK_NULLPTR)
   {
-    this->SetDiscretizableColorTransferFunction(CTK_NULLPTR);
-    return;
-  }
+    this->SetVisibleRange(0, 255);
 
-  if (ctf->IsA("vtkDiscretizableColorTransferFunction"))
-  {
-    this->SetDiscretizableColorTransferFunction(
-      vtkDiscretizableColorTransferFunction::SafeDownCast(ctf));
+    vtkSmartPointer<vtkPiecewiseFunction> pf = vtkSmartPointer<vtkPiecewiseFunction>::New();
+    pf->AddPoint(0.0, 0.0);
+    pf->AddPoint(255.0, 1.0);
+    vtkSmartPointer<vtkDiscretizableColorTransferFunction> dctf = vtkSmartPointer<vtkDiscretizableColorTransferFunction>::New();
+    dctf->EnableOpacityMappingOn();
+    this->ColorTransferFunction->DeepCopy(dctf);
+    this->ColorTransferFunction->GetScalarOpacityFunction()->DeepCopy(pf);
+    this->ColorTransferFunction->EnableOpacityMappingOn();
   }
-  else if (ctf->IsA("vtkColorTransferFunction"))
+  else
   {
-    vtkSmartPointer<vtkColorTransferFunction> newCtf =
-      vtkColorTransferFunction::SafeDownCast(ctf);
-
-    vtkSmartPointer<vtkDiscretizableColorTransferFunction> dctf =
-      vtkSmartPointer<vtkDiscretizableColorTransferFunction>::New();
-    dctf->vtkColorTransferFunction::DeepCopy(newCtf);
-
-    vtkSmartPointer<vtkPiecewiseFunction> opacityFunction =
-      vtkSmartPointer<vtkPiecewiseFunction>::New();
-    opacityFunction->AddPoint(0.0, 0.0);
-    opacityFunction->AddPoint(255.0, 1.0);
-    dctf->SetScalarOpacityFunction(opacityFunction);
-    dctf->EnableOpacityMappingOn();
-
-    this->SetDiscretizableColorTransferFunction(dctf);
+    this->SetVisibleRange(ctf->GetRange()[0], ctf->GetRange()[1]);
+
+    if (ctf->IsA("vtkDiscretizableColorTransferFunction"))
+    {
+      vtkDiscretizableColorTransferFunction* dctf = vtkDiscretizableColorTransferFunction::SafeDownCast(ctf);
+      vtkPiecewiseFunction* pf = dctf->GetScalarOpacityFunction();
+      this->ColorTransferFunction->DeepCopy(dctf);
+      this->ColorTransferFunction->GetScalarOpacityFunction()->DeepCopy(pf);
+      this->ColorTransferFunction->EnableOpacityMappingOn();
+    }
+    else
+    {
+      this->ColorTransferFunction->DeepCopy(ctf);
+    }
   }
+
+  this->BuildColorTransferFunction();
 }
 
 // ----------------------------------------------------------------------------
-void vtkScalarsToColorsContextItem::SetDiscretizableColorTransferFunction(
-  vtkDiscretizableColorTransferFunction* colorTransfer)
+void vtkScalarsToColorsContextItem::ResetColorTransferFunction()
 {
-  vtkSmartPointer<vtkCompositeControlPointsItem> oldControlPoints =
-    this->EditorChart->GetControlPointsItem();
-
-  if (oldControlPoints != CTK_NULLPTR)
-  {
-    oldControlPoints->RemoveObservers(vtkCommand::EndEvent);
-    oldControlPoints->RemoveObservers(
-      vtkControlPointsItem::CurrentPointEditEvent);
-  }
-
-  this->ColorTransferFunction = colorTransfer;
+  this->EditorChart->SetColorTransferFunction(0);
+  this->PreviewChart->SetColorTransferFunction(0);
+  this->HistogramChart->SetLookupTable(0);
+}
 
+// ----------------------------------------------------------------------------
+void vtkScalarsToColorsContextItem::BuildColorTransferFunction()
+{
   this->EditorChart->SetColorTransferFunction(this->ColorTransferFunction);
-
   this->PreviewChart->SetColorTransferFunction(this->ColorTransferFunction);
-
   this->HistogramChart->SetLookupTable(this->ColorTransferFunction);
 
   vtkSmartPointer<vtkCompositeControlPointsItem> controlPoints =
     this->EditorChart->GetControlPointsItem();
-
   controlPoints->AddObserver(vtkCommand::EndEvent,
     this->PrivateEventForwarder, &EventForwarder::ForwardEvent);
   controlPoints->AddObserver(vtkControlPointsItem::CurrentPointEditEvent,
     this->PrivateEventForwarder, &EventForwarder::ForwardEvent);
-
-  /// Set the preview chart range to the color transfer function range
-  this->RecalculateChartsRange();
-}
-
-// ----------------------------------------------------------------------------
-vtkScalarsToColors* vtkScalarsToColorsContextItem::GetColorTransferFunction()
-{
-  return this->ColorTransferFunction;
 }
 
 // ----------------------------------------------------------------------------
@@ -197,19 +195,32 @@ void vtkScalarsToColorsContextItem::SetHistogramTable(vtkTable* table,
 // ----------------------------------------------------------------------------
 void vtkScalarsToColorsContextItem::SetDataRange(double min, double max)
 {
+  if (min == this->DataRange[0]
+   && max == this->DataRange[1])
+  {
+    return;
+  }
+
+  this->DataRange[0] = min;
+  this->DataRange[1] = max;
   this->EditorChart->SetDataRange(min, max);
-  this->RecalculateChartsRange();
 }
 
 // ----------------------------------------------------------------------------
 double* vtkScalarsToColorsContextItem::GetDataRange()
 {
-  return this->EditorChart->GetDataRange();
+  return this->DataRange;
 }
 
 // ----------------------------------------------------------------------------
 void vtkScalarsToColorsContextItem::SetCurrentRange(double min, double max)
 {
+  if (min == this->GetCurrentRange()[0]
+   && max == this->GetCurrentRange()[1])
+  {
+    return;
+  }
+
   this->EditorChart->SetCurrentRange(min, max);
 }
 
@@ -220,40 +231,45 @@ double* vtkScalarsToColorsContextItem::GetCurrentRange()
 }
 
 // ----------------------------------------------------------------------------
-void vtkScalarsToColorsContextItem::CenterRange(double center)
+void vtkScalarsToColorsContextItem::SetVisibleRange(double min, double max)
 {
-  this->EditorChart->CenterRange(center);
+  if (min == this->VisibleRange[0]
+   && max == this->VisibleRange[1])
+  {
+    return;
+  }
+
+  this->VisibleRange[0] = min;
+  this->VisibleRange[1] = max;
+  this->EditorChart->SetOriginalRange(min, max);
+  this->RecalculateChartsRange();
 }
 
 // ----------------------------------------------------------------------------
-double* vtkScalarsToColorsContextItem::GetLimitRange()
+double* vtkScalarsToColorsContextItem::GetVisibleRange()
 {
-  return LimitRange;
+  return this->VisibleRange;
 }
 
 // ----------------------------------------------------------------------------
-void vtkScalarsToColorsContextItem::RecalculateChartsRange()
+void vtkScalarsToColorsContextItem::CenterRange(double center)
 {
-  if (this->GetDiscretizableColorTransferFunction() == CTK_NULLPTR)
-  {
-    return;
-  }
-
-  /// Recalculate limit range
-  double* ctfRange = this->GetDiscretizableColorTransferFunction()->GetRange();
-  this->LimitRange[0] = std::min(ctfRange[0], this->GetDataRange()[0]);
-  this->LimitRange[1] = std::max(ctfRange[1], this->GetDataRange()[1]);
+  this->EditorChart->CenterRange(center);
+}
 
+// ----------------------------------------------------------------------------
+void vtkScalarsToColorsContextItem::RecalculateChartsRange()
+{
   this->EditorChart->GetAxis(vtkAxis::BOTTOM)->SetUnscaledRange(
-    this->LimitRange);
+    this->VisibleRange);
   this->EditorChart->RecalculateBounds();
 
   this->HistogramChart->GetAxis(vtkAxis::BOTTOM)->SetUnscaledRange(
-    this->LimitRange);
+    this->VisibleRange);
   this->HistogramChart->RecalculateBounds();
 
   this->PreviewChart->GetAxis(vtkAxis::BOTTOM)->SetUnscaledRange(
-    this->LimitRange);
+    this->VisibleRange);
   this->PreviewChart->RecalculateBounds();
 }
 

+ 11 - 13
Libs/Visualization/VTK/Core/vtkScalarsToColorsContextItem.h

@@ -46,15 +46,11 @@ class CTK_VISUALIZATION_VTK_CORE_EXPORT vtkScalarsToColorsContextItem
 public:
   static vtkScalarsToColorsContextItem* New();
 
-  /// Set/Get the color transfer function as a vtkScalarsToColors
-  void SetColorTransferFunction(vtkScalarsToColors* ctf);
-  vtkScalarsToColors* GetColorTransferFunction();
-
-  /// Set/Get the color transfer function as a
-  /// vtkDiscretizableColorTransferFunction
-  void SetDiscretizableColorTransferFunction(
-    vtkDiscretizableColorTransferFunction* dctf);
+  /// Copy the color transfer function as a vtkDiscretizableColorTransferFunction
+  void CopyColorTransferFunction(vtkScalarsToColors* ctf);
   vtkDiscretizableColorTransferFunction* GetDiscretizableColorTransferFunction();
+  void ResetColorTransferFunction();
+  void BuildColorTransferFunction();
 
   /// Set the table used by the histogram chart
   void SetHistogramTable(vtkTable* table,
@@ -75,12 +71,13 @@ public:
   void SetCurrentRange(double min, double max);
   double* GetCurrentRange();
 
+  /// Get/Set the range displayed in the widget
+  void SetVisibleRange(double min, double max);
+  double* GetVisibleRange();
+
   /// Center the color tranfer function around \center
   void CenterRange(double center);
 
-  /// Return the range including both the transfer function and data extents
-  double* GetLimitRange();
-
   /// Update charts range to match data and color transfer function ranges
   void RecalculateChartsRange();
 
@@ -96,6 +93,7 @@ public:
   bool IsProcessingColorTransferFunction() const;
 
 protected:
+
   vtkSmartPointer<vtkDiscretizableColorTransferChart> EditorChart;
   vtkSmartPointer<vtkScalarsToColorsPreviewChart> PreviewChart;
   vtkSmartPointer<vtkScalarsToColorsHistogramChart> HistogramChart;
@@ -109,8 +107,8 @@ private:
   /// Cached geometry of the scene
   vtkVector2i LastSceneSize;
 
-  /// Widest possible range for charts
-  double LimitRange[2];
+  double DataRange[2];
+  double VisibleRange[2];
 
   /// Internal event forwarder
   class EventForwarder;

BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/minus.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/plus.png


+ 76 - 79
Libs/Visualization/VTK/Widgets/Resources/UI/ctkVTKDiscretizableColorTransferWidget.ui

@@ -14,16 +14,7 @@
    <string>Color Transfer Function</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <property name="leftMargin">
-    <number>0</number>
-   </property>
-   <property name="topMargin">
-    <number>0</number>
-   </property>
-   <property name="rightMargin">
-    <number>0</number>
-   </property>
-   <property name="bottomMargin">
+   <property name="margin">
     <number>0</number>
    </property>
    <item row="9" column="1">
@@ -33,79 +24,32 @@
      </property>
     </widget>
    </item>
-   <item row="9" column="3">
-    <widget class="QToolButton" name="optionButton">
-     <property name="contextMenuPolicy">
-      <enum>Qt::DefaultContextMenu</enum>
-     </property>
-     <property name="toolTip">
-      <string>Other options</string>
-     </property>
-    </widget>
-   </item>
    <item row="10" column="1" colspan="3">
-    <widget class="ctkRangeWidget" name="rangeSlider"/>
+    <widget class="ctkRangeWidget" name="rangeSlider" native="true"/>
    </item>
-   <item row="7" column="3">
-    <widget class="QToolButton" name="invertColorTransferFunctionButton">
+   <item row="1" column="3">
+    <widget class="QToolButton" name="resetRangeButton">
      <property name="toolTip">
-      <string>Invert color map</string>
+      <string>Reset current range to visible range</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/invert.png</normaloff>:/Icons/invert.png</iconset>
-     </property>
-     <property name="popupMode">
-      <enum>QToolButton::DelayedPopup</enum>
+       <normaloff>:/Icons/resetRange.png</normaloff>:/Icons/resetRange.png</iconset>
      </property>
     </widget>
    </item>
-   <!--
-   <item row="3" column="2" rowspan="7">
-    <widget class="QVTKWidget" name="scalarsToColorsView" native="true"/>
-   </item>
-   -->
-   <item row="3" column="3">
-    <widget class="QToolButton" name="resetRangeButton">
+   <item row="2" column="3">
+    <widget class="QToolButton" name="centerRangeButton">
      <property name="toolTip">
-      <string>Reset to data range</string>
+      <string>Center current range on median</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/resetRange.png</normaloff>:/Icons/resetRange.png</iconset>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="1" rowspan="5">
-    <widget class="ctkDoubleSlider" name="opacitySlider">
-     <property name="value">
-      <double>1.000000000000000</double>
-     </property>
-     <property name="singleStep">
-      <double>0.100000000000000</double>
-     </property>
-     <property name="pageStep">
-      <double>0.200000000000000</double>
-     </property>
-     <property name="minimum">
-      <double>0.000001000000000</double>
-     </property>
-     <property name="maximum">
-      <double>1.000000000000000</double>
-     </property>
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="1">
-    <widget class="QLabel" name="maxOpacityLabel">
-     <property name="text">
-      <string>1</string>
+       <normaloff>:/Icons/resetRangeCustom.png</normaloff>:/Icons/resetRangeCustom.png</iconset>
      </property>
     </widget>
    </item>
-   <item row="4" column="3">
+   <item row="3" column="3">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
@@ -118,14 +62,25 @@
      </property>
     </spacer>
    </item>
+   <item row="4" column="3">
+    <widget class="QToolButton" name="zoomInButton">
+     <property name="toolTip">
+      <string>Zoom in</string>
+     </property>
+     <property name="icon">
+      <iconset resource="../ctkVTKWidgets.qrc">
+       <normaloff>:/Icons/plus.png</normaloff>:/Icons/plus.png</iconset>
+     </property>
+    </widget>
+   </item>
    <item row="5" column="3">
-    <widget class="QToolButton" name="centerRangeButton">
+    <widget class="QToolButton" name="zoomOutButton">
      <property name="toolTip">
-      <string>Center current range on median</string>
+      <string>Zoom out</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/resetRangeCustom.png</normaloff>:/Icons/resetRangeCustom.png</iconset>
+       <normaloff>:/Icons/minus.png</normaloff>:/Icons/minus.png</iconset>
      </property>
     </widget>
    </item>
@@ -142,6 +97,20 @@
      </property>
     </spacer>
    </item>
+   <item row="7" column="3">
+    <widget class="QToolButton" name="invertColorTransferFunctionButton">
+     <property name="toolTip">
+      <string>Invert color map</string>
+     </property>
+     <property name="icon">
+      <iconset resource="../ctkVTKWidgets.qrc">
+       <normaloff>:/Icons/invert.png</normaloff>:/Icons/invert.png</iconset>
+     </property>
+     <property name="popupMode">
+      <enum>QToolButton::DelayedPopup</enum>
+     </property>
+    </widget>
+   </item>
    <item row="8" column="3">
     <spacer name="verticalSpacer_3">
      <property name="orientation">
@@ -155,9 +124,45 @@
      </property>
     </spacer>
    </item>
-   <item row="2" column="1" colspan="3">
+   <item row="9" column="3">
+    <widget class="QToolButton" name="optionButton">
+     <property name="contextMenuPolicy">
+      <enum>Qt::DefaultContextMenu</enum>
+     </property>
+     <property name="toolTip">
+      <string>Other options</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1" colspan="2">
     <widget class="ctkVTKScalarsToColorsComboBox" name="scalarsToColorsSelector"/>
    </item>
+   <item row="2" column="1">
+    <widget class="QLabel" name="maxOpacityLabel">
+     <property name="text">
+      <string>1</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1" rowspan="6">
+    <widget class="ctkDoubleSlider" name="opacitySlider" native="true">
+     <property name="value" stdset="0">
+      <double>1.000000000000000</double>
+     </property>
+     <property name="singleStep" stdset="0">
+      <double>0.100000000000000</double>
+     </property>
+     <property name="pageStep" stdset="0">
+      <double>0.200000000000000</double>
+     </property>
+     <property name="minimum" stdset="0">
+      <double>0.000001000000000</double>
+     </property>
+     <property name="maximum" stdset="0">
+      <double>1.000000000000000</double>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>
@@ -176,14 +181,6 @@
    <extends>QComboBox</extends>
    <header>ctkVTKScalarsToColorsComboBox.h</header>
   </customwidget>
-  <!--
-  <customwidget>
-   <class>QVTKWidget</class>
-   <extends>QWidget</extends>
-   <header>QVTKWidget.h</header>
-   <container>1</container>
-  </customwidget>
-  -->
  </customwidgets>
  <resources>
   <include location="../ctkVTKWidgets.qrc"/>

+ 390 - 105
Libs/Visualization/VTK/Widgets/ctkVTKDiscretizableColorTransferWidget.cpp

@@ -66,6 +66,7 @@
 #include <vtkScalarsToColors.h>
 #include <vtkTable.h>
 
+//#define DEBUG_RANGE
 
 
 // ----------------------------------------------------------------------------
@@ -91,6 +92,7 @@ public:
   vtkSmartPointer<vtkScalarsToColorsContextItem> scalarsToColorsContextItem;
   vtkSmartPointer<vtkContextView> scalarsToColorsContextView;
   vtkSmartPointer<vtkEventQtSlotConnect> eventLink;
+  vtkSmartPointer<vtkImageAccumulate> histogramFilter;
 
   ///Option part
   ctkColorPickerButton* nanButton;
@@ -147,10 +149,12 @@ void ctkVTKDiscretizableColorTransferWidgetPrivate::setupUi(QWidget* widget)
 #else
   this->ScalarsToColorsView = new QVTKWidget;
 #endif
-  this->gridLayout->addWidget(this->ScalarsToColorsView, 3, 2, 7, 1);
+  this->gridLayout->addWidget(this->ScalarsToColorsView, 2, 2, 8, 1);
+
+  this->scalarsToColorsContextItem = vtkSmartPointer<vtkScalarsToColorsContextItem>::New();
+  vtkDiscretizableColorTransferFunction* ctf = this->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction();
+  ctf->AddObserver(vtkCommand::ModifiedEvent, this->colorTransferFunctionModified);
 
-  this->scalarsToColorsContextItem =
-    vtkSmartPointer<vtkScalarsToColorsContextItem>::New();
   this->scalarsToColorsContextView = vtkSmartPointer<vtkContextView> ::New();
 
 #if CTK_USE_QVTKOPENGLWIDGET
@@ -188,6 +192,12 @@ void ctkVTKDiscretizableColorTransferWidgetPrivate::setupUi(QWidget* widget)
   QObject::connect(opacitySlider, SIGNAL(valueChanged(double)),
     q, SLOT(setGlobalOpacity(double)));
 
+  QObject::connect(zoomOutButton, SIGNAL(clicked()),
+    q, SLOT(resetVisibleRangeToData()));
+
+  QObject::connect(zoomInButton, SIGNAL(clicked()),
+    q, SLOT(resetVisibleRangeToCTF()));
+
   QObject::connect(resetRangeButton, SIGNAL(clicked()),
     q, SLOT(resetColorTransferFunctionRange()));
 
@@ -320,128 +330,345 @@ ctkVTKDiscretizableColorTransferWidget::~ctkVTKDiscretizableColorTransferWidget(
 }
 
 // ----------------------------------------------------------------------------
-void ctkVTKDiscretizableColorTransferWidget::setColorTransferFunction(
-  vtkScalarsToColors* ctf)
+void ctkVTKDiscretizableColorTransferWidget::copyColorTransferFunction(
+  vtkScalarsToColors* ctf, bool useCtfRange)
 {
+#ifdef DEBUG_RANGE
+  if (ctf)
+  {
+    std::cout << "DEBUG_RANGE ctf input range = " << ctf->GetRange()[0]
+              << " " << ctf->GetRange()[1] << std::endl;
+  }
+#endif
+
   Q_D(ctkVTKDiscretizableColorTransferWidget);
 
-  vtkScalarsToColors* oldCtf =
-    d->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction();
-  if (oldCtf != CTK_NULLPTR)
+  if (useCtfRange)
+  {
+    // set cft, current range and visible range
+    d->scalarsToColorsContextItem->CopyColorTransferFunction(ctf);
+    emit(currentScalarsToColorsChanged(d->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction()));
+  }
+  else
   {
-    oldCtf->RemoveObserver(d->colorTransferFunctionModified);
+    // save old ranges
+    double ctfRange[2];
+    ctfRange[0] = this->getColorTransferFunctionRange()[0];
+    ctfRange[1] = this->getColorTransferFunctionRange()[1];
+    double visibleRange[2];
+    visibleRange[0] = this->getVisibleRange()[0];
+    visibleRange[1] = this->getVisibleRange()[1];
+
+    // set cft, current range and visible range
+    d->scalarsToColorsContextItem->CopyColorTransferFunction(ctf);
+    emit(currentScalarsToColorsChanged(d->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction()));
+
+    // set old ranges back
+    if (visibleRange[0] <= visibleRange[0])
+    {
+      this->setVisibleRange(visibleRange[0], visibleRange[1]);
+      this->setColorTransferFunctionRange(ctfRange[0], ctfRange[1]);
+    }
   }
 
-  ///Setting the transfer function to the scalarsToColorsContextItem convert
-  /// it to a vtkDiscretizableTransferFunction
-  d->scalarsToColorsContextItem->SetColorTransferFunction(ctf);
+  // todo should be replaced by callback when visible range changes
+  this->updateCtfWidgets();
 
-  ctf = d->scalarsToColorsContextItem->GetColorTransferFunction();
-  emit(currentScalarsToColorsChanged(ctf));
+  d->colorTransferFunctionModified->Execute(ctf, vtkCommand::ModifiedEvent, this);
+}
+
+// ----------------------------------------------------------------------------
+vtkDiscretizableColorTransferFunction*
+ctkVTKDiscretizableColorTransferWidget::discretizableColorTransferFunction()
+const
+{
+  Q_D(const ctkVTKDiscretizableColorTransferWidget);
+  return d->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction();
+}
 
-  if (ctf == CTK_NULLPTR)
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::setHistogramInputConnection(
+  vtkAlgorithmOutput* input, bool useInputDataRange)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  this->initializeHistogramAndDataRange(input);
+  if (useInputDataRange)
   {
-    d->rangeSlider->setRange(0., 255.);
-    d->rangeSlider->setValues(0., 1.);
-    d->rangeSlider->setEnabled(false);
-    d->previousOpacityValue = 0.0;
-    d->opacitySlider->setValue(d->previousOpacityValue);
-    d->opacitySlider->setEnabled(false);
-    d->optionButton->setEnabled(false);
-    d->resetRangeButton->setEnabled(false);
-    d->centerRangeButton->setEnabled(false);
-    d->invertColorTransferFunctionButton->setEnabled(false);
-    return;
+    // update visible range, which updates histogram
+    this->resetVisibleRange(ResetVisibleRange::UNION_DATA_AND_CTF);
   }
+  else
+  {
+    this->updateHistogram();
+  }
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::resetColorTransferFunctionRange(
+    ResetCTFRange resetMode)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  double newRange[2];
+  switch (resetMode)
+  {
+    case DATA:
+    {
+      double* dataRange = this->getDataRange();
+      newRange[0] = dataRange[0];
+      newRange[1] = dataRange[1];
+      break;
+    }
+    case VISIBLE:
+    {
+      double* visibleRange = this->getVisibleRange();
+      newRange[0] = visibleRange[0];
+      newRange[1] = visibleRange[1];
+      break;
+    }
+    default:
+      return;
+  }
+
+  this->setColorTransferFunctionRange(newRange[0], newRange[1]);
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::resetVisibleRange(
+    ResetVisibleRange resetMode)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  double newRange[2];
+  switch (resetMode)
+  {
+    case UNION_DATA_AND_CTF:
+    {
+      double* ctfRange = this->getColorTransferFunctionRange();
+      double* dataRange = this->getDataRange();
+      newRange[0] = std::min(dataRange[0], ctfRange[0]);
+      newRange[1] = std::max(dataRange[1], ctfRange[1]);
+      break;
+    }
+    case UNION_DATA_AND_VISIBLE:
+    {
+      double* visibleRange = this->getVisibleRange();
+      double* dataRange = this->getDataRange();
+      newRange[0] = std::min(dataRange[0], visibleRange[0]);
+      newRange[1] = std::max(dataRange[1], visibleRange[1]);
+      break;
+    }
+    case ONLY_DATA:
+    {
+      double* dataRange = this->getDataRange();
+      newRange[0] = dataRange[0];
+      newRange[1] = dataRange[1];
+      break;
+    }
+    case ONLY_CTF:
+    {
+      double* ctfRange = this->getColorTransferFunctionRange();
+      newRange[0] = ctfRange[0];
+      newRange[1] = ctfRange[1];
+      break;
+    }
+    default:
+      return;
+  }
+
+  this->setVisibleRange(newRange[0], newRange[1]);
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::updateCtfWidgets()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  if (this->discretizableColorTransferFunction() == CTK_NULLPTR)
+  {
+    this->disableCtfWidgets();
+  }
+  else
+  {
+    this->enableCtfWidgets();
+  }
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::disableCtfWidgets()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  d->rangeSlider->setRange(0., 255.);
+  d->rangeSlider->setValues(0., 1.);
+  d->rangeSlider->setEnabled(false);
+  d->previousOpacityValue = 0.0;
+  d->opacitySlider->setValue(d->previousOpacityValue);
+  d->opacitySlider->setEnabled(false);
+  d->optionButton->setEnabled(false);
+  d->resetRangeButton->setEnabled(false);
+  d->zoomInButton->setEnabled(false);
+  d->zoomOutButton->setEnabled(false);
+  d->centerRangeButton->setEnabled(false);
+  d->invertColorTransferFunctionButton->setEnabled(false);
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE slider range = " << 0
+            << " " << 255 << std::endl;
+  std::cout << "DEBUG_RANGE slider value = " << 0
+            << " " << 1 << std::endl;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::enableCtfWidgets()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
 
-  // Set sliders values depending on the new color transfer function
   d->rangeSlider->setEnabled(true);
   d->opacitySlider->setEnabled(true);
   d->optionButton->setEnabled(true);
   d->resetRangeButton->setEnabled(true);
+  d->zoomInButton->setEnabled(true);
+  d->zoomOutButton->setEnabled(true);
   d->centerRangeButton->setEnabled(true);
   d->invertColorTransferFunctionButton->setEnabled(true);
 
   d->previousOpacityValue = 1.0;
   d->opacitySlider->setValue(d->previousOpacityValue);
 
-  double* crng = d->scalarsToColorsContextItem->
-    GetDiscretizableColorTransferFunction()->GetRange();
-  double* limitRange = d->scalarsToColorsContextItem->GetLimitRange();
-  d->rangeSlider->setRange(limitRange[0], limitRange[1]);
-  d->rangeSlider->setValues(crng[0], crng[1]);
+  double* visibleRange = this->getVisibleRange();
+  double* ctfRange = this->getColorTransferFunctionRange();
+  d->rangeSlider->setRange(visibleRange[0], visibleRange[1]);
+  d->rangeSlider->setValues(ctfRange[0], ctfRange[1]);
 
-  ctf->AddObserver(
-    vtkCommand::ModifiedEvent, d->colorTransferFunctionModified);
-  d->colorTransferFunctionModified->Execute(ctf, vtkCommand::ModifiedEvent,
-    this);
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE slider range = " << visibleRange[0]
+            << " " << visibleRange[1] << std::endl;
+  std::cout << "DEBUG_RANGE slider value = " << ctfRange[0]
+            << " " << ctfRange[1] << std::endl;
+#endif
 }
 
 // ----------------------------------------------------------------------------
-vtkScalarsToColors*
-ctkVTKDiscretizableColorTransferWidget::colorTransferFunction() const
+void ctkVTKDiscretizableColorTransferWidget::initializeHistogramAndDataRange(
+    vtkAlgorithmOutput* input)
 {
-  Q_D(const ctkVTKDiscretizableColorTransferWidget);
-  return d->scalarsToColorsContextItem->GetColorTransferFunction();
-}
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
 
-// ----------------------------------------------------------------------------
-vtkDiscretizableColorTransferFunction*
-ctkVTKDiscretizableColorTransferWidget::discretizableColorTransferFunction()
-const
-{
-  Q_D(const ctkVTKDiscretizableColorTransferWidget);
-  return d->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction();
+  if (!input)
+  {
+    d->histogramFilter = nullptr;
+    d->dataMean = 0.;
+    this->setDataRange(VTK_DOUBLE_MAX, VTK_DOUBLE_MIN);
+    return;
+  }
+
+  d->histogramFilter = vtkSmartPointer<vtkImageAccumulate>::New();
+  d->histogramFilter->SetInputConnection(input);
+
+  // use histogram filter to compute min max values
+  d->histogramFilter->Update();
+  d->dataMean = d->histogramFilter->GetMean()[0];
+  this->setDataRange(d->histogramFilter->GetMin()[0], d->histogramFilter->GetMax()[0]);
+
+#ifdef DEBUG_RANGE
+  std::cout << "DEBUG_RANGE histo real range = " << *d->histogramFilter->GetMin()
+            << " " << *d->histogramFilter->GetMax() << std::endl;
+  vtkImageData* histogram = d->histogramFilter->GetOutput();
+  int dims[3];
+  histogram->GetDimensions(dims);
+  std::cout << "DEBUG_RANGE histo = ";
+  for(vtkIdType i = 0; i < dims[0]; ++i)
+  {
+      std::cout << *(static_cast<int*>(histogram->GetScalarPointer(i, 0, 0))) << " ";
+  }
+  std::cout << std::endl;
+#endif
 }
 
 // ----------------------------------------------------------------------------
-void ctkVTKDiscretizableColorTransferWidget::setHistogram(
-  vtkImageAccumulate* histogram)
+void ctkVTKDiscretizableColorTransferWidget::updateHistogram()
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
-  histogram->Update();
-  d->dataRange[0] = histogram->GetMin()[0];
-  d->dataRange[1] = histogram->GetMax()[0];
-  d->dataMean = histogram->GetMean()[0];
 
-  int* output = static_cast<int*>(histogram->GetOutput()->GetScalarPointer());
-  double spacing = histogram->GetComponentSpacing()[0];
-  double bin = histogram->GetComponentOrigin()[0];
+  // convert histogram data into table
+
+  std::string binsName = "image_extents";
+  std::string frequenciesName = "Frequency";
 
   vtkSmartPointer<vtkDoubleArray> bins =
     vtkSmartPointer<vtkDoubleArray>::New();
   bins->SetNumberOfComponents(1);
-  bins->SetNumberOfTuples(255);
-  bins->SetName("image_extents");
+  bins->SetName(binsName.c_str());
+
   vtkSmartPointer<vtkIntArray> frequencies =
     vtkSmartPointer<vtkIntArray>::New();
   frequencies->SetNumberOfComponents(1);
-  frequencies->SetNumberOfTuples(255);
-  frequencies->SetName("Frequency");
-
-  for (unsigned int j = 0; j < 255; ++j)
-  {
-    bins->SetTuple1(j, bin);
-    bin += spacing;
-    frequencies->SetTuple1(j, *output++);
-  }
+  frequencies->SetName(frequenciesName.c_str());
 
   vtkNew<vtkTable> table;
   table->AddColumn(bins);
   table->AddColumn(frequencies);
 
-  d->scalarsToColorsContextItem->SetHistogramTable(table.Get(),
-    "image_extents", "Frequency");
+  // fill bins and frequencies
 
-  d->scalarsToColorsContextItem->SetDataRange(d->dataRange[0], d->dataRange[1]);
+  if (d->histogramFilter == nullptr)
+  {
+    bins->SetNumberOfTuples(1);
+    bins->SetTuple1(0, 0);
 
-  if (this->discretizableColorTransferFunction() == CTK_NULLPTR)
+    frequencies->SetNumberOfTuples(1);
+    frequencies->SetTuple1(0, 0);
+  }
+  else
   {
-    return;
+
+    double* visibleRange = d->scalarsToColorsContextItem->GetVisibleRange();
+
+    int extent = d->histogramFilter->GetComponentExtent()[1];
+    double origin = visibleRange[0] - std::numeric_limits<double>::epsilon();
+    double spacing = (visibleRange[1] - visibleRange[0] + 2 * std::numeric_limits<double>::epsilon())
+        / static_cast<double>(extent + 1);
+
+    // recompute histogram in data range
+    d->histogramFilter->SetComponentOrigin(origin, 0, 0);
+    d->histogramFilter->SetComponentSpacing(spacing, 0, 0);
+    d->histogramFilter->Update();
+
+    vtkImageData* histogram = d->histogramFilter->GetOutput();
+    int* output = static_cast<int*>(histogram->GetScalarPointer());
+
+#ifdef DEBUG_RANGE
+    std::cout << "DEBUG_RANGE histo input range = " << origin
+              << " " << origin + extent + 1 * spacing << std::endl;
+    std::cout << "DEBUG_RANGE histo real range = " << *d->histogramFilter->GetMin()
+              << " " << *d->histogramFilter->GetMax() << std::endl;
+    int dims[3];
+    histogram->GetDimensions(dims);
+    std::cout << "DEBUG_RANGE histo = ";
+    for(vtkIdType i = 0; i < dims[0]; ++i)
+    {
+        std::cout << *(static_cast<int*>(histogram->GetScalarPointer(i, 0, 0))) << " ";
+    }
+    std::cout << std::endl;
+#endif
+
+    bins->SetNumberOfTuples(extent + 1);
+    frequencies->SetNumberOfTuples(extent + 1);
+
+    double bin = origin;
+    for (int j = 0; j < extent + 1; ++j)
+    {
+      bins->SetTuple1(j, bin);
+      bin += spacing;
+      frequencies->SetTuple1(j, *output++);
+    }
   }
 
-  double* limitRange = d->scalarsToColorsContextItem->GetLimitRange();
-  d->rangeSlider->setRange(limitRange[0], limitRange[1]);
+  d->scalarsToColorsContextItem->SetHistogramTable(table.Get(),
+    binsName.c_str(), frequenciesName.c_str());
 }
 
 // ----------------------------------------------------------------------------
@@ -450,28 +677,8 @@ void ctkVTKDiscretizableColorTransferWidget::onPaletteIndexChanged(
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
 
-  if (ctf == CTK_NULLPTR)
-  {
-    this->setColorTransferFunction(ctf);
-    return;
-  }
-
-  if (ctf->IsA("vtkDiscretizableColorTransferFunction"))
-  {
-    vtkNew<vtkDiscretizableColorTransferFunction> newCtf;
-    vtkNew<vtkPiecewiseFunction> newPf;
-    newCtf->DeepCopy(vtkDiscretizableColorTransferFunction::SafeDownCast(ctf));
-    newPf->DeepCopy(vtkDiscretizableColorTransferFunction::SafeDownCast(ctf)->GetScalarOpacityFunction());
-    newCtf->SetScalarOpacityFunction(newPf.Get());
-    newCtf->EnableOpacityMappingOn();
-    this->setColorTransferFunction(newCtf.Get());
-  }
-  else if (ctf->IsA("vtkColorTransferFunction"))
-  {
-    vtkNew<vtkColorTransferFunction> newCtf;
-    newCtf->DeepCopy(vtkColorTransferFunction::SafeDownCast(ctf));
-    this->setColorTransferFunction(newCtf.Get());
-  }
+  this->copyColorTransferFunction(ctf);
+  d->ScalarsToColorsView->GetInteractor()->Render();
 }
 
 // ----------------------------------------------------------------------------
@@ -513,16 +720,83 @@ void ctkVTKDiscretizableColorTransferWidget::setNumberOfDiscreteValues(
 }
 
 // ----------------------------------------------------------------------------
+double* ctkVTKDiscretizableColorTransferWidget::getColorTransferFunctionRange()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  return d->scalarsToColorsContextItem->GetCurrentRange();
+}
+
+// ----------------------------------------------------------------------------
 void ctkVTKDiscretizableColorTransferWidget::setColorTransferFunctionRange(
-  double minValue, double maxValue)
+  double min, double max)
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
-  if (minValue == d->scalarsToColorsContextItem->GetCurrentRange()[0] &&
-      maxValue == d->scalarsToColorsContextItem->GetCurrentRange()[1])
+
+  if (min == this->getColorTransferFunctionRange()[0]
+   && max == this->getColorTransferFunctionRange()[1])
   {
     return;
   }
-  d->scalarsToColorsContextItem->SetCurrentRange(minValue, maxValue);
+
+  if (max < min)
+  {
+    return;
+  }
+
+  d->scalarsToColorsContextItem->SetCurrentRange(min, max);
+}
+
+// ----------------------------------------------------------------------------
+double* ctkVTKDiscretizableColorTransferWidget::getVisibleRange()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  return d->scalarsToColorsContextItem->GetVisibleRange();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::setVisibleRange(
+    double min, double max)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  if (min == this->getVisibleRange()[0]
+   && max == this->getVisibleRange()[1])
+  {
+    return;
+  }
+
+  if (max < min)
+  {
+    return;
+  }
+
+  d->scalarsToColorsContextItem->SetVisibleRange(min, max);
+
+  // todo should be replaced by callback when visible range changes
+  this->updateHistogram();
+  this->updateCtfWidgets();
+}
+
+// ----------------------------------------------------------------------------
+double* ctkVTKDiscretizableColorTransferWidget::getDataRange()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  return d->scalarsToColorsContextItem->GetDataRange();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::setDataRange(
+    double min, double max)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  if (min == this->getDataRange()[0]
+   && max == this->getDataRange()[1])
+  {
+    return;
+  }
+
+  d->scalarsToColorsContextItem->SetDataRange(min, max);
 }
 
 // ----------------------------------------------------------------------------
@@ -546,13 +820,24 @@ void ctkVTKDiscretizableColorTransferWidget::onCurrentPointEdit()
 }
 
 // ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::resetVisibleRangeToData()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  this->resetVisibleRange(ResetVisibleRange::ONLY_DATA);
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::resetVisibleRangeToCTF()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  this->resetVisibleRange(ResetVisibleRange::ONLY_CTF);
+}
+
+// ----------------------------------------------------------------------------
 void ctkVTKDiscretizableColorTransferWidget::resetColorTransferFunctionRange()
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
-  if (d->dataRange[0] <= d->dataRange[1])
-  {
-    setColorTransferFunctionRange(d->dataRange[0], d->dataRange[1]);
-  }
+  this->resetColorTransferFunctionRange(ResetCTFRange::VISIBLE);
 }
 
 // ----------------------------------------------------------------------------

+ 36 - 5
Libs/Visualization/VTK/Widgets/ctkVTKDiscretizableColorTransferWidget.h

@@ -28,6 +28,7 @@ class ctkVTKScalarsToColorsComboBox;
 
 // VTK includes
 #include <vtkSmartPointer.h>
+class vtkAlgorithmOutput;
 class vtkDiscretizableColorTransferFunction;
 class vtkImageAccumulate;
 class vtkPiecewiseFunction;
@@ -51,20 +52,37 @@ public:
   explicit ctkVTKDiscretizableColorTransferWidget(QWidget* parent_ = CTK_NULLPTR);
   virtual ~ctkVTKDiscretizableColorTransferWidget();
 
-  void setColorTransferFunction(vtkScalarsToColors* ctf);
-  vtkScalarsToColors* colorTransferFunction() const;
+  enum ResetVisibleRange
+  {
+    UNION_DATA_AND_CTF,
+    UNION_DATA_AND_VISIBLE,
+    ONLY_DATA,
+    ONLY_CTF
+  };
+
+  enum ResetCTFRange
+  {
+    CTF,
+    DATA,
+    VISIBLE
+  };
+
+  void copyColorTransferFunction(vtkScalarsToColors* ctf, bool useCtfRange = false);
   vtkDiscretizableColorTransferFunction* discretizableColorTransferFunction() const;
 
-  void setHistogram(vtkImageAccumulate* hist);
+  void setHistogramInputConnection(vtkAlgorithmOutput* input, bool useInputDataRange = true);
 
   void setViewBackgroundColor(const QColor& i_color);
   QColor viewBackgroundColor() const;
 
   ctkVTKScalarsToColorsComboBox* scalarsToColorsSelector() const;
 
+  void resetVisibleRange(ResetVisibleRange resetMode);
+  void resetColorTransferFunctionRange(ResetCTFRange resetMode);
+
 signals:
   void currentScalarsToColorsModified();
-  void currentScalarsToColorsChanged(vtkScalarsToColors* ctf);
+  void currentScalarsToColorsChanged(vtkDiscretizableColorTransferFunction* ctf);
 
 public slots:
   void onCurrentPointEdit();
@@ -72,6 +90,8 @@ public slots:
 
   void setGlobalOpacity(double opacity);
 
+  void resetVisibleRangeToCTF();
+  void resetVisibleRangeToData();
   void resetColorTransferFunctionRange();
   void centerColorTransferFunctionRange();
   void invertColorTransferFunction();
@@ -79,11 +99,22 @@ public slots:
   void setNaNColor();
   void setDiscretize(bool checked);
   void setNumberOfDiscreteValues(int value);
-  void setColorTransferFunctionRange(double minValue, double maxValue);
+  void setColorTransferFunctionRange(double min, double max);
+  void setVisibleRange(double min, double max);
+  void setDataRange(double min, double max);
 
 protected:
   QScopedPointer<ctkVTKDiscretizableColorTransferWidgetPrivate> d_ptr;
 
+  double* getColorTransferFunctionRange();
+  double* getVisibleRange();
+  double* getDataRange();
+  void updateCtfWidgets();
+  void disableCtfWidgets();
+  void enableCtfWidgets();
+  void initializeHistogramAndDataRange(vtkAlgorithmOutput* input);
+  void updateHistogram();
+
 private:
   Q_DECLARE_PRIVATE(ctkVTKDiscretizableColorTransferWidget);
   Q_DISABLE_COPY(ctkVTKDiscretizableColorTransferWidget);