2016-07-09 10 views
4

Ich habe eine "Leinwand", auf die der Benutzer Pixel usw. aufzeichnen kann. Es funktioniert gut, aber meine Zoom-Funktion verwendet derzeit denselben Ursprung unabhängig von der Position der Maus. Ich möchte Funktionalität implementieren wie die von Google Maps' Zoomverhalten:Berechnen der Ansichtsverschiebung zum Zoomen an der Position des Mauszeigers

google maps zoom

Das heißt, die Herkunft des Zoom sollte die Position des Mauszeigers immer sein.

What I currently have ist nicht ganz richtig ...

Meine Versuche haben meist ersticht gewesen im Dunkeln, aber ich habe auch versucht, aus this answer ohne Erfolg mit dem Code.

main.cpp:

#include <QGuiApplication> 
#include <QtQuick> 

class Canvas : public QQuickPaintedItem 
{ 
    Q_OBJECT 

public: 
    Canvas() : 
     mTileWidth(25), 
     mTileHeight(25), 
     mTilesAcross(10), 
     mTilesDown(10), 
     mOffset(QPoint(400, 400)), 
     mZoomLevel(1) 
    { 
    } 

    void paint(QPainter *painter) override { 
     painter->translate(mOffset); 

     const int zoomedTileWidth = mTilesAcross * mZoomLevel; 
     const int zoomedTileHeight = mTilesDown * mZoomLevel; 
     const int zoomedMapWidth = qMin(mTilesAcross * zoomedTileWidth, qFloor(width())); 
     const int zoomedMapHeight = qMin(mTilesDown * zoomedTileHeight, qFloor(height())); 
     painter->fillRect(0, 0, zoomedMapWidth, zoomedMapHeight, QColor(Qt::gray)); 

     for (int y = 0; y < mTilesDown; ++y) { 
      for (int x = 0; x < mTilesAcross; ++x) { 
       const QRect rect(x * zoomedTileWidth, y * zoomedTileHeight, zoomedTileWidth, zoomedTileHeight); 
       painter->drawText(rect, QString::fromLatin1("%1, %2").arg(x).arg(y)); 
      } 
     } 
    } 

protected: 
    void wheelEvent(QWheelEvent *event) override { 
     const int oldZoomLevel = mZoomLevel; 
     mZoomLevel = qMax(1, qMin(mZoomLevel + (event->angleDelta().y() > 0 ? 1 : -1), 30)); 

     const QPoint cursorPosRelativeToOffset = event->pos() - mOffset; 

     if (mZoomLevel != oldZoomLevel) { 
      mOffset.rx() -= cursorPosRelativeToOffset.x(); 
      mOffset.ry() -= cursorPosRelativeToOffset.y(); 

      // Attempts based on https://stackoverflow.com/a/14085161/904422 
//   mOffset.setX((event->pos().x() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.x())); 
//   mOffset.setY((event->pos().y() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.y())); 

//   mOffset.setX((cursorPosRelativeToOffset.x() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.x())); 
//   mOffset.setY((cursorPosRelativeToOffset.y() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.y())); 

      update(); 
     } 
    } 

    void keyReleaseEvent(QKeyEvent *event) override { 
     static const int panDistance = 50; 
     switch (event->key()) { 
     case Qt::Key_Left: 
      mOffset.rx() -= panDistance; 
      update(); 
      break; 
     case Qt::Key_Right: 
      mOffset.rx() += panDistance; 
      update(); 
      break; 
     case Qt::Key_Up: 
      mOffset.ry() -= panDistance; 
      update(); 
      break; 
     case Qt::Key_Down: 
      mOffset.ry() += panDistance; 
      update(); 
      break; 
     } 
    } 

private: 
    const int mTileWidth; 
    const int mTileHeight; 
    const int mTilesAcross; 
    const int mTilesDown; 
    QPoint mOffset; 
    int mZoomLevel; 
}; 

int main(int argc, char *argv[]) 
{ 
    QGuiApplication app(argc, argv); 

    qmlRegisterType<Canvas>("App", 1, 0, "Canvas"); 

    QQmlApplicationEngine engine; 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 

    return app.exec(); 
} 

#include "main.moc" 

main.qml:

import QtQuick 2.5 
import QtQuick.Window 2.2 

import App 1.0 as App 

Window { 
    visible: true 
    width: 1200 
    height: 900 
    title: qsTr("Hello World") 

    Shortcut { 
     sequence: "Ctrl+Q" 
     onActivated: Qt.quit() 
    } 

    App.Canvas { 
     focus: true 
     anchors.fill: parent 
    } 
} 

Was mache ich in der wheelEvent() Funktion falsch?

+0

Was erwarten Sie und was sehen Sie?Und warum nicht Qt Graphics Framework? – ilotXXI

+0

@ilotXXI Alle Informationen gibt es in der Frage, wenn Sie sich die Zeit nehmen, es zu lesen. – Mitch

+2

Oh, habe ein GIF gefunden. Versuchen Sie folgendes: 'mOffset = event-> pos() - Gleitkomma (mZoomLevel)/float (oldZoomLevel) * (event-> pos() - mOffset);'. – ilotXXI

Antwort

5

Sie haben ein Rechteck R = [x_0, x_0 + w] x [y_0, y_0 + h] mit absoluten Koordinaten. Wenn Sie es einem Widget (einem anderen Rechteck) zuordnen, wenden Sie eine Transformation T auf einen Bereich W von R an. Diese Transformation ist linear mit Offset:

T(x, y) = (a_x x + b_x, a_y y + b_y).

Werte von a_x, b_x, a_y, b_y berechnet einige einfache Bedingungen zu erfüllen, Sie haben es bereits getan.

Sie haben auch einen Cursor (x_c, y_c) in R. Es sind Koordinaten in W sind T(x_c, y_c). Nun wollen Sie eine andere Transformation anwenden T'\colon R \rightarrow W',

T'(x, y) = (a_x' x + b_x', a_y' y + b_y')

Ändern Skalenkoeffizienten a_x, a_y bekannte a_x', a_y' mit folgenden Bedingung: Sie mit dem Cursor zu Punkt an den gleichen Koordinaten wollen (x_c, y_c) in R. I.e. T'(x_c, y_c) = T(x_c, y_c) - derselbe Punkt in relativen Koordinaten zeigt auf die gleiche Position in absoluten Koordinaten. Wir leiten ein System für unbekannte Offsets b_x', b_y' mit bekannten Restwerten ab. Es gibt

b_z' = z_c (a_z - a_z') + b_z, z = x,y.

Letzte Arbeit (x_c, y_c) von Widget Cursor-Position zu finden ist (x_p, y_p) = T(x_c, y_c):

z_c = (z_p - b_z)/a_z, z = x,y

und es zu ersetzen:

b_z' = z_p - a_z'/a_z (z_p - b_z), \quad z = x,y.

In Ihren Bedingungen es ist

mOffset = event->pos() - float(mZoomLevel)/float(oldZoomLevel) * 
    (event->pos() - mOffset); 
+2

Ich bin ziemlich schrecklich in Mathe, aber das sieht sehr gut für jemanden aus, der nicht ist. : D – Mitch