ctkTransferFunctionScene.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00021 #include <QGraphicsScene>
00022 #include <QLinearGradient>
00023 #include <QResizeEvent>
00024 #include <QDebug>
00025 
00027 #include "ctkTransferFunction.h"
00028 #include "ctkTransferFunctionScene.h"
00029 
00031 #include <limits>
00032 
00033 //-----------------------------------------------------------------------------
00034 class ctkTransferFunctionScenePrivate: public ctkPrivate<ctkTransferFunctionScene>
00035 {
00036   CTK_DECLARE_PUBLIC(ctkTransferFunctionScene);
00037 public:
00038   ctkTransferFunctionScenePrivate();
00039   QRectF               OldRect;
00040   ctkTransferFunction* TransferFunction;
00041   QPainterPath   Path;
00042   QLinearGradient Gradient;
00043   QList<QPointF> Points;
00044   qreal        WorldRangeX[2];
00045   QVariant     WorldRangeY[2];
00046   qreal        RangeXDiff;
00047   qreal        RangeXOffSet;
00048   qreal        RangeYDiff;
00049   qreal        RangeYOffSet;
00050   QColor       VerticalGradientColor;
00051 };
00052 
00053 ctkTransferFunctionScenePrivate::ctkTransferFunctionScenePrivate()
00054 {
00055   this->TransferFunction = 0;
00056   this->VerticalGradientColor = QColor::fromRgbF(1., 0., 0., 1. );
00057 }
00058 
00059 //-----------------------------------------------------------------------------
00060 ctkTransferFunctionScene::ctkTransferFunctionScene(QObject* parentObject)
00061   :QGraphicsScene(parentObject)
00062 {
00063   CTK_INIT_PRIVATE(ctkTransferFunctionScene);
00064 }
00065 
00066 //-----------------------------------------------------------------------------
00067 ctkTransferFunctionScene::ctkTransferFunctionScene(
00068   ctkTransferFunction* transferFunction, QObject* parentObject)
00069   :QGraphicsScene(parentObject)
00070 {
00071   CTK_INIT_PRIVATE(ctkTransferFunctionScene);
00072   this->setTransferFunction(transferFunction);
00073 }
00074 //-----------------------------------------------------------------------------
00075 ctkTransferFunctionScene::~ctkTransferFunctionScene()
00076 {
00077 }
00078 
00079 //-----------------------------------------------------------------------------
00080 void ctkTransferFunctionScene::setTransferFunction(ctkTransferFunction* transferFunction)
00081 {
00082   CTK_D(ctkTransferFunctionScene);
00083   if (d->TransferFunction == transferFunction)
00084     {
00085     return;
00086     }
00087   d->TransferFunction = transferFunction;
00088   connect( d->TransferFunction, SIGNAL(changed()),
00089            this, SLOT(onTransferFunctionChanged()),
00090            Qt::UniqueConnection);
00091   this->update();
00092 }
00093 
00094 //-----------------------------------------------------------------------------
00095 ctkTransferFunction* ctkTransferFunctionScene::transferFunction()const
00096 {
00097   return ctk_d()->TransferFunction;
00098 }
00099 
00100 //-----------------------------------------------------------------------------
00101 void ctkTransferFunctionScene::onTransferFunctionChanged()
00102 {
00103   CTK_D(ctkTransferFunctionScene);
00104   // TODO delete cache here
00105   d->Path = QPainterPath();
00106   this->update();  
00107 }
00108 
00109 //-----------------------------------------------------------------------------
00110 const QPainterPath& ctkTransferFunctionScene::curve()const
00111 {
00112   CTK_D(const ctkTransferFunctionScene);
00113   if (d->Path.isEmpty())// || this->sceneRect() != d->OldRect)
00114     {
00115     const_cast<ctkTransferFunctionScene*>(this)->computeCurve();
00116     const_cast<ctkTransferFunctionScene*>(this)->computeGradient();
00117     }
00118   return d->Path;
00119 }
00120 
00121 //-----------------------------------------------------------------------------
00122 const QList<QPointF>& ctkTransferFunctionScene::points()const
00123 {
00124   CTK_D(const ctkTransferFunctionScene);
00125   if (d->Path.isEmpty())// || this->sceneRect() != d->OldRect)
00126     {
00127     const_cast<ctkTransferFunctionScene*>(this)->computeCurve();
00128     const_cast<ctkTransferFunctionScene*>(this)->computeGradient();
00129     }
00130   return d->Points;
00131 }
00132 
00133 //-----------------------------------------------------------------------------
00134 const QGradient& ctkTransferFunctionScene::gradient()const
00135 {
00136   CTK_D(const ctkTransferFunctionScene);
00137   if (d->Path.isEmpty())// || this->sceneRect() != d->OldRect)
00138     {
00139     const_cast<ctkTransferFunctionScene*>(this)->computeCurve();
00140     const_cast<ctkTransferFunctionScene*>(this)->computeGradient();
00141     }
00142   return d->Gradient;
00143 }
00144 
00145 //-----------------------------------------------------------------------------
00146 void ctkTransferFunctionScene::computeCurve()
00147 {
00148   CTK_D(ctkTransferFunctionScene);
00149 
00150   int count = d->TransferFunction ? d->TransferFunction->count() : 0;
00151   if (count <= 0)
00152     {
00153     return;
00154     }
00155   qDebug() << "computeCurve" << this->sceneRect();
00156   d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
00157   d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
00158   d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
00159 
00160   d->RangeXDiff   = this->computeRangeXDiff(this->sceneRect(), d->WorldRangeX);
00161   d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
00162 
00163   d->RangeYDiff   = this->computeRangeYDiff(this->sceneRect(), d->WorldRangeY);
00164   d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
00165 
00166   ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
00167   ctkControlPoint* nextCP = 0;
00168 
00169   QPointF startPos = this->mapPointToScene(startCP);
00170   
00171   d->Points.clear();
00172   d->Points << startPos;
00173 
00174   d->Path = QPainterPath();
00175   d->Path.moveTo(startPos);
00176   for(int i = 1; i < count; ++i)
00177     {
00178     nextCP = d->TransferFunction->controlPoint(i);
00179     if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
00180       {
00181       QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
00182       int j;
00183       for (j = 1; j < points.count(); ++j)
00184         {
00185         d->Path.lineTo(this->mapPointToScene(points[j]));
00186         }
00187       j = points.count() - 1;
00188       d->Points << this->mapPointToScene(points[j]);
00189       }
00190     else //dynamic_cast<ctkBezierControlPoint*>(startCP))
00191       {
00192       QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
00193       QList<ctkPoint>::iterator it = points.begin();
00194       QList<QPointF> bezierPoints;
00195       foreach(const ctkPoint& p, points)
00196         {
00197         bezierPoints << this->mapPointToScene(p);
00198         }
00199       d->Path.cubicTo(bezierPoints[1], bezierPoints[2], bezierPoints[3]);
00200       d->Points << bezierPoints[3];
00201       }
00202     //qDebug() << i << points[0] << points[1] << points[2] << points[3];
00203     delete startCP;
00204     startCP = nextCP;
00205     }
00206   if (startCP)
00207     {
00208     delete startCP;
00209     }
00210 }
00211 
00212 //-----------------------------------------------------------------------------
00213 void ctkTransferFunctionScene::computeGradient()
00214 {
00215   CTK_D(ctkTransferFunctionScene);
00216 
00217   int count = d->TransferFunction ? d->TransferFunction->count() : 0;
00218   if (count <= 0)
00219     {
00220     return;
00221     }
00222   qDebug() << "computeCurve" << this->sceneRect();
00223   d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
00224   d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
00225   d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
00226 
00227   d->RangeXDiff   = this->computeRangeXDiff(QRectF(0.,0.,1.,1.), d->WorldRangeX);
00228   d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
00229 
00230   d->RangeYDiff   = this->computeRangeYDiff(QRectF(0.,0.,1.,1.), d->WorldRangeY);
00231   d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
00232 
00233   ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
00234   ctkControlPoint* nextCP = 0;
00235 
00236   qreal startPos = this->mapXToScene(this->posX(startCP->x()));
00237   qreal nextPos;
00238   
00239   //
00240   //if we have no colors in value (i.e. can't convert value to color)
00241   if (! d->TransferFunction->value(0).canConvert<QColor>())
00242     {
00243     // create vertical gradient
00244     d->Gradient = QLinearGradient(0., 0., 0., 1.);
00245     // red
00246     d->Gradient.setColorAt(0, d->VerticalGradientColor );
00247     // to black
00248     d->Gradient.setColorAt(1, QColor::fromRgbF(0., 0., 0., 1. ));
00249     return;
00250     }
00251 
00252   // classic gradient if we have colors in value
00253   d->Gradient = QLinearGradient(0., 0., 1., 0.);
00254   d->Gradient.setColorAt(startPos, this->color(startCP));
00255   for(int i = 1; i < count; ++i)
00256     {
00257     nextCP = d->TransferFunction->controlPoint(i);
00258     nextPos = this->mapXToScene(this->posX(nextCP));
00259     if (this->transferFunction()->isDiscrete())
00260       {
00261       qreal midPoint = (startPos + nextPos)  / 2;
00262       d->Gradient.setColorAt(midPoint, this->color(startCP));
00263       d->Gradient.setColorAt(midPoint + std::numeric_limits<qreal>::epsilon(), this->color(nextCP));
00264       }
00265     else if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
00266       {
00267       QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
00268       foreach(const ctkPoint& p, points)
00269         {
00270         d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
00271         }
00272       //no need, d->Gradient.setColorAt(nextPos, this->color(nextCP));
00273       }
00274     else //dynamic_cast<ctkBezierControlPoint*>(startCP))
00275       { // TODO handle bezier points with color
00276       QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
00277       QList<ctkPoint>::iterator it = points.begin();
00278       QList<QPointF> bezierPoints;
00279       foreach(const ctkPoint& p, points)
00280         {
00281         d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
00282         }
00283       nextPos = this->mapXToScene(this->posX(points[points.size() - 1]));
00284       }
00285     //qDebug() << i << points[0] << points[1] << points[2] << points[3];
00286     delete startCP;
00287     startCP = nextCP;
00288     startPos = nextPos;
00289     }
00290   d->Gradient.setColorAt(startPos, this->color(startCP));
00291   if (startCP)
00292     {
00293     delete startCP;
00294     }
00295 }
00296 
00297 //-----------------------------------------------------------------------------
00298 QList<ctkPoint> ctkTransferFunctionScene::bezierParams(
00299   ctkControlPoint* start, ctkControlPoint* end) const
00300 {
00301   Q_ASSERT(start);
00302   Q_ASSERT(end);
00303   QList<ctkPoint> points; 
00304   
00305   ctkBezierControlPoint* bezierCP = dynamic_cast<ctkBezierControlPoint*>(start);
00306   if (!bezierCP)
00307     {// just duplicate start and end into p1 and p2
00308     points << start->P;
00309     points << start->P;
00310     points << end->P;
00311     points << end->P;
00312     return points;
00313     }
00314   
00315   points << start->P;
00316   points << bezierCP->P1;
00317   points << bezierCP->P2;
00318   points << end->P;
00319   return points;
00320 }
00321 
00322 //-----------------------------------------------------------------------------
00323 QList<ctkPoint> ctkTransferFunctionScene::nonLinearPoints(
00324   ctkControlPoint* start, ctkControlPoint* end) const
00325 {
00326   Q_ASSERT(start);
00327     
00328   ctkNonLinearControlPoint* nonLinearCP = 
00329     dynamic_cast<ctkNonLinearControlPoint*>(start);
00330   if (!nonLinearCP)
00331     {
00332     QList<ctkPoint> points; 
00333     points << start->P;
00334     points << end->P;
00335     return points;
00336     }
00337   return nonLinearCP->SubPoints;
00338 }
00339 
00340 //-----------------------------------------------------------------------------
00341 QColor ctkTransferFunctionScene::color(const QVariant& v) const
00342 { 
00343   //Q_ASSERT(v.canConvert<QColor>());
00344   if (v.canConvert<QColor>())
00345     {
00346     return v.value<QColor>();
00347     }
00348   else
00349     {
00350     //black background
00351     QColor defaultColor(0., 0., 0.);
00352     return defaultColor;
00353     }
00354   return QColor();
00355 }
00356 
00357 //-----------------------------------------------------------------------------
00358 qreal ctkTransferFunctionScene::computeRangeXDiff(const QRectF& rect, qreal rangeX[2])
00359 {
00360   return rect.width() / (rangeX[1] - rangeX[0]);
00361 }
00362 
00363 //-----------------------------------------------------------------------------
00364 qreal ctkTransferFunctionScene::computeRangeXOffset(qreal rangeX[2])
00365 {
00366   return rangeX[0];
00367 }
00368 
00369 //-----------------------------------------------------------------------------
00370 qreal ctkTransferFunctionScene::computeRangeYDiff(const QRectF& rect, const QVariant rangeY[2])
00371 {
00372   qreal rangeYDiff = rect.height();
00373   qreal rangePosY[2];
00374   rangePosY[0] = this->posY(rangeY[0]);
00375   rangePosY[1] = this->posY(rangeY[1]);
00376   if (rangePosY[1] == rangePosY[0])
00377     {
00378     rangeYDiff /= rangePosY[0];
00379     return rangeYDiff;
00380     }
00381   rangeYDiff /= rangePosY[1] - rangePosY[0];
00382   return rangeYDiff;
00383 }
00384 
00385 //-----------------------------------------------------------------------------
00386 qreal ctkTransferFunctionScene::computeRangeYOffset(const QVariant rangeY[2])
00387 {
00388   qreal rangePosY[2];
00389   rangePosY[0] = this->posY(rangeY[0]);
00390   rangePosY[1] = this->posY(rangeY[1]);
00391 
00392   if (rangePosY[1] == rangePosY[0])
00393     {
00394     return 0.;
00395     }
00396   return rangePosY[0];
00397 }
00398 
00399 //-----------------------------------------------------------------------------
00400 qreal ctkTransferFunctionScene::posX(const qreal& x)const
00401 {
00402   return x;
00403 }
00404 
00405 //-----------------------------------------------------------------------------
00406 qreal ctkTransferFunctionScene::posY(const QVariant& value)const
00407 {
00408   Q_ASSERT(value.canConvert<qreal>() || value.canConvert<QColor>());
00409   if (value.canConvert<QColor>())
00410     {
00411     return value.value<QColor>().alphaF();
00412     }
00413   return value.toReal();
00414 }
00415 
00416 //-----------------------------------------------------------------------------
00417 QPointF ctkTransferFunctionScene::mapPointToScene(const ctkControlPoint* cp)const
00418 {
00419   return QPointF(this->mapXToScene(this->posX(cp->x())),
00420                  this->mapYToScene(this->posY(cp->value())));
00421 }
00422 
00423 //-----------------------------------------------------------------------------
00424 QPointF ctkTransferFunctionScene::mapPointToScene(const ctkPoint& point)const
00425 {
00426   return QPointF( this->mapXToScene(this->posX(point.X)),
00427                   this->mapYToScene(this->posY(point.Value)));
00428 }
00429 
00430 //-----------------------------------------------------------------------------
00431 qreal ctkTransferFunctionScene::mapXToScene(qreal xPos)const
00432 {
00433   CTK_D(const ctkTransferFunctionScene);
00434   return (xPos - d->RangeXOffSet) * d->RangeXDiff;
00435 }
00436 
00437 //-----------------------------------------------------------------------------
00438 qreal ctkTransferFunctionScene::mapYToScene(qreal yPos)const
00439 {
00440   CTK_D(const ctkTransferFunctionScene);
00441   return this->height() - (yPos - d->RangeYOffSet) * d->RangeYDiff;
00442 }
00443 
00444 //-----------------------------------------------------------------------------
00445 qreal ctkTransferFunctionScene::mapXFromScene(qreal scenePosX)const
00446 {
00447   CTK_D(const ctkTransferFunctionScene);
00448   return (scenePosX / d->RangeXDiff) + d->RangeXOffSet;
00449 }
00450 
00451 //-----------------------------------------------------------------------------
00452 qreal ctkTransferFunctionScene::mapYFromScene(qreal scenePosY)const
00453 {
00454   CTK_D(const ctkTransferFunctionScene);
00455   return ((this->height() - scenePosY) / d->RangeYDiff) + d->RangeYOffSet ;
00456 }
00457 //-----------------------------------------------------------------------------
00458 QColor ctkTransferFunctionScene::verticalGradientColor() const
00459 {
00460   CTK_D( const ctkTransferFunctionScene );
00461   return d->VerticalGradientColor;
00462 }
00463 //-----------------------------------------------------------------------------
00464 void ctkTransferFunctionScene::setVerticalGradientColor( QColor verticalGradientColor )
00465 {
00466   CTK_D( ctkTransferFunctionScene );
00467   d->VerticalGradientColor = verticalGradientColor;
00468 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1