计算在鼠标光标位置放大的视图偏移 [英] Calculating view offset for zooming in at the position of the mouse cursor

查看:63
本文介绍了计算在鼠标光标位置放大的视图偏移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个画布",用户可以在上面绘制像素等.它运行良好,但我的缩放功能目前使用相同的原点,而不管鼠标的位置如何.我想实现类似于 Google 地图缩放行为的功能:

也就是说,缩放的原点应该始终是鼠标光标的位置.

我目前拥有的并不完全正确...

我的尝试主要是在黑暗中刺伤,但我也尝试使用 这个答案 中的代码而没有成功.

main.cpp:

#include #include 类画布:公共QQuickPaintedItem{Q_OBJECT上市:帆布() :mTileWidth(25),mTileHeight(25),mTilesAcross(10),mTilesDown(10),mOffset(QPoint(400, 400)),mZoomLevel(1){}无效油漆(QPainter *painter)覆盖{画家->翻译(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()));画家->fillRect(0, 0, zoomedMapWidth, zoomedMapHeight, QColor(Qt::gray));for (int y = 0; y  drawText(rect, QString::fromLatin1("%1, %2").arg(x).arg(y));}}}受保护:无效轮事件(QWheelEvent *事件)覆盖{const int oldZoomLevel = mZoomLevel;mZoomLevel = qMax(1, qMin(mZoomLevel + (event->angleDelta().y() > 0 ? 1 : -1), 30));const QPoint cursorPosRelativeToOffset = event->pos() - mOffset;如果(mZoomLevel != oldZoomLevel){mOffset.rx() -= cursorPosRelativeToOffset.x();mOffset.ry() -= cursorPosRelativeToOffset.y();//基于 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()));更新();}}void keyReleaseEvent(QKeyEvent *event) 覆盖 {静态常量 int panDistance = 50;开关(事件->键()){案例 Qt::Key_Left:mOffset.rx() -= panDistance;更新();休息;案例 Qt::Key_Right:mOffset.rx() += panDistance;更新();休息;案例 Qt::Key_Up:mOffset.ry() -= panDistance;更新();休息;案例 Qt::Key_Down:mOffset.ry() += panDistance;更新();休息;}}私人的: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("App", 1, 0, "Canvas");QQmlApplicationEngine 引擎;engine.load(QUrl(QStringLiteral("qrc:/main.qml")));返回 app.exec();}#include "main.moc"

main.qml:

导入QtQuick 2.5导入 QtQuick.Window 2.2将 App 1.0 导入为 App窗户 {可见:真实宽度:1200高度:900标题:qsTr("Hello World")快捷方式{顺序:Ctrl+Q"onActivated: Qt.quit()}应用程序画布{焦点:真实anchors.fill:父级}}

我在 wheelEvent() 函数中做错了什么?

解决方案

你有一个带有绝对坐标的矩形 R = [x_0, x_0 + w] x [y_0, y_0 + h].当您将它映射到一个小部件(另一个矩形)时,您将一些转换 T 应用到 R 的区域 W.此变换与偏移量呈线性关系:

计算a_x, b_x, a_y, b_y 的值满足一些简单的条件,你已经做到了.

您在 R 中还有一个光标 (x_c, y_c).它在 W 中的坐标是 T(x_c, y_c).现在你想应用另一个转换 ,

将比例系数a_x, a_y 更改为已知的a_x', a_y',条件如下:您希望光标指向相同的坐标(x_c,y_c)R 中.IE.T'(x_c, y_c) = T(x_c, y_c)——相对坐标中的同一个点指向绝对坐标中的同一个位置.我们推导出一个未知偏移量b_x', b_y' 的系统,具有已知的剩余值.它给

最后的工作是从widget光标位置找到(x_c, y_c) (x_p, y_p) = T(x_c, y_c):

并替换它:

用你的话说是

mOffset = event->pos() - float(mZoomLevel)/float(oldZoomLevel) *(事件-> pos() - mOffset);

I've got a "canvas" that the user can draw pixels, etc. onto. It works well, but my zoom functionality currently uses the same origin regardless of the position of the mouse. I'd like to implement functionality like that of Google Maps' zoom behaviour:

That is, the zoom's origin should always be the position of the mouse cursor.

What I currently have is not exactly right...

My attempts have mostly been stabs in the dark, but I've also tried using the code from this answer without success.

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

What am I doing wrong in the wheelEvent() function?

解决方案

You have a rectangle R = [x_0, x_0 + w] x [y_0, y_0 + h] with absolute coordinates. When you map it to a widget (another rectangle), you apply some transformation T to an area W of R. This transformation is linear with offset:

Values of a_x, b_x, a_y, b_y are calculated to satisfy some simple conditions, you have already done it.

You also have a cursor (x_c, y_c) in R. It's coordinates in W are T(x_c, y_c). Now you want to apply another transformation ,

changing scale coefficients a_x, a_y to known a_x', a_y' with following condition: you want your cursor to point at the same coordinates (x_c, y_c) in R. I.e. T'(x_c, y_c) = T(x_c, y_c) — the same point in relative coordinates points to the same position in absolute coordinates. We derive a system for unknown offsets b_x', b_y' with known rest values. It gives

Last work is to find (x_c, y_c) from widget cursor position (x_p, y_p) = T(x_c, y_c):

and to substitute it:

In your terms it is

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

这篇关于计算在鼠标光标位置放大的视图偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆