From 9a3f321f66d5490801c2ce1f2c890c6dad9d63d8 Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Fri, 11 Sep 2020 23:19:31 +0200 Subject: [PATCH] improved and bugfixed handling of aspectRatio and its documentation --- lib/jkqtplotter/jkqtpbaseplotter.cpp | 64 ++++++++++++--------------- lib/jkqtplotter/jkqtpbaseplotter.h | 50 +++++++++++++++------ lib/jkqtplotter/jkqtplotter.cpp | 66 +++++++++++++++++++++------- 3 files changed, 114 insertions(+), 66 deletions(-) diff --git a/lib/jkqtplotter/jkqtpbaseplotter.cpp b/lib/jkqtplotter/jkqtpbaseplotter.cpp index 35d7196eaf..10601cf729 100644 --- a/lib/jkqtplotter/jkqtpbaseplotter.cpp +++ b/lib/jkqtplotter/jkqtpbaseplotter.cpp @@ -441,11 +441,13 @@ void JKQTBasePlotter::zoomOut(double factor) { void JKQTBasePlotter::setMaintainAspectRatio(bool value) { maintainAspectRatio=value; + setAspectRatio(aspectRatio); redrawPlot(); } void JKQTBasePlotter::setMaintainAxisAspectRatio(bool value) { maintainAxisAspectRatio=value; + setAspectRatio(axisAspectRatio); redrawPlot(); } @@ -688,53 +690,42 @@ void JKQTBasePlotter::loadSettings(const QSettings &settings, const QString& gro -void JKQTBasePlotter::setXY(double xminn, double xmaxx, double yminn, double ymaxx){ - xAxis->setRange(xminn, xmaxx); - yAxis->setRange(yminn, ymaxx); - if (maintainAxisAspectRatio) { +void JKQTBasePlotter::correctXYRangeForAspectRatio(double& xminn, double& xmaxx, double& yminn, double& ymaxx) const { + if (xminn>xmaxx) std::swap(xminn,xmaxx); + if (yminn>ymaxx) std::swap(yminn,ymaxx); + if (maintainAspectRatio) { if (xAxis->isLinearAxis() && yAxis->isLinearAxis()) { - const double mid=(yAxis->getMax()+yAxis->getMin())/2.0; - const double w=fabs(xmaxx-xminn)/axisAspectRatio; - yAxis->setRange(mid-w/2.0, mid+w/2.0); + const double mid=(yminn+ymaxx)/2.0; + const double w=fabs(xmaxx-xminn)/aspectRatio; + //qDebug()<<"mod y from "<isLogAxis() && yAxis->isLogAxis()) { - const double mid=(log(yAxis->getMax())+log(yAxis->getMin()))/2.0; - const double w=fabs(log(xmaxx)-log(xminn))/axisAspectRatio; - yAxis->setRange(exp(mid-w/2.0), exp(mid+w/2.0)); + const double mid=(log(yminn)+log(ymaxx))/2.0; + const double w=fabs(log(xmaxx)-log(xminn))/aspectRatio; + yminn=exp(mid-w/2.0); + ymaxx=exp(mid+w/2.0); } } +} + + +void JKQTBasePlotter::setXY(double xminn, double xmaxx, double yminn, double ymaxx) { + + correctXYRangeForAspectRatio(xminn, xmaxx, yminn, ymaxx); + + xAxis->setRange(xminn, xmaxx); + yAxis->setRange(yminn, ymaxx); + if (emitSignals) emit zoomChangedLocally(xAxis->getMin(), xAxis->getMax(), yAxis->getMin(), yAxis->getMax(), this); } void JKQTBasePlotter::setX(double xminn, double xmaxx){ - xAxis->setRange(xminn, xmaxx); - if (maintainAxisAspectRatio) { - if (xAxis->isLinearAxis() && yAxis->isLinearAxis()) { - const double mid=(yAxis->getMax()+yAxis->getMin())/2.0; - const double w=fabs(xmaxx-xminn)/axisAspectRatio; - yAxis->setRange(mid-w/2.0, mid+w/2.0); - } else if (xAxis->isLogAxis() && yAxis->isLogAxis()) { - const double mid=(log(yAxis->getMax())+log(yAxis->getMin()))/2.0; - const double w=fabs(log(xmaxx)-log(xminn))/axisAspectRatio; - yAxis->setRange(exp(mid-w/2.0), exp(mid+w/2.0)); - } - } - if (emitSignals) emit zoomChangedLocally(xAxis->getMin(), xAxis->getMax(), yAxis->getMin(), yAxis->getMax(), this); + setXY(xminn, xmaxx, yAxis->getMin(), yAxis->getMax()); } void JKQTBasePlotter::setY(double yminn, double ymaxx) { - yAxis->setRange(yminn, ymaxx); - if (maintainAxisAspectRatio) { - if (xAxis->isLinearAxis() && yAxis->isLinearAxis()) { - const double mid=(xAxis->getMax()+xAxis->getMin())/2.0; - const double w=fabs(ymaxx-yminn)*axisAspectRatio; - xAxis->setRange(mid-w/2.0, mid+w/2.0); - } else if (xAxis->isLogAxis() && yAxis->isLogAxis()) { - const double mid=(log(xAxis->getMax())+log(xAxis->getMin()))/2.0; - const double w=fabs(log(ymaxx)-log(yminn))/axisAspectRatio; - xAxis->setRange(exp(mid-w/2.0), exp(mid+w/2.0)); - } - } - if (emitSignals) emit zoomChangedLocally(xAxis->getMin(), xAxis->getMax(), yAxis->getMin(), yAxis->getMax(), this); + setXY(xAxis->getMin(), xAxis->getMax(), yminn, ymaxx); } void JKQTBasePlotter::setAbsoluteX(double xminn, double xmaxx) { @@ -2394,6 +2385,7 @@ void JKQTBasePlotter::setAspectRatio(double __value) { if (jkqtp_approximatelyUnequal(this->aspectRatio , __value)) { this->aspectRatio = __value; + setXY(getXMin(), getXMax(), getYMin(), getYMax()); redrawPlot(); } } diff --git a/lib/jkqtplotter/jkqtpbaseplotter.h b/lib/jkqtplotter/jkqtpbaseplotter.h index e0f28789c4..6e748413c0 100644 --- a/lib/jkqtplotter/jkqtpbaseplotter.h +++ b/lib/jkqtplotter/jkqtpbaseplotter.h @@ -167,7 +167,9 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPPaintDeviceAdapter { * * You can set two different aspect ratios: * - The ratio of plotWidth/plotHeight (setAspectRatio(), setMaintainAspectRatio()) will keep the plots pixel-width and height at a certain value. + * \f[ \mbox{aspectRatio}=\frac{\mbox{plotWidth}}{\mbox{plotHeight}} \f] * - The ratio of (xmax-xmin)/(ymax-ymin) (setAxisAspectRatio(), setMaintainAxisAspectRatio()) will keep the displayed axis ranges in a certain ratio. + * \f[ \mbox{axisAspectRatio}=\frac{\left|x_\text{max}-x_\text{min}\right|}{\left|y_\text{max}-y_\text{min}\right|} \f] * . * So to achieve different effects, use these combinations: * - you have a 200x100 range where each 1x1-pixel should have an aspect ratio of 4: @@ -656,14 +658,14 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { /** \copydoc JKQTBasePlotterStyle::plotBorderRight */ int getPlotBorderRight() const; - /** \brief returns whether the maintaining of the data aspect ratio is enabled or disabled */ + /** \brief returns whether the maintaining of the data aspect ratio is enabled or disabled \see aspectRatio */ bool doesMaintainAspectRatio() const; - /** \brief returns the data aspect ratio, enforced with setMaintainApsectRatio(true) */ + /** \brief returns the data aspect ratio, enforced with setMaintainApsectRatio(true) \see aspectRatio */ double getAspectRatio() const; - /** \brief returns whether the maintaining of the axis aspect ratio is enabled or disabled */ + /** \brief returns whether the maintaining of the axis aspect ratio is enabled or disabled \see axisAspectRatio */ bool doesMaintainAxisAspectRatio() const; - /** \brief returns the axis aspect ratio, enforced with setMaintainAxisApsectRatio(true) */ + /** \brief returns the axis aspect ratio, enforced with setMaintainAxisApsectRatio(true) \see axisAspectRatio */ double getAxisAspectRatio() const; /** \copydoc JKQTBasePlotterStyle::useAntiAliasingForSystem */ bool isUsingAntiAliasingForSystem() const; @@ -1075,7 +1077,13 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { QSizeF getTextSizeSize(const QString& fontName, double fontSize, const QString& text, QPainter &painter); - + /** \brief takes a new axis range \a xminn ... \a xmaxx and \a yminn ... \a ymaxx and corrects the values to match the currently set axisAspectRatio + * + * This function is used by setXY(), setX(), setY(). + * + * \see axisAspectRatio, setXY(), setX(), setY() + */ + void correctXYRangeForAspectRatio(double &xminn, double &xmaxx, double &yminn, double &ymaxx) const; signals: /** \brief signal: emitted whenever the user selects a new x-y zoom range (by mouse) */ void zoomChangedLocally(double newxmin, double newxmax, double newymin, double newymax, JKQTBasePlotter* sender); @@ -1391,10 +1399,10 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { /** \brief zooms out of the graph (the same as turning the mouse wheel) by the given factor */ void zoomOut(double factor=2.0); - /** \brief en-/disables the maintaining of the data aspect ratio */ + /** \brief en-/disables the maintaining of the data aspect ratio \see aspectRatio */ void setMaintainAspectRatio(bool value); - /** \brief en-/disables the maintaining of the axis aspect ratio */ + /** \brief en-/disables the maintaining of the axis aspect ratio \see axisAspectRatio */ void setMaintainAxisAspectRatio(bool value); /** \brief set filename and prefix, used by loadUserSettings() and saveUserSettings() @@ -1441,9 +1449,9 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { /** \brief set all graphs invisible, except graph start, start+n, start+2*n, ... */ void setOnlyNthGraphsVisible(int start, int n); - /** \brief sets the data aspect ratio, enforced with setMaintainApsectRatio(true) */ + /** \brief sets the data aspect ratio, enforced with setMaintainApsectRatio(true) \see aspectRatio */ void setAspectRatio(double __value); - /** \brief sets the axis aspect ratio, enforced with setMaintainAxisApsectRatio(true) */ + /** \brief sets the axis aspect ratio, enforced with setMaintainAxisApsectRatio(true) \see axisAspectRatio */ void setAxisAspectRatio(double __value); /*! \copydoc JKQTBasePlotterStyle::useAntiAliasingForSystem */ void setUseAntiAliasingForSystem(bool __value); @@ -1952,14 +1960,26 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { */ int internalPlotHeight; - /** \brief indicates whether the widget should maintain an aspect ratio of plotwidth and plotheight */ + /** \brief indicates whether the widget should maintain an aspect ratio of plotwidth and plotheight + * + * \see aspectRatio + */ bool maintainAspectRatio; - /** \brief the aspect ratio of plotwidth and plotheight to maintain, if \c maintainAspectRatio==true */ + /** \brief the aspect ratio of plotwidth and plotheight to maintain, if \c maintainAspectRatio==true + * + * \f[ \mbox{aspectRatio}=\frac{\mbox{plotWidth}}{\mbox{plotHeight}} \f] + * + * \see maintainAspectRatio + */ double aspectRatio; /** \brief indicates whether the axes should maintain an aspect ratio * - * \note An axis aspect ration is only well defined for linear axes (if both axes are linear). + * When the axis aspect ratio is to be maintained and new axis ranges are set (e.g. when calling setXY() ), + * the given axis ranges are modified, so + * \f[ \mbox{axisAspectRatio}=\frac{\left|x_\text{max}-x_\text{min}\right|}{\left|y_\text{max}-y_\text{min}\right|} \f] + * + * \note An axis aspect ratio is only well defined for linear axes (if both axes are linear). * If both axes a logarithmic, the axis ration is defined for log(axismax)-log(axismin). * For other combinations of axes, this function is deactivated * @@ -1968,7 +1988,11 @@ class JKQTPLOTTER_LIB_EXPORT JKQTBasePlotter: public QObject { bool maintainAxisAspectRatio; /** \brief the aspect ratio of axis widths to maintain, if \c maintainAxisAspectRatio==true * - * \note An axis aspect ration is only well defined for linear axes (if both axes are linear). + * When the axis aspect ratio is to be maintained and new axis ranges are set (e.g. when calling setXY() ), + * the given axis ranges are modified, so + * \f[ \mbox{axisAspectRatio}=\frac{\left|x_\text{max}-x_\text{min}\right|}{\left|y_\text{max}-y_\text{min}\right|} \f] + * + * \note An axis aspect ratio is only well defined for linear axes (if both axes are linear). * If both axes a logarithmic, the axis ration is defined for log(axismax)-log(axismin). * For other combinations of axes, this function is deactivated * diff --git a/lib/jkqtplotter/jkqtplotter.cpp b/lib/jkqtplotter/jkqtplotter.cpp index 891ddd6ced..312aff58ca 100644 --- a/lib/jkqtplotter/jkqtplotter.cpp +++ b/lib/jkqtplotter/jkqtplotter.cpp @@ -484,16 +484,31 @@ void JKQTPlotter::paintUserAction() { painter.setRenderHint(JKQTPEnhancedPainter::TextAntialiasing, true); painter.setPen(plotterStyle.userActionOverlayPen); if (jkqtp_approximatelyUnequal(mouseDragRectXEnd,mouseDragRectXStart) && jkqtp_approximatelyUnequal(mouseDragRectYEnd,mouseDragRectYStart)) { - double x1=plotter->x2p(mouseDragRectXStart)*magnification; - double y1=plotter->y2p(mouseDragRectYStart)*magnification; - double x2=plotter->x2p(mouseDragRectXEnd)*magnification; - double y2=plotter->y2p(mouseDragRectYEnd)*magnification; - double dx=x2-x1; - double dy=y2-y1; - if ((currentMouseDragAction.mode==jkqtpmdaZoomByRectangle) || (currentMouseDragAction.mode==jkqtpmdaDrawRectangleForEvent)) { + const double x1=plotter->x2p(mouseDragRectXStart)*magnification; + const double y1=plotter->y2p(mouseDragRectYStart)*magnification; + const double x2=plotter->x2p(mouseDragRectXEnd)*magnification; + const double y2=plotter->y2p(mouseDragRectYEnd)*magnification; + const double dx=x2-x1; + const double dy=y2-y1; + if (currentMouseDragAction.mode==jkqtpmdaDrawRectangleForEvent) { painter.fillRect(QRectF(x1, y1, x2-x1, y2-y1), plotterStyle.userActionOverlayBrush); painter.setPen(plotterStyle.userActionOverlayPen); painter.drawRect(QRectF(x1, y1, x2-x1, y2-y1)); + } else if (currentMouseDragAction.mode==jkqtpmdaZoomByRectangle) { + double xmin=mouseDragRectXStart; + double xmax=mouseDragRectXEnd; + double ymin=mouseDragRectYStart; + double ymax=mouseDragRectYEnd; + plotter->correctXYRangeForAspectRatio(xmin,xmax,ymin,ymax); + + const double xz1=plotter->x2p(xmin)*magnification; + const double yz1=plotter->y2p(ymin)*magnification; + const double xz2=plotter->x2p(xmax)*magnification; + const double yz2=plotter->y2p(ymax)*magnification; + + painter.fillRect(QRectF(xz1, yz1, xz2-xz1, yz2-yz1), plotterStyle.userActionOverlayBrush); + painter.setPen(plotterStyle.userActionOverlayPen); + painter.drawRect(QRectF(xz1, yz1, xz2-xz1, yz2-yz1)); } else if (currentMouseDragAction.mode==jkqtpmdaDrawCircleForEvent) { painter.setPen(plotterStyle.userActionOverlayPen); painter.setBrush(plotterStyle.userActionOverlayBrush); @@ -717,7 +732,13 @@ void JKQTPlotter::mouseMoveEvent ( QMouseEvent * event ) { event->accept(); //std::cout<modifiers()); + double xmin=mouseDragRectXStart; + double xmax=mouseDragRectXEnd; + double ymin=mouseDragRectYStart; + double ymax=mouseDragRectYEnd; + plotter->correctXYRangeForAspectRatio(xmin,xmax,ymin,ymax); + emit plotNewZoomRectangle(xmin,xmax,ymin,ymax, event->modifiers()); + //emit plotNewZoomRectangle(mouseDragRectXStart, mouseDragRectXEnd, mouseDragRectYStart, mouseDragRectYEnd, event->modifiers()); } if ((currentMouseDragAction.mode==jkqtpmdaScribbleForEvents) && (jkqtp_approximatelyUnequal(mouseDragRectXStart,mouseDragRectXEnd) || jkqtp_approximatelyUnequal(mouseDragRectYStart,mouseDragRectYEnd)) ) { emit userScribbleClick(mouseDragRectXEnd, mouseDragRectYEnd, event->modifiers(), false, false); @@ -816,6 +837,9 @@ void JKQTPlotter::mouseReleaseEvent ( QMouseEvent * event ){ double ymin=mouseDragRectYStart; double ymax=mouseDragRectYEnd; + + plotter->correctXYRangeForAspectRatio(xmin,xmax,ymin,ymax); + emit zoomChangedLocally(xmin, xmax, ymin, ymax, this); plotter->setXY(xmin, xmax, ymin, ymax); } else if (currentMouseDragAction.mode==jkqtpmdaPanPlotOnRelease) { @@ -924,6 +948,14 @@ void JKQTPlotter::keyReleaseEvent(QKeyEvent *event) { } void JKQTPlotter::wheelEvent ( QWheelEvent * event ) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + const double wheel_x=event->position().x(); + const double wheel_y=event->position().y(); +#else + const int wheel_x=event->x(); + const int wheel_y=event->y(); +#endif + //qDebug()<<"wheelEvent("<modifiers()<<"): plotterStyle.registeredMouseWheelActions="<modifiers()); //qDebug()<<"wheelEvent("<modifiers()<<"): plotterStyle.registeredMouseWheelActions="<modifiers()<<"):ZoomByWheel"; const double factor=pow(2.0, 1.0*static_cast(event->angleDelta().y())/120.0)*2.0; - double xmin=plotter->p2x(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor); - double xmax=plotter->p2x(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor); - double ymin=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor); - double ymax=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor); - if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + double xmin=plotter->p2x(static_cast(wheel_x)/magnification-static_cast(plotter->getPlotWidth())/factor); + double xmax=plotter->p2x(static_cast(wheel_x)/magnification+static_cast(plotter->getPlotWidth())/factor); + double ymin=plotter->p2y(static_cast(wheel_y)/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor); + double ymax=plotter->p2y(static_cast(wheel_y)/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor); + if ( (wheel_x/magnificationgetInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { xmin=getXMin(); xmax=getXMax(); - } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + } else if (((wheel_y-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { ymin=getYMin(); ymax=getYMax(); } @@ -960,9 +992,9 @@ void JKQTPlotter::wheelEvent ( QWheelEvent * event ) { if (d.x()<0 && d.x()>-10) d.setX(-10); if (d.y()>=0 && d.y()<10) d.setY(10); if (d.y()<0 && d.y()>-10) d.setY(-10); - if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + if ( (wheel_x/magnificationgetInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { zoomRect.translate(0, d.y()); - } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + } else if (((wheel_y-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { zoomRect.translate(d.x(), 0); } else { zoomRect.translate(d.x(), d.y()); @@ -974,7 +1006,7 @@ void JKQTPlotter::wheelEvent ( QWheelEvent * event ) { event->accept(); - emit plotMouseWheelOperated(plotter->p2x(event->x()), plotter->p2x(event->y()), event->modifiers(), event->angleDelta().x(), event->angleDelta().y()); + emit plotMouseWheelOperated(plotter->p2x(wheel_x), plotter->p2x(wheel_y), event->modifiers(), event->angleDelta().x(), event->angleDelta().y()); updateCursor(); currentMouseDragAction.clear();