ctkTransferFunctionScene.cpp 15 KB

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