1
0

FractalWidget.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #include "FractalWidget.h"
  2. #include <QMouseEvent>
  3. #include <QPainter>
  4. Q_DECLARE_METATYPE(mnd::MandelViewport)
  5. ViewportAnimation::ViewportAnimation(QObject* parent) :
  6. QPropertyAnimation{ parent }
  7. {
  8. }
  9. QVariant ViewportAnimation::interpolated(const QVariant& from, const QVariant& to,
  10. qreal progress) const
  11. {
  12. const mnd::MandelViewport& a = from.value<mnd::MandelViewport>();
  13. const mnd::MandelViewport& b = to.value<mnd::MandelViewport>();
  14. auto retVal = mnd::MandelViewport {
  15. a.x * (1 - progress) + b.x * progress,
  16. a.y * (1 - progress) + b.y * progress,
  17. a.width * (1 - progress) + b.width * progress,
  18. a.height * (1 - progress) + b.height * progress,
  19. };
  20. return QVariant::fromValue(retVal);
  21. }
  22. FractalWidget::FractalWidget(QWidget* parent) :
  23. FractalZoomWidget{ parent }
  24. {
  25. }
  26. void FractalWidget::mousePressEvent(QMouseEvent* me)
  27. {
  28. QOpenGLWidget::mousePressEvent(me);
  29. if (me->button() == Qt::RightButton) {
  30. rubberbanding = true;
  31. rubberband.setCoords(me->x(), me->y(), me->x(), me->y());
  32. update();
  33. me->accept();
  34. }
  35. else if (me->button() == Qt::LeftButton) {
  36. dragging = true;
  37. dragX = me->x();
  38. dragY = me->y();
  39. me->accept();
  40. }
  41. }
  42. void FractalWidget::mouseMoveEvent(QMouseEvent* me)
  43. {
  44. QOpenGLWidget::mouseMoveEvent(me);
  45. if (rubberbanding) {
  46. QRectF& rect = rubberband;
  47. double aspect = double(geometry().width()) / geometry().height();
  48. rect.setBottomRight(QPoint(me->x(), me->y()));
  49. if (rect.width() > rect.height() * aspect)
  50. rect.setHeight(rect.width() / aspect);
  51. else
  52. rect.setWidth(rect.height() * aspect);
  53. update();
  54. }
  55. else if (selectingPoint) {
  56. pointX = me->x();
  57. pointY = me->y();
  58. update();
  59. }
  60. else if (dragging) {
  61. double deltaX = me->x() - dragX;
  62. double deltaY = me->y() - dragY;
  63. auto& viewport = mandelInfo.view;
  64. viewport.x -= deltaX * viewport.width / this->width();
  65. viewport.y -= deltaY * viewport.height / this->height();
  66. targetViewport = viewport;
  67. dragX = me->x(); dragY = me->y();
  68. update();
  69. }
  70. me->accept();
  71. }
  72. void FractalWidget::mouseReleaseEvent(QMouseEvent* me)
  73. {
  74. QOpenGLWidget::mouseReleaseEvent(me);
  75. if (rubberbanding) {
  76. QRect rect = rubberband.toRect();
  77. if(rect.width() != 0 && rect.height() != 0) {
  78. QRect full = this->geometry();
  79. auto& viewport = targetViewport;
  80. viewport.x += mnd::Real(rect.left()) * viewport.width / full.width();
  81. viewport.y += mnd::Real(rect.top()) * viewport.height / full.height();
  82. viewport.width *= mnd::Real(rect.width()) / full.width();
  83. viewport.height *= mnd::Real(rect.height()) / full.height();
  84. viewport.normalize();
  85. viewport.adjustAspectRatio(getResolutionX(), getResolutionY());
  86. newAnimation();
  87. //currentViewport = viewport;
  88. }
  89. update();
  90. rubberbanding = false;
  91. }
  92. else if (selectingPoint) {
  93. selectingPoint = false;
  94. this->setMouseTracking(false);
  95. /*mnd::Real x = currentViewport.x + currentViewport.width * mnd::convert<mnd::Real>(float(me->x()) / width());
  96. mnd::Real y = currentViewport.y + currentViewport.height * mnd::convert<mnd::Real>(float(me->y()) / height());
  97. emit pointSelected(x, y);*/
  98. update();
  99. }
  100. dragging = false;
  101. }
  102. void FractalWidget::wheelEvent(QWheelEvent* we)
  103. {
  104. QOpenGLWidget::wheelEvent(we);
  105. float x = float(we->x()) / this->width();
  106. float y = float(we->y()) / this->height();
  107. float scale = ::powf(0.9975f, we->angleDelta().y());
  108. //mandelInfo.view.zoom(scale, x, y);
  109. zoom(scale, x, y);
  110. //if (!we->pixelDelta().isNull())
  111. // this->currentViewport = this->viewport;
  112. we->accept();
  113. }
  114. void FractalWidget::zoom(float factor)
  115. {
  116. targetViewport.zoomCenter(factor);
  117. newAnimation();
  118. update();
  119. }
  120. void FractalWidget::zoom(float factor, float fx, float fy)
  121. {
  122. targetViewport.zoom(factor, fx, fy);
  123. newAnimation();
  124. update();
  125. /*viewportSmoother = new ViewportAnimation(this);
  126. viewportSmoother->setStartValue(QVariant::fromValue(getViewport()));
  127. viewportSmoother->setEndValue(QVariant::fromValue(newVp));
  128. viewportSmoother->setTargetObject(this);
  129. viewportSmoother->setPropertyName("viewport");
  130. viewportSmoother->setDuration(200);
  131. viewportSmoother->setEasingCurve(QEasingCurve::OutExpo);
  132. viewportSmoother->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);*/
  133. }
  134. void FractalWidget::setViewport(const mnd::MandelViewport& viewport)
  135. {
  136. FractalZoomWidget::setViewport(viewport);
  137. targetViewport = mandelInfo.view;
  138. update();
  139. }
  140. const mnd::MandelViewport& FractalWidget::getViewport(void) const
  141. {
  142. return mandelInfo.view;
  143. }
  144. void FractalWidget::setDisplayInfo(bool displayInfo)
  145. {
  146. if (displayInfo != this->displayInfo) {
  147. this->displayInfo = displayInfo;
  148. update();
  149. }
  150. }
  151. void FractalWidget::resizeGL(int w, int h)
  152. {
  153. FractalZoomWidget::resizeGL(w, h);
  154. targetViewport.height = targetViewport.width * h / w;
  155. }
  156. void FractalWidget::paintGL(void)
  157. {
  158. FractalZoomWidget::paintGL();
  159. updateAnimations();
  160. if (displayInfo)
  161. drawDisplayInfo();
  162. }
  163. void FractalWidget::drawDisplayInfo(void)
  164. {
  165. QPainter infoPainter{ this };
  166. const float DIST_FROM_BORDER = 15;
  167. float maxWidth = this->width() - 2 * DIST_FROM_BORDER;
  168. mnd::Real distPerPixel = getViewport().width / this->width();
  169. float log10 = (mnd::convert<float>(mnd::log(distPerPixel)) + ::logf(maxWidth)) / ::logf(10);
  170. mnd::Real displayDist = mnd::pow(mnd::Real(10), ::floor(log10));
  171. float pixels = mnd::convert<float>(displayDist / distPerPixel);
  172. int factor = 1;
  173. for (int i = 9; i > 1; i--) {
  174. if (pixels * i < maxWidth) {
  175. factor *= i;
  176. pixels *= i;
  177. displayDist *= i;
  178. break;
  179. }
  180. }
  181. std::stringstream dis;
  182. if (::abs(log10) < 3) {
  183. dis << mnd::convert<float>(displayDist);
  184. }
  185. else {
  186. dis << factor << "e" << int(::floor(log10));
  187. }
  188. if (maxWidth > 400) {
  189. dis << "; per pixel: " << distPerPixel;
  190. }
  191. float lineY = this->height() - DIST_FROM_BORDER;
  192. float lineXEnd = DIST_FROM_BORDER + pixels;
  193. infoPainter.setPen(Qt::white);
  194. infoPainter.setFont(QFont("Arial", 12));
  195. infoPainter.drawLine(QPointF{ DIST_FROM_BORDER, lineY }, QPointF{ lineXEnd, lineY });
  196. infoPainter.drawLine(QPointF{ DIST_FROM_BORDER, lineY }, QPointF{ DIST_FROM_BORDER, lineY - 5 });
  197. infoPainter.drawLine(QPointF{ lineXEnd, lineY }, QPointF{ lineXEnd, lineY - 5 });
  198. infoPainter.drawText(int(DIST_FROM_BORDER), int(lineY - 20), int(lineXEnd - DIST_FROM_BORDER), 20,
  199. Qt::AlignCenter, QString::fromStdString(dis.str()));
  200. }
  201. void FractalWidget::newAnimation(void)
  202. {
  203. auto now = std::chrono::high_resolution_clock::now();
  204. lastAnimUpdate = now;
  205. }
  206. void FractalWidget::updateAnimations(void)
  207. {
  208. auto& currentViewport = mandelInfo.view;
  209. if (mnd::abs(currentViewport.width / targetViewport.width - 1.0) < 1e-3
  210. && mnd::abs(currentViewport.height / targetViewport.height - 1.0) < 1e-3) {
  211. // animation finished
  212. currentViewport = targetViewport;
  213. }
  214. else {
  215. auto now = std::chrono::high_resolution_clock::now();
  216. auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastAnimUpdate).count();
  217. const mnd::Real factor = mnd::Real(::pow(0.97, millis));
  218. const mnd::Real one(1.0);
  219. currentViewport.x = currentViewport.x * factor + targetViewport.x * (one - factor);
  220. currentViewport.y = currentViewport.y * factor + targetViewport.y * (one - factor);
  221. currentViewport.width = currentViewport.width * factor + targetViewport.width * (one - factor);
  222. currentViewport.height = currentViewport.height * factor + targetViewport.height * (one - factor);
  223. lastAnimUpdate = now;
  224. emit update();
  225. }
  226. }