소스 검색

ENH: Add ctkNonLinearControlPoint, support midpoint/sharpness in ctkVTKColorTransferFunction

In order to support the midpoint/sharpness parameters of vtkColorTransferFunction, ctkNonLinearControlPoint has been added. The Hermit parameters (midpoing/sharpness) can't be converted in Bezier as their implementation in vtkColorTransferFunciton is not linear.
We introduced the structure ctkPoint to handle the control (sub)points.
Julien Finet 15 년 전
부모
커밋
02608f37ca

+ 21 - 0
Libs/Core/ctkTransferFunction.cpp

@@ -15,6 +15,21 @@
 #include "ctkTransferFunction.h"
 
 //-----------------------------------------------------------------------------
+ctkControlPoint::~ctkControlPoint()
+{ 
+}
+
+//-----------------------------------------------------------------------------
+ctkBezierControlPoint::~ctkBezierControlPoint()
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkNonLinearControlPoint::~ctkNonLinearControlPoint()
+{
+}
+
+//-----------------------------------------------------------------------------
 ctkTransferFunction::ctkTransferFunction(QObject* parentObject)
   :QObject(parentObject)
 {
@@ -30,3 +45,9 @@ ctkTransferFunction::~ctkTransferFunction()
   // this->ControlPoints->clear();
   // emit changed();
 }
+
+ctkBezierControlPoint*  ctkTransferFunction::toto()
+{
+  return new ctkBezierControlPoint();
+}
+

+ 33 - 14
Libs/Core/ctkTransferFunction.h

@@ -24,24 +24,43 @@
 /// CTK includes
 #include "CTKCoreExport.h"
 
-/// assumes the mapping is linear by default, if not, then subclasses must be 
-/// used
 //-----------------------------------------------------------------------------
-struct ctkControlPoint
+struct CTK_CORE_EXPORT ctkPoint
 {
-  virtual ~ctkControlPoint(){}
+  ctkPoint(){}
+  ctkPoint(qreal x, const QVariant& v)
+    :X(x),Value(v)
+  {  
+  }
+  qreal X;
   // QColor or qreal.
   QVariant Value;
-  qreal    Pos;
 };
 
 //-----------------------------------------------------------------------------
-struct ctkBezierControlPoint : public ctkControlPoint
+/// assumes the mapping is linear by default, if not, then subclasses must be 
+/// used
+struct CTK_CORE_EXPORT ctkControlPoint
 {
-  qreal P1;
-  QVariant ValueP1;
-  qreal P2;
-  QVariant ValueP2;
+  virtual ~ctkControlPoint();
+  inline const qreal& x()const {return this->P.X;}
+  inline const QVariant& value()const {return this->P.Value;}
+  ctkPoint P;
+};
+
+//-----------------------------------------------------------------------------
+struct CTK_CORE_EXPORT ctkBezierControlPoint : public ctkControlPoint
+{
+  virtual ~ctkBezierControlPoint();
+  ctkPoint P1;
+  ctkPoint P2;
+};
+
+//-----------------------------------------------------------------------------
+struct CTK_CORE_EXPORT ctkNonLinearControlPoint : public ctkControlPoint
+{
+  virtual ~ctkNonLinearControlPoint();
+  QList<ctkPoint> SubPoints;
 };
 
 //-----------------------------------------------------------------------------
@@ -52,9 +71,9 @@ public:
   ctkTransferFunction(QObject* parent = 0);
   virtual ~ctkTransferFunction();
   
-  virtual ctkControlPoint* controlPoint(int index)const =0;
+  virtual ctkControlPoint* controlPoint(int index)const = 0;
   inline QVariant value(int index)const;
-  virtual QVariant value(qreal pos)const =0;
+  virtual QVariant value(qreal pos)const = 0;
   
   virtual int count()const = 0;
   inline void range(qreal rangeValues[2])const;
@@ -72,7 +91,7 @@ public:
   /// be careful with it, as changing the value might require
   /// more changes to ctkControlPoint.
   virtual void setControlPointValue(int index, const QVariant& value)=0;
-
+  virtual ctkBezierControlPoint* toto();
 signals:
   void changed();
 };
@@ -81,7 +100,7 @@ signals:
 QVariant ctkTransferFunction::value(int index)const
 {
   QSharedPointer<ctkControlPoint> cp(this->controlPoint(index));
-  return cp->Value;
+  return cp->P.Value;
 }
 
 //-----------------------------------------------------------------------------

+ 34 - 18
Libs/Visualization/VTK/Core/ctkVTKColorTransferFunction.cpp

@@ -86,6 +86,9 @@ QVariant ctkVTKColorTransferFunction::minValue()const
   for (int i = 0; i < this->count(); ++i)
     {
     d->ColorTransferFunction->GetColor(i, rgb);
+    Q_ASSERT(rgb[0] >= 0. && rgb[0] <= 1. &&
+             rgb[1] >= 0. && rgb[1] <= 1. &&
+             rgb[2] >= 0. && rgb[2] <= 1.);
     QColor color = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
     if ( qGray(color.red(), color.green(), color.blue()) <
          qGray(minValue.red(), minValue.green(), minValue.blue()))
@@ -110,6 +113,9 @@ QVariant ctkVTKColorTransferFunction::maxValue()const
   for (int i = 0; i < this->count(); ++i)
     {
     d->ColorTransferFunction->GetColor(i, rgb);
+    Q_ASSERT(rgb[0] >= 0. && rgb[0] <= 1. &&
+             rgb[1] >= 0. && rgb[1] <= 1. &&
+             rgb[2] >= 0. && rgb[2] <= 1.);
     QColor color = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
     if ( qGray(color.red(), color.green(), color.blue()) >
          qGray(minValue.red(), minValue.green(), minValue.blue()))
@@ -120,7 +126,6 @@ QVariant ctkVTKColorTransferFunction::maxValue()const
   return minValue;
 }
 
-
 //-----------------------------------------------------------------------------
 ctkControlPoint* ctkVTKColorTransferFunction::controlPoint(int index)const
 {
@@ -135,15 +140,18 @@ ctkControlPoint* ctkVTKColorTransferFunction::controlPoint(int index)const
            values[3] >= 0. && values[3] <= 1. &&  // Blue
            values[4] >= 0. && values[4] <= 1. &&  // MidPoint
            values[5] >= 0. && values[5] <= 1.);   // Sharpness
-  ctkBezierControlPoint* cp = new ctkBezierControlPoint();
   QColor rgb = QColor::fromRgbF(values[1], values[2], values[3]);
-  cp->Value = rgb;
-  cp->Pos = values[0];
-  // TODO convert P1, P2, Value1 and Value2 from midpoint/sharpness to bezier points
-  cp->P1 = values[0];
-  cp->ValueP1 = rgb;
-  index = (index + 1 )%this->count();
-  d->ColorTransferFunction->GetNodeValue(index, values);
+  if (index + 1 >= this->count())
+    {
+    ctkControlPoint* cp = new ctkControlPoint();
+    cp->P.X = values[0];
+    cp->P.Value = rgb;
+    return cp;
+    }
+  ctkNonLinearControlPoint* cp = new ctkNonLinearControlPoint();
+  cp->P.X = values[0];
+  cp->P.Value = rgb;
+  d->ColorTransferFunction->GetNodeValue(index + 1, values);
   Q_ASSERT(values[0] >= d->ColorTransferFunction->GetRange()[0] &&
            values[0] <= d->ColorTransferFunction->GetRange()[1] &&
            values[1] >= 0. && values[1] <= 1. &&  // Red
@@ -151,9 +159,17 @@ ctkControlPoint* ctkVTKColorTransferFunction::controlPoint(int index)const
            values[3] >= 0. && values[3] <= 1. &&  // Blue
            values[4] >= 0. && values[4] <= 1. &&  // MidPoint
            values[5] >= 0. && values[5] <= 1.);   // Sharpness
-  // TODO convert P1 and P2
-  cp->P2 = (index ? values[0] : cp->P1);
-  cp->ValueP2 = QColor::fromRgbF(values[1], values[2], values[3]);
+  double subPoints[30];
+  d->ColorTransferFunction->GetTable(cp->x(), values[0], 10, subPoints);
+  qreal interval = (values[0] - cp->x()) / 9.;
+  for(int i = 0; i < 10; ++i)
+    {
+    
+    QColor rgb = QColor::fromRgbF(subPoints[3*i], 
+                                  subPoints[3*i+1],
+                                  subPoints[3*i+2]);
+    cp->SubPoints << ctkPoint(cp->x() + interval*i, rgb);
+    }
   return cp;
 }
 
@@ -177,18 +193,18 @@ int ctkVTKColorTransferFunction::insertControlPoint(const ctkControlPoint& cp)
     {
     return index;
     }
-  QColor rgb = cp.Value.value<QColor>();
-  const ctkBezierControlPoint* bezierCp = dynamic_cast<const ctkBezierControlPoint*>(&cp);
-  if (bezierCp)
+  QColor rgb = cp.value().value<QColor>();
+  const ctkNonLinearControlPoint* nonLinearCp = dynamic_cast<const ctkNonLinearControlPoint*>(&cp);
+  if (nonLinearCp)
     {
-    // TODO convert P1 and P2
+    // TODO retrieve midpoint & sharpness
     index = d->ColorTransferFunction->AddRGBPoint(
-      cp.Pos, rgb.redF(), rgb.greenF(), rgb.blueF(), bezierCp->P1, bezierCp->P2);
+      cp.x(), rgb.redF(), rgb.greenF(), rgb.blueF());
     }
   else
     {
     index = d->ColorTransferFunction->AddRGBPoint(
-      cp.Pos, rgb.redF(), rgb.greenF(), rgb.blueF());
+      cp.x(), rgb.redF(), rgb.greenF(), rgb.blueF());
     }
   return index;
 }

+ 3 - 1
Libs/Visualization/VTK/Core/ctkVTKColorTransferFunction.h

@@ -24,7 +24,9 @@
 class vtkColorTransferFunction;
 class ctkVTKColorTransferFunctionPrivate;
 
-//-----------------------------------------------------------------------------
+///
+/// Transfer function for a vtkColorTransferFunction. 
+/// The value is an RGB QColor (no alpha supported)
 class CTK_VISUALIZATION_VTK_CORE_EXPORT ctkVTKColorTransferFunction: public ctkTransferFunction
 {
   Q_OBJECT;

+ 4 - 4
Libs/Visualization/VTK/Widgets/Testing/Cpp/ctkTransferFunctionWidgetTest1.cpp

@@ -37,8 +37,8 @@ int ctkTransferFunctionWidgetTest1(int argc, char * argv [] )
   vtkSmartPointer<vtkColorTransferFunction> ctf = 
     vtkSmartPointer<vtkColorTransferFunction>::New();
   //
-  ctf->AddRGBPoint(0., 1.,0.,0.);
-  ctf->AddRGBPoint(0.5, 0.,0.,1.);
+  ctf->AddRGBPoint(0., 1.,0.,0., 0.5, 0.);
+  //ctf->AddRGBPoint(0.5, 0.,0.,1.);
   ctf->AddRGBPoint(1., 0.,1.,0.);
   //ctf->AddHSVPoint(0., 0.,1.,1.);
   //ctf->AddHSVPoint(1., 0.66666,1.,1.);
@@ -49,10 +49,10 @@ int ctkTransferFunctionWidgetTest1(int argc, char * argv [] )
   // the widget is not really shown here, only when app.exec() is called
   transferFunctionWidget.show();
 
-  ctf->AddRGBPoint(0.7, 0.0,0.0,0.0);
+  //ctf->AddRGBPoint(0.7, 0.0,0.0,0.0);
   //ctkTransferFunctionWidget* toto = new ctkTransferFunctionWidget();
   QTimer autoExit;
   QObject::connect(&autoExit, SIGNAL(timeout()), &app, SLOT(quit()));
-  autoExit.start(1000);
+  //autoExit.start(1000);
   return app.exec();
 }

+ 97 - 33
Libs/Widgets/ctkTransferFunctionItems.cpp

@@ -118,30 +118,49 @@ QRectF ctkTransferFunctionItem::boundingRect()const
 }
 
 //-----------------------------------------------------------------------------
-QList<QPointF> ctkTransferFunctionItem::bezierParams(
+QList<ctkPoint> ctkTransferFunctionItem::bezierParams(
   ctkControlPoint* start, ctkControlPoint* end) const
 {
   Q_ASSERT(start);
   Q_ASSERT(end);
-  QList<QPointF> points; 
+  QList<ctkPoint> points; 
   
-  points << QPointF(start->Pos, this->y(start->Value));
-  points << QPointF(end->Pos, this->y(end->Value));
-
   ctkBezierControlPoint* bezierCP = dynamic_cast<ctkBezierControlPoint*>(start);
   if (!bezierCP)
     {// just duplicate start and end into p1 and p2
-    points << points[0];
-    points << points[1];
+    points << start->P;
+    points << start->P;
+    points << end->P;
+    points << end->P;
     return points;
     }
   
-  points << QPointF(bezierCP->P1, this->y(bezierCP->ValueP1));
-  points << QPointF(bezierCP->P2, this->y(bezierCP->ValueP2));
+  points << start->P;
+  points << bezierCP->P1;
+  points << bezierCP->P2;
+  points << end->P;
   return points;
 }
 
 //-----------------------------------------------------------------------------
+QList<ctkPoint> ctkTransferFunctionItem::nonLinearPoints(
+  ctkControlPoint* start, ctkControlPoint* end) const
+{
+  Q_ASSERT(start);
+    
+  ctkNonLinearControlPoint* nonLinearCP = 
+    dynamic_cast<ctkNonLinearControlPoint*>(start);
+  if (!nonLinearCP)
+    {
+    QList<ctkPoint> points; 
+    points << start->P;
+    points << end->P;
+    return points;
+    }
+  return nonLinearCP->SubPoints;
+}
+
+//-----------------------------------------------------------------------------
 qreal ctkTransferFunctionItem::y(const QVariant& v) const
 { 
   Q_ASSERT(v.canConvert<qreal>() || v.canConvert<QColor>());
@@ -197,27 +216,54 @@ void ctkTransferFunctionGradientItem::paint(
   ctkControlPoint* startCP = this->transferFunction()->controlPoint(0);
   ctkControlPoint* endCP = 0;
   
-  qreal start = startCP->Pos * rangeDiff;
+  qreal start = startCP->x() * rangeDiff;
   qreal end = 0;
   for(int i = 1; i < count; ++i)
     {
     endCP = this->transferFunction()->controlPoint(i);
-    // TODO, use Bezier points for a finer gradient
-    end = endCP->Pos * rangeDiff;
-    QLinearGradient gradient(start,0, end, 0);
-    gradient.setColorAt(0, this->color(startCP->Value));
-    gradient.setColorAt(1, this->color(endCP->Value));
-    QRectF itemRect = QRectF(start, 0, end - start, 
-                             this->rect().height());
-    if (i==1)
+    // TODO, handle Bezier points for a finer gradient
+    // TODO, handle nonlinear points
+    if (dynamic_cast<ctkNonLinearControlPoint*>(startCP) != 0)
       {
-      itemRect.setLeft(0.);
+      QList<ctkPoint> points = this->nonLinearPoints(startCP, endCP);
+      for (int j = 1; j < points.count(); ++j)
+        {
+        end = points[j].X * rangeDiff;
+        QLinearGradient gradient(start, 0, end, 0);
+        gradient.setColorAt(0, this->color(points[j-1]));
+        gradient.setColorAt(1, this->color(points[j]));
+        QRectF itemRect = QRectF(start, 0, end - start, 
+                                 this->rect().height());
+        if (i==1 && j == 1)
+          {
+          itemRect.setLeft(0.);
+          }
+        if ((i == count -1) && (j == points.count() -1))
+          {
+          itemRect.setRight(this->rect().width());
+          }
+        painter->fillRect(itemRect, gradient);
+        start = end;
+        }
       }
-    if (i == count -1)
+    else
       {
-      itemRect.setRight(this->rect().width());
+      end = endCP->x() * rangeDiff;
+      QLinearGradient gradient(start, 0, end, 0);
+      gradient.setColorAt(0, this->color(startCP->value()));
+      gradient.setColorAt(1, this->color(endCP->value()));
+      QRectF itemRect = QRectF(start, 0, end - start, 
+                               this->rect().height());
+      if (i==1)
+        {
+        itemRect.setLeft(0.);
+        }
+      if (i == count -1)
+        {
+        itemRect.setRight(this->rect().width());
+        }
+      painter->fillRect(itemRect, gradient);
       }
-    painter->fillRect(itemRect, gradient);
     delete startCP;
     startCP = endCP;
     start = end;
@@ -313,7 +359,7 @@ void ctkTransferFunctionControlPointsItem::paint(
 
   QPainterPath path;
 
-  QPointF startPos(startCP->Pos, this->y(startCP->Value));
+  QPointF startPos(startCP->x(), this->y(startCP->value()));
   startPos.rx() *= rangeXDiff;
   startPos.setY(this->rect().height() 
                 - startPos.y() * rangeYDiff);
@@ -325,17 +371,35 @@ void ctkTransferFunctionControlPointsItem::paint(
   for(int i = 1; i < count; ++i)
     {
     endCP = this->transferFunction()->controlPoint(i);
-    QList<QPointF> points = this->bezierParams(startCP, endCP);
-    QList<QPointF>::iterator it = points.begin();
-    for ( ; it != points.end(); ++it)
+    if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
+      {
+      QList<ctkPoint> points = this->nonLinearPoints(startCP, endCP);
+      int j;
+      for (j = 1; j < points.count(); ++j)
+        {
+        path.lineTo(
+          QPointF(points[j].X * rangeXDiff, this->rect().height() - 
+                  this->y(points[j].Value) * rangeYDiff));
+        }
+      j = points.count() -1;
+      d->ControlPoints << QPointF(points[j].X * rangeXDiff, this->rect().height() - 
+                  this->y(points[j].Value) * rangeYDiff);
+      }
+    else //dynamic_cast<ctkBezierControlPoint*>(startCP))
       {
-      (*it).rx() *= rangeXDiff;
-      (*it).setY(this->rect().height() 
-                 - (*it).y() * rangeYDiff);
+      QList<ctkPoint> points = this->bezierParams(startCP, endCP);
+      QList<ctkPoint>::iterator it = points.begin();
+      QList<QPointF> bezierPoints;
+      foreach(const ctkPoint& p, points)
+        {
+        bezierPoints << 
+          QPointF(p.X * rangeXDiff, 
+                  this->rect().height() - this->y(p.Value) * rangeYDiff);
+        }
+      path.cubicTo(bezierPoints[1], bezierPoints[2], bezierPoints[3]);
+      d->ControlPoints << bezierPoints[3];
       }
-    d->ControlPoints << points[1];
-    path.cubicTo(points[2], points[3], points[1]);
-    //qDebug() << i << points[0] << points[2] << points[3] << points[1];
+    //qDebug() << i << points[0] << points[1] << points[2] << points[3];
     delete startCP;
     startCP = endCP;
     }
@@ -348,7 +412,7 @@ void ctkTransferFunctionControlPointsItem::paint(
   painter->drawPath(path);
 
   QPainterPath points;
-  foreach(const QPointF point, d->ControlPoints)
+  foreach(const QPointF& point, d->ControlPoints)
     {
     points.addEllipse(point, d->PointSize.width(), d->PointSize.height());
     }

+ 21 - 4
Libs/Widgets/ctkTransferFunctionItems.h

@@ -21,9 +21,9 @@
 /// CTK includes
 #include "CTKWidgetsExport.h"
 #include "ctkPimpl.h"
+#include "ctkTransferFunction.h"
 
 class ctkControlPoint;
-class ctkTransferFunction;
 class ctkTransferFunctionItemPrivate;
 //class ctkTransferFunctionGradientItemPrivate;
 class ctkTransferFunctionControlPointsItemPrivate;
@@ -47,10 +47,16 @@ public:
   QRectF rect()const;
   virtual QRectF boundingRect()const;
 protected:
-  qreal y(const QVariant& v)const;
-  QColor color(const QVariant& v)const;
+  qreal y(const QVariant& value)const;
+  inline qreal y(const ctkPoint& point)const;
 
-  QList<QPointF> bezierParams(ctkControlPoint* start, ctkControlPoint* end)const;
+  QColor color(const QVariant& value)const;
+  inline QColor color(const ctkPoint& point)const;
+
+
+
+  QList<ctkPoint> bezierParams(ctkControlPoint* start, ctkControlPoint* end)const;
+  QList<ctkPoint> nonLinearPoints(ctkControlPoint* start, ctkControlPoint* end)const;
 protected slots:
   virtual void onTransferFunctionChanged();
 private:
@@ -62,6 +68,17 @@ void ctkTransferFunctionItem::setRect(qreal x, qreal y, qreal width, qreal heigh
   this->setRect(QRectF(x,y,width,height));
 }
 
+qreal ctkTransferFunctionItem::y(const ctkPoint& p)const
+{
+  return this->y(p.Value);
+}
+
+QColor ctkTransferFunctionItem::color(const ctkPoint& p)const
+{
+  return this->color(p.Value);
+}
+
+
 //-----------------------------------------------------------------------------
 class CTK_WIDGETS_EXPORT ctkTransferFunctionGradientItem: public ctkTransferFunctionItem
 {