c++ - 在 2D 空间中随意缩放和旋转

标签 c++ mouse rotation scale transformation

我正在尝试使用 QT 在 C++ 中编写一个图形程序,用户可以在其中使用鼠标缩放和旋转对象(就像 inkscape 或 CorelDraw 所做的那样),但是在尝试实现它几个月后我仍然无法实现它.例如,它目前仅通过旋转或缩放来工作,但当用户想要以任意方式变换对象时则不行。 QT 中有一个关于仿射变换的示例,但它非常简单(例如,它使用单个因子而不是 x 和 Y 因子进行缩放),它不提供缩放方向或固定缩放点)所以我不知道如何扩展它或使用它。

这是程序预期的行为方式:

  1. 用户在 Canvas 中放置一个多边形。
  2. 如果用户单击多边形,对象周围将出现一组蓝色框。这些框用于在任何方向(例如,向上、向下、向左、向右等)缩放对象
  3. 如果用户在多边形中再次单击,对象周围将出现一组红色框。这些框用于向任何方向旋转对象。

那么,我怎样才能至少实现以下内容:

  1. 如果用户单击顶部的蓝色框(向上缩放),按住左键并将鼠标向上移动,我怎样才能使多边形向上缩放?我需要比例方向吗?我需要一个通用的定标点吗?如何计算鼠标向上移动时的比例因子,以便“实时”缩放多边形?

以下是我认为可以使其工作的代码:See the code here但它不起作用:-(。如果你能帮助我更好地实现,我将不胜感激。

很抱歉提出许多问题,但我感到非常沮丧。

谢谢, 卡洛斯。

最佳答案

cannot make it work

the result is just wrong

没有很好地描述您的问题。

Basically I don't know what is needed in terms of the concatenation/multiplications of matrices

在对象存储中:
1.职位
2.旋转
3.规模

当需要绘制对象时,按以下顺序进行操作:
1.使用存储的比例因子进行缩放
2.使用存储的角度旋转
3.翻译到位置

给定比例因子 s 和旋转角度 r,围绕任意点 (p.x, p.y) 旋转/缩放对象(点数组,或其他),执行此操作:
1. 将对象翻译成 -p.x, -p.y 。 IE。对每个顶点做 vertex -= p;
2. 缩放对象。对于每个顶点做 vertex *= s
3. 旋转物体。使用角度 r 围绕点零旋转每个顶点。
4. 将对象转换为 p.x、p.y。

另外,我建议您查看 "Affine Transformations" Qt 4 中的演示。要查看演示,请启动 qtdemo,选择“演示 -> 仿射变换”。
考虑聘请几何导师。 “月”太长,无法处理旋转/缩放/平移问题。

But, I have no clue on how to combine of these function in a proper order

如果您围绕同一点旋转和缩放,则操作顺序无关紧要。

--编辑--

实例:

Picture

点表示枢轴、变换开始和变换结束。 线框字母代表原始图像。
红色字母代表“旋转均匀缩放”变换。
绿色字母代表“二维比例”变换。

对于这两种变换,您都需要枢轴、开始拖动形状的点和停止拖动形状的点。

我不会再解释了。

转换测试.pro:

TEMPLATE = app
TARGET = 
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += MainWindow.h
SOURCES += main.cpp MainWindow.cpp

主要.cpp:

#include <QApplication>
#include "MainWindow.h"

int main(int argc, char** argv){
    QApplication app(argc, argv);
    MainWindow window;
    window.show();

    return app.exec();
}

主窗口.h:

#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H

#include <QGLWidget>
class QPaintEvent;

class MainWindow: public QWidget{
Q_OBJECT
public:
    MainWindow(QWidget* parent = 0);
protected slots:
    void updateAngle();
protected:
    void paintEvent(QPaintEvent* ev);
    float angle;
    float distAngle;
};

#endif

主窗口.cpp:

#include "MainWindow.h"
#include <QTimer>
#include <QPainter>
#include <QColor>
#include <QVector2D>
#include <math.h>

static const int timerMsec = 50;
static const float pi = 3.14159265f;

MainWindow::MainWindow(QWidget* parent)
:QWidget(parent), angle(0), distAngle(0){
    QTimer* timer = new QTimer(this);
    timer->start(timerMsec);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    connect(timer, SIGNAL(timeout()), this, SLOT(updateAngle()));
}

float randFloat(){
    return (qrand()&0xFF)/255.0f;
}

float randFloat(float f){
    return randFloat()*f;
}

inline QVector2D perp(const QVector2D v){
    return QVector2D(-v.y(), v.x());
}

void MainWindow::updateAngle(){
    angle = fmod(angle + pi*5.0f/180.0f, pi*2.0f);
    distAngle = fmod(distAngle + pi*1.0f/180.0f, pi*2.0f);
}

QTransform buildRotateScale(QVector2D pivot, QVector2D start, QVector2D end){
    QVector2D startDiff = start - pivot;
    QVector2D endDiff = end - pivot;
    float startLength = startDiff.length();
    float endLength = endDiff.length();
    if (startLength == 0)   
        return QTransform();
    if (endLength == 0) 
        return QTransform();

    float s = endLength/startLength;
    startDiff.normalize();
    endDiff.normalize();

    QVector2D startPerp = perp(startDiff);
    float rotationAngle = acos(QVector2D::dotProduct(startDiff, endDiff))*180.0f/pi;
    if (QVector2D::dotProduct(startPerp, endDiff) < 0)
        rotationAngle = -rotationAngle;

    return QTransform().translate(pivot.x(), pivot.y()).rotate(rotationAngle).scale(s, s).translate(-pivot.x(), -pivot.y());
}

QTransform buildScale(QVector2D pivot, QVector2D start, QVector2D end){
    QVector2D startDiff = start - pivot;
    QVector2D endDiff = end - pivot;
    float startLength = startDiff.length();
    float endLength = endDiff.length();
    if ((startDiff.x() == 0)||(startDiff.y() == 0))
        return QTransform();
    QVector2D s(endDiff.x()/startDiff.x(), endDiff.y()/startDiff.y());

    return QTransform().translate(pivot.x(), pivot.y()).scale(s.x(), s.y()).translate(-pivot.x(), -pivot.y());
}

void MainWindow::paintEvent(QPaintEvent* ev){
    QPainter painter(this);
    QPointF pivot(width()/2, height()/2);
    QPointF transformStart(pivot.x() + 100.0f, pivot.y() - 100.0f);
    float r = sinf(distAngle)*100.0f + 150.0f;
    QPointF transformEnd(pivot.x() + r*cosf(angle), pivot.y() - r*sinf(angle));

    painter.fillRect(this->rect(), QBrush(QColor(Qt::white)));
    QPainterPath path;
    QString str(tr("This is a test!"));
    QFont textFont("Arial", 40);
    QFontMetrics metrics(textFont);
    QRect rect = metrics.boundingRect(str);
    path.addText(QPoint((width()-rect.width())/2, (height()-rect.height())/2), textFont, str);

    painter.setPen(QColor(200, 200, 255));
    painter.drawPath(path);
    painter.setTransform(buildRotateScale(QVector2D(pivot), QVector2D(transformStart), QVector2D(transformEnd)));
    painter.fillPath(path, QBrush(QColor(255, 100, 100)));
    painter.setPen(QColor(100, 255, 100));
    painter.setTransform(buildScale(QVector2D(pivot), QVector2D(transformStart), QVector2D(transformEnd)));
    painter.fillPath(path, QBrush(QColor(100, 255, 100)));
    painter.setTransform(QTransform());

    QPainterPath coords;
    r = 10.0f;
    coords.addEllipse(pivot, r, r);
    coords.addEllipse(transformStart, r, r);
    coords.addEllipse(transformEnd, r, r);
    painter.setPen(QPen(QBrush(Qt::red), 5.0f));
    painter.setBrush(QBrush(QColor(127, 0, 0)));
    painter.setPen(QPen(QBrush(Qt::green), 5.0f));
    painter.drawLine(QLineF(pivot, transformStart));
    painter.setPen(QPen(QBrush(Qt::blue), 5.0f));
    painter.drawLine(QLineF(transformStart, transformEnd));
    painter.setPen(Qt::red);
    painter.drawPath(coords);
    painter.end();
}

关于c++ - 在 2D 空间中随意缩放和旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3521156/

相关文章:

C++ 静态函数复制

c++ - linux中管道之间的通信

Android旋转图像ontouch

android - 可绘制限定符,位图在 HTC with Android 4+ 屏幕旋转后未正确更改

c++ - BGL : bundled object without a default constructor?

c++ - 如何在vc++中拆分字符串?

powershell - 使用 powershell(或其他非外部软件)进行鼠标单击自动化

javascript - Matter.js 鼠标点击主体

keyboard-shortcuts - 使用 Karabiner-Elements 将应用程序键映射到插入符位置的右键单击

matlab - 在 Matlab 中放大和缩小删除