ctkTransferFunctionRepresentation.cpp 16 KB

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