ctkTransferFunctionRepresentation.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) 2010 Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. /// Qt includes
  15. #include <QGraphicsScene>
  16. #include <QLinearGradient>
  17. #include <QResizeEvent>
  18. #include <QDebug>
  19. /// CTK includes
  20. #include "ctkTransferFunction.h"
  21. #include "ctkTransferFunctionRepresentation.h"
  22. /// STL includes
  23. #include <limits>
  24. //-----------------------------------------------------------------------------
  25. class ctkTransferFunctionRepresentationPrivate: public ctkPrivate<ctkTransferFunctionRepresentation>
  26. {
  27. CTK_DECLARE_PUBLIC(ctkTransferFunctionRepresentation);
  28. public:
  29. ctkTransferFunctionRepresentationPrivate();
  30. ctkTransferFunction* TransferFunction;
  31. QColor VerticalGradientColor;
  32. QPainterPath Path;
  33. QLinearGradient Gradient;
  34. QList<QPointF> Points;
  35. QRectF rect()const;
  36. qreal width()const;
  37. qreal height()const;
  38. qreal WorldRangeX[2];
  39. QVariant WorldRangeY[2];
  40. qreal RangeXDiff;
  41. qreal RangeXOffSet;
  42. qreal RangeYDiff;
  43. qreal RangeYOffSet;
  44. };
  45. //-----------------------------------------------------------------------------
  46. ctkTransferFunctionRepresentationPrivate::ctkTransferFunctionRepresentationPrivate()
  47. {
  48. this->TransferFunction = 0;
  49. this->VerticalGradientColor = QColor::fromRgbF(1., 0., 0., 1. );
  50. }
  51. //-----------------------------------------------------------------------------
  52. QRectF ctkTransferFunctionRepresentationPrivate::rect()const
  53. {
  54. return QRectF(0.,0.,1.,1.);
  55. }
  56. //-----------------------------------------------------------------------------
  57. qreal ctkTransferFunctionRepresentationPrivate::width()const
  58. {
  59. return 1.;
  60. }
  61. //-----------------------------------------------------------------------------
  62. qreal ctkTransferFunctionRepresentationPrivate::height()const
  63. {
  64. return 1.;
  65. }
  66. //-----------------------------------------------------------------------------
  67. ctkTransferFunctionRepresentation::ctkTransferFunctionRepresentation(QObject* parentObject)
  68. :QObject(parentObject)
  69. {
  70. CTK_INIT_PRIVATE(ctkTransferFunctionRepresentation);
  71. }
  72. //-----------------------------------------------------------------------------
  73. ctkTransferFunctionRepresentation::ctkTransferFunctionRepresentation(
  74. ctkTransferFunction* transferFunction, QObject* parentObject)
  75. :QObject(parentObject)
  76. {
  77. CTK_INIT_PRIVATE(ctkTransferFunctionRepresentation);
  78. this->setTransferFunction(transferFunction);
  79. }
  80. //-----------------------------------------------------------------------------
  81. ctkTransferFunctionRepresentation::~ctkTransferFunctionRepresentation()
  82. {
  83. }
  84. //-----------------------------------------------------------------------------
  85. QColor ctkTransferFunctionRepresentation::verticalGradientColor() const
  86. {
  87. CTK_D( const ctkTransferFunctionRepresentation );
  88. return d->VerticalGradientColor;
  89. }
  90. //-----------------------------------------------------------------------------
  91. void ctkTransferFunctionRepresentation::setVerticalGradientColor( QColor verticalGradientColor )
  92. {
  93. CTK_D( ctkTransferFunctionRepresentation );
  94. d->VerticalGradientColor = verticalGradientColor;
  95. }
  96. //-----------------------------------------------------------------------------
  97. void ctkTransferFunctionRepresentation::setTransferFunction(ctkTransferFunction* transferFunction)
  98. {
  99. CTK_D(ctkTransferFunctionRepresentation);
  100. if (d->TransferFunction == transferFunction)
  101. {
  102. return;
  103. }
  104. d->TransferFunction = transferFunction;
  105. connect( d->TransferFunction, SIGNAL(changed()),
  106. this, SLOT(onTransferFunctionChanged()),
  107. Qt::UniqueConnection);
  108. this->onTransferFunctionChanged();
  109. }
  110. //-----------------------------------------------------------------------------
  111. ctkTransferFunction* ctkTransferFunctionRepresentation::transferFunction()const
  112. {
  113. return ctk_d()->TransferFunction;
  114. }
  115. //-----------------------------------------------------------------------------
  116. void ctkTransferFunctionRepresentation::onTransferFunctionChanged()
  117. {
  118. CTK_D(ctkTransferFunctionRepresentation);
  119. // delete cache here
  120. d->Path = QPainterPath();
  121. d->Points.clear();
  122. }
  123. //-----------------------------------------------------------------------------
  124. const QPainterPath& ctkTransferFunctionRepresentation::curve()const
  125. {
  126. CTK_D(const ctkTransferFunctionRepresentation);
  127. if (d->Path.isEmpty())
  128. {
  129. const_cast<ctkTransferFunctionRepresentation*>(this)->computeCurve();
  130. const_cast<ctkTransferFunctionRepresentation*>(this)->computeGradient();
  131. }
  132. return d->Path;
  133. }
  134. //-----------------------------------------------------------------------------
  135. const QList<QPointF>& ctkTransferFunctionRepresentation::points()const
  136. {
  137. CTK_D(const ctkTransferFunctionRepresentation);
  138. if (d->Path.isEmpty())
  139. {
  140. const_cast<ctkTransferFunctionRepresentation*>(this)->computeCurve();
  141. const_cast<ctkTransferFunctionRepresentation*>(this)->computeGradient();
  142. }
  143. return d->Points;
  144. }
  145. //-----------------------------------------------------------------------------
  146. const QGradient& ctkTransferFunctionRepresentation::gradient()const
  147. {
  148. CTK_D(const ctkTransferFunctionRepresentation);
  149. if (d->Path.isEmpty())
  150. {
  151. const_cast<ctkTransferFunctionRepresentation*>(this)->computeCurve();
  152. const_cast<ctkTransferFunctionRepresentation*>(this)->computeGradient();
  153. }
  154. return d->Gradient;
  155. }
  156. //-----------------------------------------------------------------------------
  157. void ctkTransferFunctionRepresentation::computeCurve()
  158. {
  159. CTK_D(ctkTransferFunctionRepresentation);
  160. int count = d->TransferFunction ? d->TransferFunction->count() : 0;
  161. if (count <= 0)
  162. {
  163. return;
  164. }
  165. d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
  166. d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
  167. d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
  168. d->RangeXDiff = this->computeRangeXDiff(d->rect(), d->WorldRangeX);
  169. d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
  170. d->RangeYDiff = this->computeRangeYDiff(d->rect(), d->WorldRangeY);
  171. d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
  172. ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
  173. ctkControlPoint* nextCP = 0;
  174. QPointF startPos = this->mapPointToScene(startCP);
  175. d->Points.clear();
  176. d->Points << startPos;
  177. d->Path = QPainterPath();
  178. d->Path.moveTo(startPos);
  179. for(int i = 1; i < count; ++i)
  180. {
  181. nextCP = d->TransferFunction->controlPoint(i);
  182. if (this->transferFunction()->isDiscrete())
  183. {
  184. QPointF nextPos = this->mapPointToScene(nextCP);
  185. qreal midPosX = (startPos.x() + nextPos.x()) / 2.;
  186. d->Path.lineTo(QPointF(midPosX, startPos.y()));
  187. d->Path.lineTo(QPointF(midPosX, nextPos.y()));
  188. d->Points << nextPos;
  189. startPos = nextPos;
  190. if (i == count -1)
  191. {
  192. d->Path.lineTo(nextPos);
  193. }
  194. }
  195. else if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
  196. {
  197. QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
  198. int j;
  199. for (j = 1; j < points.count(); ++j)
  200. {
  201. d->Path.lineTo(this->mapPointToScene(points[j]));
  202. }
  203. j = points.count() - 1;
  204. d->Points << this->mapPointToScene(points[j]);
  205. }
  206. else //dynamic_cast<ctkBezierControlPoint*>(startCP))
  207. {
  208. QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
  209. QList<ctkPoint>::iterator it = points.begin();
  210. QList<QPointF> bezierPoints;
  211. foreach(const ctkPoint& p, points)
  212. {
  213. bezierPoints << this->mapPointToScene(p);
  214. }
  215. d->Path.cubicTo(bezierPoints[1], bezierPoints[2], bezierPoints[3]);
  216. d->Points << bezierPoints[3];
  217. }
  218. //qDebug() << i << points[0] << points[1] << points[2] << points[3];
  219. delete startCP;
  220. startCP = nextCP;
  221. }
  222. if (startCP)
  223. {
  224. delete startCP;
  225. }
  226. }
  227. //-----------------------------------------------------------------------------
  228. void ctkTransferFunctionRepresentation::computeGradient()
  229. {
  230. CTK_D(ctkTransferFunctionRepresentation);
  231. int count = d->TransferFunction ? d->TransferFunction->count() : 0;
  232. if (count <= 0)
  233. {
  234. return;
  235. }
  236. qDebug() << "computeCurve" << d->rect();
  237. d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
  238. d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
  239. d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
  240. d->RangeXDiff = this->computeRangeXDiff(QRectF(0.,0.,1.,1.), d->WorldRangeX);
  241. d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
  242. d->RangeYDiff = this->computeRangeYDiff(QRectF(0.,0.,1.,1.), d->WorldRangeY);
  243. d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
  244. ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
  245. ctkControlPoint* nextCP = 0;
  246. qreal startPos = this->mapXToScene(this->posX(startCP->x()));
  247. qreal nextPos;
  248. //
  249. //if we have no colors in value (i.e. can't convert value to color)
  250. if (! d->TransferFunction->value(0).canConvert<QColor>())
  251. {
  252. // create vertical gradient
  253. d->Gradient = QLinearGradient(0., 0., 0., 1.);
  254. // red
  255. d->Gradient.setColorAt(0, d->VerticalGradientColor );
  256. // to black
  257. d->Gradient.setColorAt(1, QColor::fromRgbF(0., 0., 0., 1. ));
  258. return;
  259. }
  260. // classic gradient if we have colors in value
  261. d->Gradient = QLinearGradient(0., 0., 1., 0.);
  262. d->Gradient.setColorAt(startPos, this->color(startCP));
  263. for(int i = 1; i < count; ++i)
  264. {
  265. nextCP = d->TransferFunction->controlPoint(i);
  266. nextPos = this->mapXToScene(this->posX(nextCP));
  267. if (this->transferFunction()->isDiscrete())
  268. {
  269. qreal midPoint = (startPos + nextPos) / 2;
  270. d->Gradient.setColorAt(midPoint, this->color(startCP));
  271. d->Gradient.setColorAt(midPoint + std::numeric_limits<qreal>::epsilon(), this->color(nextCP));
  272. }
  273. else if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
  274. {
  275. QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
  276. foreach(const ctkPoint& p, points)
  277. {
  278. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  279. }
  280. //no need, d->Gradient.setColorAt(nextPos, this->color(nextCP));
  281. }
  282. else //dynamic_cast<ctkBezierControlPoint*>(startCP))
  283. { // TODO handle bezier points with color
  284. QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
  285. QList<ctkPoint>::iterator it = points.begin();
  286. QList<QPointF> bezierPoints;
  287. foreach(const ctkPoint& p, points)
  288. {
  289. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  290. }
  291. nextPos = this->mapXToScene(this->posX(points[points.size() - 1]));
  292. }
  293. //qDebug() << i << points[0] << points[1] << points[2] << points[3];
  294. delete startCP;
  295. startCP = nextCP;
  296. startPos = nextPos;
  297. }
  298. d->Gradient.setColorAt(startPos, this->color(startCP));
  299. if (startCP)
  300. {
  301. delete startCP;
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. QList<ctkPoint> ctkTransferFunctionRepresentation::bezierParams(
  306. ctkControlPoint* start, ctkControlPoint* end) const
  307. {
  308. Q_ASSERT(start);
  309. Q_ASSERT(end);
  310. QList<ctkPoint> points;
  311. ctkBezierControlPoint* bezierCP = dynamic_cast<ctkBezierControlPoint*>(start);
  312. if (!bezierCP)
  313. {// just duplicate start and end into p1 and p2
  314. points << start->P;
  315. points << start->P;
  316. points << end->P;
  317. points << end->P;
  318. return points;
  319. }
  320. points << start->P;
  321. points << bezierCP->P1;
  322. points << bezierCP->P2;
  323. points << end->P;
  324. return points;
  325. }
  326. //-----------------------------------------------------------------------------
  327. QList<ctkPoint> ctkTransferFunctionRepresentation::nonLinearPoints(
  328. ctkControlPoint* start, ctkControlPoint* end) const
  329. {
  330. Q_ASSERT(start);
  331. ctkNonLinearControlPoint* nonLinearCP =
  332. dynamic_cast<ctkNonLinearControlPoint*>(start);
  333. if (!nonLinearCP)
  334. {
  335. QList<ctkPoint> points;
  336. points << start->P;
  337. points << end->P;
  338. return points;
  339. }
  340. return nonLinearCP->SubPoints;
  341. }
  342. //-----------------------------------------------------------------------------
  343. QColor ctkTransferFunctionRepresentation::color(const QVariant& v) const
  344. {
  345. //Q_ASSERT(v.canConvert<QColor>());
  346. if (v.canConvert<QColor>())
  347. {
  348. return v.value<QColor>();
  349. }
  350. else
  351. {
  352. //black background
  353. QColor defaultColor(0., 0., 0.);
  354. return defaultColor;
  355. }
  356. return QColor();
  357. }
  358. //-----------------------------------------------------------------------------
  359. qreal ctkTransferFunctionRepresentation::computeRangeXDiff(const QRectF& rect, qreal rangeX[2])
  360. {
  361. return rect.width() / (rangeX[1] - rangeX[0]);
  362. }
  363. //-----------------------------------------------------------------------------
  364. qreal ctkTransferFunctionRepresentation::computeRangeXOffset(qreal rangeX[2])
  365. {
  366. return rangeX[0];
  367. }
  368. //-----------------------------------------------------------------------------
  369. qreal ctkTransferFunctionRepresentation::computeRangeYDiff(const QRectF& rect, const QVariant rangeY[2])
  370. {
  371. qreal rangeYDiff = rect.height();
  372. qreal rangePosY[2];
  373. rangePosY[0] = this->posY(rangeY[0]);
  374. rangePosY[1] = this->posY(rangeY[1]);
  375. if (rangePosY[1] == rangePosY[0])
  376. {
  377. rangeYDiff /= rangePosY[0];
  378. return rangeYDiff;
  379. }
  380. rangeYDiff /= rangePosY[1] - rangePosY[0];
  381. return rangeYDiff;
  382. }
  383. //-----------------------------------------------------------------------------
  384. qreal ctkTransferFunctionRepresentation::computeRangeYOffset(const QVariant rangeY[2])
  385. {
  386. qreal rangePosY[2];
  387. rangePosY[0] = this->posY(rangeY[0]);
  388. rangePosY[1] = this->posY(rangeY[1]);
  389. if (rangePosY[1] == rangePosY[0])
  390. {
  391. return 0.;
  392. }
  393. return rangePosY[0];
  394. }
  395. //-----------------------------------------------------------------------------
  396. qreal ctkTransferFunctionRepresentation::posX(const qreal& x)const
  397. {
  398. return x;
  399. }
  400. //-----------------------------------------------------------------------------
  401. qreal ctkTransferFunctionRepresentation::posY(const QVariant& value)const
  402. {
  403. Q_ASSERT(value.canConvert<qreal>() || value.canConvert<QColor>());
  404. if (value.canConvert<QColor>())
  405. {
  406. return value.value<QColor>().alphaF();
  407. }
  408. return value.toReal();
  409. }
  410. //-----------------------------------------------------------------------------
  411. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkControlPoint* cp)const
  412. {
  413. return QPointF(this->mapXToScene(this->posX(cp->x())),
  414. this->mapYToScene(this->posY(cp->value())));
  415. }
  416. //-----------------------------------------------------------------------------
  417. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkPoint& point)const
  418. {
  419. return QPointF( this->mapXToScene(this->posX(point.X)),
  420. this->mapYToScene(this->posY(point.Value)));
  421. }
  422. //-----------------------------------------------------------------------------
  423. qreal ctkTransferFunctionRepresentation::mapXToScene(qreal xPos)const
  424. {
  425. CTK_D(const ctkTransferFunctionRepresentation);
  426. return (xPos - d->RangeXOffSet) * d->RangeXDiff;
  427. }
  428. //-----------------------------------------------------------------------------
  429. qreal ctkTransferFunctionRepresentation::mapYToScene(qreal yPos)const
  430. {
  431. CTK_D(const ctkTransferFunctionRepresentation);
  432. return d->height() - (yPos - d->RangeYOffSet) * d->RangeYDiff;
  433. }
  434. //-----------------------------------------------------------------------------
  435. qreal ctkTransferFunctionRepresentation::mapXFromScene(qreal scenePosX)const
  436. {
  437. CTK_D(const ctkTransferFunctionRepresentation);
  438. return (scenePosX / d->RangeXDiff) + d->RangeXOffSet;
  439. }
  440. //-----------------------------------------------------------------------------
  441. qreal ctkTransferFunctionRepresentation::mapYFromScene(qreal scenePosY)const
  442. {
  443. CTK_D(const ctkTransferFunctionRepresentation);
  444. return ((d->height() - scenePosY) / d->RangeYDiff) + d->RangeYOffSet ;
  445. }