Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
centralplotframe.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 #include "centralplotframe.h"
21 #include <QGestureEvent>
22 #include <QApplication>
23 #include <cmath>
24 #include "qwt_plot_panner.h"
25 #include "qwt_plot_canvas.h"
26 #include "qwt_plot_magnifier.h"
27 #include "qwt_plot_zoomer.h"
28 #include "core/system/log.h"
29 #include "keyindexscaledraw.h"
30 #include "keyindexscaleengine.h"
31 
32 
34 
35 CentralPlotFrame::CentralPlotFrame(int numberOfKeys, int keyOffset) :
36  mNumberOfKeys(numberOfKeys),
37  mKeyOffset(keyOffset)
38 {
39  // important: accept touch events, so they dont get propagated
40  setAttribute(Qt::WA_AcceptTouchEvents);
41 
42  setAxisScaleEngine(xBottom, new KeyIndexScaleEngine(numberOfKeys, mKeyOffset));
43 
44  KeyIndexScaleDraw *xScaleDraw = new KeyIndexScaleDraw;
45  setAxisScaleDraw(xBottom, xScaleDraw);
46  QObject::connect(this, SIGNAL(keyWidthChanged(double)), xScaleDraw, SLOT(setKeyWidth(double)));
47 
48  // panning with the left mouse button
49  QwtPlotPanner *panner = new QwtPlotPanner(canvas());
50  panner->setMouseButton(Qt::LeftButton, Qt::ControlModifier);
51  QObject::connect(this, SIGNAL(moveCanvas(int,int)), panner, SLOT(moveCanvas(int,int)));
52 
53  // zoom in/out with the wheel
54  ( void ) new QwtPlotMagnifier( canvas() );
55 
56  // rect zoomer and used manually for touch
57  mPlotZoomer = new QwtPlotZoomer(canvas());
58  mPlotZoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton);
59 
60  // this zoomer is just for on the fly updates
61  mNonStackInvisibleZoomer = new QwtPlotZoomer(canvas());
62  mNonStackInvisibleZoomer->setEnabled(false);
63 }
64 
66  return transform(QwtPlot::xBottom, 3) - transform(QwtPlot::xBottom, 2);
67 }
68 
70  bool isLogX = dynamic_cast<QwtLogScaleEngine*>(axisScaleEngine(xBottom)) != nullptr;
71  bool isLogY = dynamic_cast<QwtLogScaleEngine*>(axisScaleEngine(yLeft)) != nullptr;
72 
73 
74  QStack< QRectF > zoomStack = mPlotZoomer->zoomStack();
75  QRectF newRect;
76 
77  auto invTransform = [this](const QPointF &p) {
78  return QPointF(this->invTransform(xBottom, p.x()),
79  this->invTransform(yLeft, p.y()));
80  };
81 
82  const QwtInterval xInterval(axisInterval(xBottom));
83  const QwtInterval yInterval(axisInterval(yLeft));
84  QPointF topLeft(xInterval.minValue(), yInterval.maxValue());
85  QPointF botRight(xInterval.maxValue(), yInterval.minValue());
86 
87  const QList<QTouchEvent::TouchPoint> &points(mTouchPoints);
88  if (points.size() == 1) {
89  const QTouchEvent::TouchPoint &p(points.first());
90  const QPointF pPrev(invTransform(p.lastPos()));
91  const QPointF pCurr(invTransform(p.pos()));
92  const QPointF d = pCurr - pPrev;
93  newRect = QRectF(topLeft, botRight);
94  if (dynamic_cast<QwtLogScaleEngine*>(axisScaleEngine(yLeft))) {
95  newRect.translate(-d.x(), 0);
96  newRect = newRect.normalized();
97 
98  double logFac = pCurr.y() / pPrev.y();
99  newRect.setBottom(newRect.bottom() / logFac);
100  newRect.setTop(newRect.top() / logFac);
101 
102  } else {
103  newRect.translate(-d);
104  }
105  } else if (points.size() == 2) {
106  const QTouchEvent::TouchPoint &p1(points.first());
107  const QTouchEvent::TouchPoint &p2(points.last());
108 
109  const QPointF p1Last = invTransform(p1.lastPos());
110  const QPointF p2Last = invTransform(p2.lastPos());
111  const QPointF p1Curr = invTransform(p1.pos());
112  const QPointF p2Curr = invTransform(p2.pos());
113 
114  // auto linearTransform = [](qreal x, qreal m, qreal t) {return m * x + t;};
115  auto invLinearTransform = [](qreal x, qreal m, qreal t) {return (x - t) / m;};
116 
117  // auto logTransform = [](qreal x, qreal m, qreal t) {return std::pow(x, m) * t;};
118  auto invLogTransform = [](qreal x, qreal m, qreal t) {return std::pow(x / t, 1/m);};
119 
120  auto linM = [](qreal p1, qreal c1, qreal p2, qreal c2) {return (c1 - c2) / (p1 - p2);};
121  auto logM = [](qreal p1, qreal c1, qreal p2, qreal c2) {return log(c1 / c2) / log(p1 / p2);};
122 
123  auto linT = [](qreal p1, qreal c1, qreal m) {return c1 - m * p1;};
124  auto logT = [](qreal p1, qreal c1, qreal m) {return c1 / std::pow(p1, m);};
125 
126  auto computeBounds = [&](int axis, bool log, qreal &top, qreal &bot) {
127  auto c = &QPointF::y;
128  if (axis == xBottom) {c = &QPointF::x;}
129 
130  const qreal p1 = (p1Last.*c)();
131  const qreal p2 = (p2Last.*c)();
132  const qreal c1 = (p1Curr.*c)();
133  const qreal c2 = (p2Curr.*c)();
134 
135  if (log) {
136  const qreal m = logM(p1, c1, p2, c2);
137  const qreal t = logT(p1, c1, m);
138  top = invLogTransform(top, m, t);
139  bot =invLogTransform(bot, m, t);
140  } else {
141  const qreal m = linM(p1, c1, p2, c2);
142  const qreal t = linT(p1, c1, m);
143 
144  top = invLinearTransform(top, m, t);
145  bot = invLinearTransform(bot, m, t);
146  }
147  };
148 
149  computeBounds(xBottom, isLogX, topLeft.rx(), botRight.rx());
150  computeBounds(yLeft, isLogY, topLeft.ry(), botRight.ry());
151 
152  newRect.setTopLeft(topLeft);
153  newRect.setBottomRight(botRight);
154  }
155 
156  if (final) {
157  if (newRect.isNull() == false) {
158  zoomStack << newRect.normalized();
159  mPlotZoomer->setZoomStack(zoomStack);
160  }
161  mTouchPoints.clear();
162  } else {
163  // check timer
164  if (mPlotTimer.elapsed() > FLYING_UPDATE_INTERVALL_IN_MS) {
165  mPlotTimer = QTime(); // reset, but dont start
166  if (newRect.isNull() == false) {
167  mNonStackInvisibleZoomer->zoom(newRect);
168  replot();
169  }
170  mTouchPoints.clear();
171  }
172  }
173 
174 }
175 
176 bool CentralPlotFrame::event(QEvent *e) {
177  if (dynamic_cast<QTouchEvent*>(e)) {
178  e->accept();
179  return touchEvent(static_cast<QTouchEvent *>(e));
180  }
181  return QwtPlot::event(e);
182 }
183 
184 bool CentralPlotFrame::touchEvent(QTouchEvent *e) {
185  QList<QTouchEvent::TouchPoint> points(e->touchPoints());
186  for (auto it = points.begin(); it != points.end(); ) {
187  if (it->state() == Qt::TouchPointReleased || it->state() == Qt::TouchPointPressed) {
188  it = points.erase(it);
189  } else {
190  it++;
191  }
192  }
193  if (points.size() != mTouchPoints.size()) {
194  applyTouchTransform(true);
195  mTouchPoints = points;
196  if (mTouchPoints.size() > 0) {
197  mPlotTimer.start();
198  }
199  } else {
200  for (int i = 0; i < points.size(); ++i) {
201  mTouchPoints[i].setPos(points[i].pos());
202  }
203  applyTouchTransform(false);
204  }
205 
206  return true;
207 }
208 
211  QwtPlot::updateLayout();
212 }
213 
214 void CentralPlotFrame::showEvent(QShowEvent *e) {
216  QwtPlot::showEvent(e);
217 }
218 
219 void CentralPlotFrame::resizeEvent(QResizeEvent *e) {
220  QwtPlot::resizeEvent(e);
222 }
223 
224 void CentralPlotFrame::paintEvent(QPaintEvent *e) {
225  QwtPlot::paintEvent(e);
226  mPlotTimer.start();
227 }
228 
230  setAxisAutoScale(QwtPlot::yLeft);
231  setAxisScale(QwtPlot::xBottom, mKeyOffset, mKeyOffset + mNumberOfKeys, 12);
232  replot();
233 
234  // set initial zoom rect to current view
235  const QwtInterval xInterval(axisInterval(xBottom));
236  const QwtInterval yInterval(axisInterval(yLeft));
237  QPointF topLeft(xInterval.minValue(), yInterval.maxValue());
238  QPointF botRight(xInterval.maxValue(), yInterval.minValue());
239  QStack<QRectF> stack;
240  QRectF r(topLeft, botRight);
241  stack << r.normalized();
242  mPlotZoomer->setZoomStack(stack);
243 }
244 
246  mPlotZoomer->zoom(mPlotZoomer->maxStackDepth());
247 }
248 
250  mPlotZoomer->zoom(-mPlotZoomer->maxStackDepth());
251 }
252 
254  mPlotZoomer->zoom(1);
255 }
256 
258  mPlotZoomer->zoom(-1);
259 }
virtual bool touchEvent(QTouchEvent *e)
QwtPlotZoomer * mPlotZoomer
virtual void resizeEvent(QResizeEvent *e) override
CentralPlotFrame(int numberOfKeys, int keyOffset)
const int mNumberOfKeys
void keyWidthChanged(double width)
void moveCanvas(int dx, int dy)
static const int FLYING_UPDATE_INTERVALL_IN_MS
virtual void paintEvent(QPaintEvent *e) override
virtual void updateLayout() override
virtual void showEvent(QShowEvent *e) override
QwtPlotZoomer * mNonStackInvisibleZoomer
void applyTouchTransform(int final)
virtual bool event(QEvent *) override
double currentTickDistanceInPixel() const
QList< QTouchEvent::TouchPoint > mTouchPoints