2016-07-09 2 views
4

나는 픽셀 등을 그릴 수있는 "캔버스"가 있습니다. 잘 작동하지만 내 확대/축소 기능은 현재 마우스의 위치에 관계없이 현재 동일한 원점을 사용합니다. 나는 Google지도 '줌 동작의 같은 기능을 구현하고 싶습니다 :마우스 커서의 위치에서 줌인을위한 뷰 오프셋 계산

입니다

google maps zoom

, 줌의 원점은 항상 마우스 커서의 위치해야한다.

What I currently have

내 시도는 주로 어둠 속에서 찢고 있었다, 그러나 나는 또한 성공하지 this answer의 코드를 사용하여 시도했습니다 ... 정확히 옳지 않다.

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 
    } 
} 

은 무엇 나는 wheelEvent() 기능에 잘못하고 있는가?

+0

무엇을 기대합니까?왜 Qt Graphics 프레임 워크가 아닌가? – ilotXXI

+0

@ilotXXI 당신이 그것을 읽을 시간이 있다면 모든 정보가 질문에 있습니다. – Mitch

+2

아, GIF를 찾았습니다. 이것을보십시오 :'mOffset = event-> pos() - float (mZoomLevel)/float (oldZoomLevel) * (event-> pos() - mOffset);'. – ilotXXI

답변

5

절대 좌표가있는 직사각형이 R = [x_0, x_0 + w] x [y_0, y_0 + h]입니다. 위젯 (다른 직사각형)에 매핑 할 때 W의 변 환을 R에 적용합니다. 이 변환은 오프셋 선형 : a_x, b_x, a_y, b_y

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

값은 몇 가지 간단한 조건을 만족하는 계산, 당신은 이미 완료했다.

R에는 (x_c, y_c)의 커서가 있습니다. W의 좌표는 T(x_c, y_c)입니다. 지금 당신은 다음과 같은 조건과 다른 변환 T'\colon R \rightarrow W' 알려진 a_x', a_y'

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

변경 규모 계수 a_x, a_y을 적용 할 : 당신이 R에서 동일한 좌표 (x_c, y_c)에 지점에 커서를 원한다. 나는. T'(x_c, y_c) = T(x_c, y_c) - 상대 좌표에서 같은 점은 절대 좌표에서 같은 위치를 가리 킵니다. 알려진 나머지 값을 가진 알려지지 않은 오프셋 b_x', b_y'에 대한 시스템을 유도합니다.

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

그것을 대체 할 : 그것은

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

마지막 작품이 위젯 커서 위치 (x_p, y_p) = T(x_c, y_c)에서 (x_c, y_c)을 찾을 수 있습니다 제공합니다 귀하의 측면에서

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

그것을

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

나는 수학에있어서 꽤 끔찍하다. 그러나 이것은보기 좋지 않은 사람을 위해 매우 잘 보인다. :디 – Mitch

관련 문제