ctkTransferFunctionRepresentation.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  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<ctkPoint>::iterator it = points.begin();
  214. QList<QPointF> bezierPoints;
  215. foreach(const ctkPoint& p, points)
  216. {
  217. bezierPoints << this->mapPointToScene(p);
  218. }
  219. d->Path.cubicTo(bezierPoints[1], bezierPoints[2], bezierPoints[3]);
  220. d->Points << bezierPoints[3];
  221. }
  222. //qDebug() << i << points[0] << points[1] << points[2] << points[3];
  223. delete startCP;
  224. startCP = nextCP;
  225. }
  226. if (startCP)
  227. {
  228. delete startCP;
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. void ctkTransferFunctionRepresentation::computeGradient()
  233. {
  234. Q_D(ctkTransferFunctionRepresentation);
  235. int count = d->TransferFunction ? d->TransferFunction->count() : 0;
  236. if (count <= 0)
  237. {
  238. return;
  239. }
  240. d->TransferFunction->range(d->WorldRangeX[0], d->WorldRangeX[1]);
  241. d->WorldRangeY[0] = this->posY(d->TransferFunction->minValue());
  242. d->WorldRangeY[1] = this->posY(d->TransferFunction->maxValue());
  243. d->RangeXDiff = this->computeRangeXDiff(QRectF(0.,0.,1.,1.), d->WorldRangeX);
  244. d->RangeXOffSet = this->computeRangeXOffset(d->WorldRangeX);
  245. d->RangeYDiff = this->computeRangeYDiff(QRectF(0.,0.,1.,1.), d->WorldRangeY);
  246. d->RangeYOffSet = this->computeRangeYOffset(d->WorldRangeY);
  247. ctkControlPoint* startCP = d->TransferFunction->controlPoint(0);
  248. ctkControlPoint* nextCP = 0;
  249. qreal startPos = this->mapXToScene(this->posX(startCP->x()));
  250. qreal nextPos;
  251. //
  252. //if we have no colors in value (i.e. can't convert value to color)
  253. if (! d->TransferFunction->value(0).canConvert<QColor>())
  254. {
  255. // create vertical gradient
  256. d->Gradient = QLinearGradient(0., 0., 0., 1.);
  257. // red
  258. d->Gradient.setColorAt(0, d->VerticalGradientColor );
  259. // to black
  260. d->Gradient.setColorAt(1, QColor::fromRgbF(0., 0., 0., 1. ));
  261. return;
  262. }
  263. // classic gradient if we have colors in value
  264. d->Gradient = QLinearGradient(0., 0., 1., 0.);
  265. d->Gradient.setColorAt(startPos, this->color(startCP));
  266. for(int i = 1; i < count; ++i)
  267. {
  268. nextCP = d->TransferFunction->controlPoint(i);
  269. nextPos = this->mapXToScene(this->posX(nextCP));
  270. if (this->transferFunction()->isDiscrete())
  271. {
  272. qreal midPoint = (startPos + nextPos) / 2;
  273. d->Gradient.setColorAt(midPoint, this->color(startCP));
  274. d->Gradient.setColorAt(midPoint + std::numeric_limits<qreal>::epsilon(), this->color(nextCP));
  275. }
  276. else if (dynamic_cast<ctkNonLinearControlPoint*>(startCP))
  277. {
  278. QList<ctkPoint> points = this->nonLinearPoints(startCP, nextCP);
  279. foreach(const ctkPoint& p, points)
  280. {
  281. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  282. }
  283. //no need, d->Gradient.setColorAt(nextPos, this->color(nextCP));
  284. }
  285. else //dynamic_cast<ctkBezierControlPoint*>(startCP))
  286. { // TODO handle bezier points with color
  287. QList<ctkPoint> points = this->bezierParams(startCP, nextCP);
  288. QList<ctkPoint>::iterator it = points.begin();
  289. QList<QPointF> bezierPoints;
  290. foreach(const ctkPoint& p, points)
  291. {
  292. d->Gradient.setColorAt(this->mapXToScene(this->posX(p)), this->color(p));
  293. }
  294. nextPos = this->mapXToScene(this->posX(points[points.size() - 1]));
  295. }
  296. //qDebug() << i << points[0] << points[1] << points[2] << points[3];
  297. delete startCP;
  298. startCP = nextCP;
  299. startPos = nextPos;
  300. }
  301. d->Gradient.setColorAt(startPos, this->color(startCP));
  302. if (startCP)
  303. {
  304. delete startCP;
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. QList<ctkPoint> ctkTransferFunctionRepresentation::bezierParams(
  309. ctkControlPoint* start, ctkControlPoint* end) const
  310. {
  311. Q_ASSERT(start);
  312. Q_ASSERT(end);
  313. QList<ctkPoint> points;
  314. ctkBezierControlPoint* bezierCP = dynamic_cast<ctkBezierControlPoint*>(start);
  315. if (!bezierCP)
  316. {// just duplicate start and end into p1 and p2
  317. points << start->P;
  318. points << start->P;
  319. points << end->P;
  320. points << end->P;
  321. return points;
  322. }
  323. points << start->P;
  324. points << bezierCP->P1;
  325. points << bezierCP->P2;
  326. points << end->P;
  327. return points;
  328. }
  329. //-----------------------------------------------------------------------------
  330. QList<ctkPoint> ctkTransferFunctionRepresentation::nonLinearPoints(
  331. ctkControlPoint* start, ctkControlPoint* end) const
  332. {
  333. Q_ASSERT(start);
  334. ctkNonLinearControlPoint* nonLinearCP =
  335. dynamic_cast<ctkNonLinearControlPoint*>(start);
  336. if (!nonLinearCP)
  337. {
  338. QList<ctkPoint> points;
  339. points << start->P;
  340. points << end->P;
  341. return points;
  342. }
  343. return nonLinearCP->SubPoints;
  344. }
  345. //-----------------------------------------------------------------------------
  346. QColor ctkTransferFunctionRepresentation::color(const QVariant& v) const
  347. {
  348. //Q_ASSERT(v.canConvert<QColor>());
  349. if (v.canConvert<QColor>())
  350. {
  351. return v.value<QColor>();
  352. }
  353. else
  354. {
  355. //black background
  356. QColor defaultColor(0., 0., 0.);
  357. return defaultColor;
  358. }
  359. return QColor();
  360. }
  361. //-----------------------------------------------------------------------------
  362. qreal ctkTransferFunctionRepresentation::computeRangeXDiff(const QRectF& rect, qreal rangeX[2])
  363. {
  364. return rect.width() / (rangeX[1] - rangeX[0]);
  365. }
  366. //-----------------------------------------------------------------------------
  367. qreal ctkTransferFunctionRepresentation::computeRangeXOffset(qreal rangeX[2])
  368. {
  369. return rangeX[0];
  370. }
  371. //-----------------------------------------------------------------------------
  372. qreal ctkTransferFunctionRepresentation::computeRangeYDiff(const QRectF& rect, const QVariant rangeY[2])
  373. {
  374. qreal rangeYDiff = rect.height();
  375. qreal rangePosY[2];
  376. rangePosY[0] = this->posY(rangeY[0]);
  377. rangePosY[1] = this->posY(rangeY[1]);
  378. if (rangePosY[1] == rangePosY[0])
  379. {
  380. rangeYDiff /= rangePosY[0];
  381. return rangeYDiff;
  382. }
  383. rangeYDiff /= rangePosY[1] - rangePosY[0];
  384. return rangeYDiff;
  385. }
  386. //-----------------------------------------------------------------------------
  387. qreal ctkTransferFunctionRepresentation::computeRangeYOffset(const QVariant rangeY[2])
  388. {
  389. qreal rangePosY[2];
  390. rangePosY[0] = this->posY(rangeY[0]);
  391. rangePosY[1] = this->posY(rangeY[1]);
  392. if (rangePosY[1] == rangePosY[0])
  393. {
  394. return 0.;
  395. }
  396. return rangePosY[0];
  397. }
  398. //-----------------------------------------------------------------------------
  399. qreal ctkTransferFunctionRepresentation::posX(const qreal& x)const
  400. {
  401. return x;
  402. }
  403. //-----------------------------------------------------------------------------
  404. qreal ctkTransferFunctionRepresentation::posY(const QVariant& value)const
  405. {
  406. Q_ASSERT(value.canConvert<qreal>() || value.canConvert<QColor>());
  407. if (value.canConvert<QColor>())
  408. {
  409. return value.value<QColor>().alphaF();
  410. }
  411. return value.toReal();
  412. }
  413. //-----------------------------------------------------------------------------
  414. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkControlPoint* cp)const
  415. {
  416. return QPointF(this->mapXToScene(this->posX(cp->x())),
  417. this->mapYToScene(this->posY(cp->value())));
  418. }
  419. //-----------------------------------------------------------------------------
  420. QPointF ctkTransferFunctionRepresentation::mapPointToScene(const ctkPoint& point)const
  421. {
  422. return QPointF( this->mapXToScene(this->posX(point.X)),
  423. this->mapYToScene(this->posY(point.Value)));
  424. }
  425. //-----------------------------------------------------------------------------
  426. qreal ctkTransferFunctionRepresentation::mapXToScene(qreal xPos)const
  427. {
  428. Q_D(const ctkTransferFunctionRepresentation);
  429. return (xPos - d->RangeXOffSet) * d->RangeXDiff;
  430. }
  431. //-----------------------------------------------------------------------------
  432. qreal ctkTransferFunctionRepresentation::mapYToScene(qreal yPos)const
  433. {
  434. Q_D(const ctkTransferFunctionRepresentation);
  435. return d->height() - (yPos - d->RangeYOffSet) * d->RangeYDiff;
  436. }
  437. //-----------------------------------------------------------------------------
  438. qreal ctkTransferFunctionRepresentation::mapXFromScene(qreal scenePosX)const
  439. {
  440. Q_D(const ctkTransferFunctionRepresentation);
  441. return (scenePosX / d->RangeXDiff) + d->RangeXOffSet;
  442. }
  443. //-----------------------------------------------------------------------------
  444. qreal ctkTransferFunctionRepresentation::mapYFromScene(qreal scenePosY)const
  445. {
  446. Q_D(const ctkTransferFunctionRepresentation);
  447. return ((d->height() - scenePosY) / d->RangeYDiff) + d->RangeYOffSet ;
  448. }