ctkTransferFunctionRepresentation.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) 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
  26. {
  27. public:
  28. ctkTransferFunctionRepresentationPrivate();
  29. ctkTransferFunction* TransferFunction;
  30. QColor VerticalGradientColor;
  31. QPainterPath Path;
  32. QLinearGradient Gradient;
  33. QList<QPointF> Points;
  34. QRectF rect()const;
  35. qreal width()const;
  36. qreal height()const;
  37. qreal WorldRangeX[2];
  38. QVariant WorldRangeY[2];
  39. qreal RangeXDiff;
  40. qreal RangeXOffSet;
  41. qreal RangeYDiff;
  42. qreal RangeYOffSet;
  43. };
  44. //-----------------------------------------------------------------------------
  45. ctkTransferFunctionRepresentationPrivate::ctkTransferFunctionRepresentationPrivate()
  46. {
  47. this->TransferFunction = 0;
  48. this->VerticalGradientColor = QColor::fromRgbF(1., 0., 0., 1. );
  49. }
  50. //-----------------------------------------------------------------------------
  51. QRectF ctkTransferFunctionRepresentationPrivate::rect()const
  52. {
  53. return QRectF(0.,0.,1.,1.);
  54. }
  55. //-----------------------------------------------------------------------------
  56. qreal ctkTransferFunctionRepresentationPrivate::width()const
  57. {
  58. return 1.;
  59. }
  60. //-----------------------------------------------------------------------------
  61. qreal ctkTransferFunctionRepresentationPrivate::height()const
  62. {
  63. return 1.;
  64. }
  65. //-----------------------------------------------------------------------------
  66. ctkTransferFunctionRepresentation::ctkTransferFunctionRepresentation(QObject* parentObject)
  67. :QObject(parentObject)
  68. , d_ptr(new ctkTransferFunctionRepresentationPrivate)
  69. {
  70. }
  71. //-----------------------------------------------------------------------------
  72. ctkTransferFunctionRepresentation::ctkTransferFunctionRepresentation(
  73. ctkTransferFunction* transferFunction, QObject* parentObject)
  74. :QObject(parentObject)
  75. , d_ptr(new ctkTransferFunctionRepresentationPrivate)
  76. {
  77. this->setTransferFunction(transferFunction);
  78. }
  79. //-----------------------------------------------------------------------------
  80. ctkTransferFunctionRepresentation::~ctkTransferFunctionRepresentation()
  81. {
  82. }
  83. //-----------------------------------------------------------------------------
  84. QColor ctkTransferFunctionRepresentation::verticalGradientColor() const
  85. {
  86. Q_D( const ctkTransferFunctionRepresentation );
  87. return d->VerticalGradientColor;
  88. }
  89. //-----------------------------------------------------------------------------
  90. void ctkTransferFunctionRepresentation::setVerticalGradientColor( QColor verticalGradientColor )
  91. {
  92. Q_D( ctkTransferFunctionRepresentation );
  93. d->VerticalGradientColor = verticalGradientColor;
  94. }
  95. //-----------------------------------------------------------------------------
  96. void ctkTransferFunctionRepresentation::setTransferFunction(ctkTransferFunction* transferFunction)
  97. {
  98. Q_D(ctkTransferFunctionRepresentation);
  99. if (d->TransferFunction == transferFunction)
  100. {
  101. return;
  102. }
  103. d->TransferFunction = transferFunction;
  104. connect( d->TransferFunction, SIGNAL(changed()),
  105. this, SLOT(onTransferFunctionChanged()),
  106. Qt::UniqueConnection);
  107. this->onTransferFunctionChanged();
  108. }
  109. //-----------------------------------------------------------------------------
  110. ctkTransferFunction* ctkTransferFunctionRepresentation::transferFunction()const
  111. {
  112. Q_D(const ctkTransferFunctionRepresentation);
  113. return d->TransferFunction;
  114. }
  115. //-----------------------------------------------------------------------------
  116. void ctkTransferFunctionRepresentation::onTransferFunctionChanged()
  117. {
  118. Q_D(ctkTransferFunctionRepresentation);
  119. // delete cache here
  120. d->Path = QPainterPath();
  121. d->Points.clear();
  122. }
  123. //-----------------------------------------------------------------------------
  124. const QPainterPath& ctkTransferFunctionRepresentation::curve()const
  125. {
  126. Q_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. Q_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. Q_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. Q_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. Q_D(ctkTransferFunctionRepresentation);
  231. int count = d->TransferFunction ? d->TransferFunction->count() : 0;
  232. if (count <= 0)
  233. {
  234. return;
  235. }
  236. d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
  237. d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
  238. d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
  239. d->RangeXDiff = this->computeRangeXDiff(QRectF(0.,0.,1.,1.), d->WorldRangeX);
  240. d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
  241. d->RangeYDiff = this->computeRangeYDiff(QRectF(0.,0.,1.,1.), d->WorldRangeY);
  242. d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
  243. ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
  244. ctkControlPoint* nextCP = 0;
  245. qreal startPos = this->mapXToScene(this->posX(startCP->x()));
  246. qreal nextPos;
  247. //
  248. //if we have no colors in value (i.e. can't convert value to color)
  249. if (! d->TransferFunction->value(0).canConvert<QColor>())
  250. {
  251. // create vertical gradient
  252. d->Gradient = QLinearGradient(0., 0., 0., 1.);
  253. // red
  254. d->Gradient.setColorAt(0, d->VerticalGradientColor );
  255. // to black
  256. d->Gradient.setColorAt(1, QColor::fromRgbF(0., 0., 0., 1. ));
  257. return;
  258. }
  259. // classic gradient if we have colors in value
  260. d->Gradient = QLinearGradient(0., 0., 1., 0.);
  261. d->Gradient.setColorAt(startPos, this->color(startCP));
  262. for(int i = 1; i < count; ++i)
  263. {
  264. nextCP = d->TransferFunction->controlPoint(i);
  265. nextPos = this->mapXToScene(this->posX(nextCP));
  266. if (this->transferFunction()->isDiscrete())
  267. {
  268. qreal midPoint = (startPos + nextPos) / 2;
  269. d->Gradient.setColorAt(midPoint, this->color(startCP));
  270. d->Gradient.setColorAt(midPoint + std::numeric_limits<qreal>::epsilon(), this->color(nextCP));
  271. }
  272. else if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
  273. {
  274. QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
  275. foreach(const ctkPoint& p, points)
  276. {
  277. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  278. }
  279. //no need, d->Gradient.setColorAt(nextPos, this->color(nextCP));
  280. }
  281. else //dynamic_cast<ctkBezierControlPoint*>(startCP))
  282. { // TODO handle bezier points with color
  283. QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
  284. QList<ctkPoint>::iterator it = points.begin();
  285. QList<QPointF> bezierPoints;
  286. foreach(const ctkPoint& p, points)
  287. {
  288. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  289. }
  290. nextPos = this->mapXToScene(this->posX(points[points.size() - 1]));
  291. }
  292. //qDebug() << i << points[0] << points[1] << points[2] << points[3];
  293. delete startCP;
  294. startCP = nextCP;
  295. startPos = nextPos;
  296. }
  297. d->Gradient.setColorAt(startPos, this->color(startCP));
  298. if (startCP)
  299. {
  300. delete startCP;
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. QList<ctkPoint> ctkTransferFunctionRepresentation::bezierParams(
  305. ctkControlPoint* start, ctkControlPoint* end) const
  306. {
  307. Q_ASSERT(start);
  308. Q_ASSERT(end);
  309. QList<ctkPoint> points;
  310. ctkBezierControlPoint* bezierCP = dynamic_cast<ctkBezierControlPoint*>(start);
  311. if (!bezierCP)
  312. {// just duplicate start and end into p1 and p2
  313. points << start->P;
  314. points << start->P;
  315. points << end->P;
  316. points << end->P;
  317. return points;
  318. }
  319. points << start->P;
  320. points << bezierCP->P1;
  321. points << bezierCP->P2;
  322. points << end->P;
  323. return points;
  324. }
  325. //-----------------------------------------------------------------------------
  326. QList<ctkPoint> ctkTransferFunctionRepresentation::nonLinearPoints(
  327. ctkControlPoint* start, ctkControlPoint* end) const
  328. {
  329. Q_ASSERT(start);
  330. ctkNonLinearControlPoint* nonLinearCP =
  331. dynamic_cast<ctkNonLinearControlPoint*>(start);
  332. if (!nonLinearCP)
  333. {
  334. QList<ctkPoint> points;
  335. points << start->P;
  336. points << end->P;
  337. return points;
  338. }
  339. return nonLinearCP->SubPoints;
  340. }
  341. //-----------------------------------------------------------------------------
  342. QColor ctkTransferFunctionRepresentation::color(const QVariant& v) const
  343. {
  344. //Q_ASSERT(v.canConvert<QColor>());
  345. if (v.canConvert<QColor>())
  346. {
  347. return v.value<QColor>();
  348. }
  349. else
  350. {
  351. //black background
  352. QColor defaultColor(0., 0., 0.);
  353. return defaultColor;
  354. }
  355. return QColor();
  356. }
  357. //-----------------------------------------------------------------------------
  358. qreal ctkTransferFunctionRepresentation::computeRangeXDiff(const QRectF& rect, qreal rangeX[2])
  359. {
  360. return rect.width() / (rangeX[1] - rangeX[0]);
  361. }
  362. //-----------------------------------------------------------------------------
  363. qreal ctkTransferFunctionRepresentation::computeRangeXOffset(qreal rangeX[2])
  364. {
  365. return rangeX[0];
  366. }
  367. //-----------------------------------------------------------------------------
  368. qreal ctkTransferFunctionRepresentation::computeRangeYDiff(const QRectF& rect, const QVariant rangeY[2])
  369. {
  370. qreal rangeYDiff = rect.height();
  371. qreal rangePosY[2];
  372. rangePosY[0] = this->posY(rangeY[0]);
  373. rangePosY[1] = this->posY(rangeY[1]);
  374. if (rangePosY[1] == rangePosY[0])
  375. {
  376. rangeYDiff /= rangePosY[0];
  377. return rangeYDiff;
  378. }
  379. rangeYDiff /= rangePosY[1] - rangePosY[0];
  380. return rangeYDiff;
  381. }
  382. //-----------------------------------------------------------------------------
  383. qreal ctkTransferFunctionRepresentation::computeRangeYOffset(const QVariant rangeY[2])
  384. {
  385. qreal rangePosY[2];
  386. rangePosY[0] = this->posY(rangeY[0]);
  387. rangePosY[1] = this->posY(rangeY[1]);
  388. if (rangePosY[1] == rangePosY[0])
  389. {
  390. return 0.;
  391. }
  392. return rangePosY[0];
  393. }
  394. //-----------------------------------------------------------------------------
  395. qreal ctkTransferFunctionRepresentation::posX(const qreal& x)const
  396. {
  397. return x;
  398. }
  399. //-----------------------------------------------------------------------------
  400. qreal ctkTransferFunctionRepresentation::posY(const QVariant& value)const
  401. {
  402. Q_ASSERT(value.canConvert<qreal>() || value.canConvert<QColor>());
  403. if (value.canConvert<QColor>())
  404. {
  405. return value.value<QColor>().alphaF();
  406. }
  407. return value.toReal();
  408. }
  409. //-----------------------------------------------------------------------------
  410. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkControlPoint* cp)const
  411. {
  412. return QPointF(this->mapXToScene(this->posX(cp->x())),
  413. this->mapYToScene(this->posY(cp->value())));
  414. }
  415. //-----------------------------------------------------------------------------
  416. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkPoint& point)const
  417. {
  418. return QPointF( this->mapXToScene(this->posX(point.X)),
  419. this->mapYToScene(this->posY(point.Value)));
  420. }
  421. //-----------------------------------------------------------------------------
  422. qreal ctkTransferFunctionRepresentation::mapXToScene(qreal xPos)const
  423. {
  424. Q_D(const ctkTransferFunctionRepresentation);
  425. return (xPos - d->RangeXOffSet) * d->RangeXDiff;
  426. }
  427. //-----------------------------------------------------------------------------
  428. qreal ctkTransferFunctionRepresentation::mapYToScene(qreal yPos)const
  429. {
  430. Q_D(const ctkTransferFunctionRepresentation);
  431. return d->height() - (yPos - d->RangeYOffSet) * d->RangeYDiff;
  432. }
  433. //-----------------------------------------------------------------------------
  434. qreal ctkTransferFunctionRepresentation::mapXFromScene(qreal scenePosX)const
  435. {
  436. Q_D(const ctkTransferFunctionRepresentation);
  437. return (scenePosX / d->RangeXDiff) + d->RangeXOffSet;
  438. }
  439. //-----------------------------------------------------------------------------
  440. qreal ctkTransferFunctionRepresentation::mapYFromScene(qreal scenePosY)const
  441. {
  442. Q_D(const ctkTransferFunctionRepresentation);
  443. return ((d->height() - scenePosY) / d->RangeYDiff) + d->RangeYOffSet ;
  444. }