ctkTransferFunctionScene.cpp 14 KB

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