2010-08-19 2 views
0

Я пытаюсь написать графическую программу на C++ с QT, где пользователи могут масштабировать и поворачивать объекты с помощью мыши (как и inkscape или CorelDraw), однако после многих месяцев пытается это произойдет, я все еще не могу заставить его работать. В настоящее время он работает, например, только путем поворота или просто масштабирования, но не тогда, когда пользователь хочет каким-либо образом преобразовать объект. В QT есть пример об аффинном преобразовании, но он очень прост (например, он масштабируется с использованием одного фактора, а не факторов x и Y), он не обеспечивает направление шкалы или фиксированную точку масштабирования), поэтому я не знаю, как его расширить или использовать его.По желанию масштабирование и вращение в 2D-пространстве

Это как программа, как ожидается, вести себя:

  1. Пользователь уронить многоугольник на холсте.
  2. Если пользователь нажмет на многоугольник, вокруг объекта появится набор синих квадратов. Эти поля используются для масштабирования объекта в любом направлении (например, вверх, вниз, влево, вправо и т. Д.).
  3. Если пользователь снова нажмет на полигоне, вокруг объекта появится набор красных прямоугольников. Эти поля используются для поворота объекта в любом направлении.

Итак, как я могу осуществить по крайней мере, следующее:

  1. Если пользователь нажимает на верхней синей коробке (шкала в направлении вверх), удерживая левую кнопку и перемещает курсор к вверх, как могу ли я сделать полигон масштабируемым в направлении вверх? Нужно ли масштабирование? Нужна ли мне общая неподвижная точка масштабирования? Как я могу рассчитать масштабные коэффициенты, когда мышь движется вверх, чтобы полигон был масштабирован в «реальном времени»?

Вот код, который в моей точке зрения, может заставить его работать: See the code here Но это не работает :-(Если вы можете помочь мне с лучшей реализацией я оценю его

Извините ставить.. на многие вопросы, но я полностью разочарован.

Спасибо, Карлос.

+0

Вопрос неясен. «результат просто неправильный» не описывает вашу проблему. – SigTerm

ответ

2

не может заставить его работать

результат просто неправильно

Не опишете вашу проблему очень хорошо.

В принципе я не знаю, что необходимо с точки зрения конкатенации/умножений матриц

В хранилище объектов:
1. положение
2. вращения
3. шкалы

Если вам необходимо нарисовать объект, выполните операции в следующем порядке:
1. Масштаб с использованием коэффициента масштабирования
2. Поворот с использованием сохраненного угла
3. Перевести в положение

Учитывая масштабный коэффициент с и угол поворота г, вращать/масштаба объекта (массив точек, или любой другой) вокруг произвольной точки (рх, ру), это сделать :
1. Перевести объект на -px, -py. То есть для каждой вершины do вершина - = p;
2. Масштаб объекта. Для каждой вершины do verxx * = s
3. Поверните объект. Поверните каждую вершину вокруг нулевой точки с помощью угла r.
4. Перевести объект на p.x, p.y.

Также я бы рекомендовал посмотреть демо-версию "Affine Transformations" в Qt 4. Чтобы посмотреть демо, запустите qtdemo, выберите «Демонстрации-> Аффинные преобразования».
Рассмотрите возможность найма учителя геометрии. «Месяцы» слишком длинны, чтобы справляться с проблемой поворота/масштабирования/перевода.

Но я понятия не имею, о том, как объединить этих функций в надлежащем порядке

Если вы вращения и масштабирования вокруг той же точки, порядок операций не имеет значения.

--EDIT--

Живой пример:

Picture

Точки указывают на стержень, начало трансформации, и конец преобразования. Буквы каркаса представляют собой исходное изображение.
Красная буква представляет собой «поворот и равномерное масштабирование».
Зеленые буквы представляют собой преобразование «2D-масштаб».

Для обоих трансформаций вам нужна точка поворота, точка, где вы начали перетаскивать фигуру, и укажите, где вы остановили перетаскивание фигуры.

Я больше не буду это объяснять.

transformtest.pro:

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

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

main.cpp:

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

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

    return app.exec(); 
} 

mainwindow.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 

MainWindow.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(); 
} 
+0

Привет, вот скриншот, описывающий проблему: http://www.qlands.com/other_files/scale_error .pdf. Соответствующий код используется здесь: http://www.qlands.com/other_files/transform_code.txt Спасибо – QLands

+0

Привет, вы предложили использовать масштабный коэффициент S, однако как он может работать с независимыми горизонтальными и вертикальными факторами и шкалой направление? Спасибо. – QLands

+0

@qlands: «Привет, вот скриншот, описывающий проблему:« Четко указывает на ошибку в вашем коде. «Соответствующий код используется здесь« Я немного занят, чтобы полностью проверить его (или написать альтернативную реализацию), но, на мой взгляд, это сложнее, чем должно быть. Вы можете просто создать функцию, которая возвращает QTransform на основе точки поворота, начальной точки и конечной точки масштабирования. Если вы хотите, чтобы кто-то проверил код, вы должны поместить его в свой вопрос, желательно вместе со снимком экрана. – SigTerm

1

в принципе, у вас есть точка (или несколько точек), которые вы хотите преобразовать с двумя линейными преобразованиями, R (вращение) и S (масштабирование). Таким образом, вы пытаетесь c что-то вроде

R(S(x)) 

где x - это точка. Если представить эти операции с использованием матриц, то выполнение последовательных операций эквивалентно умножению матриц, т.е.

R*S*x 

К сожалению, вы не дали достаточно информации для меня, чтобы быть более конкретным ... могли бы вы опубликовать код (только небольшие, соответствующие части), показывающие, что вы делаете? Что вы подразумеваете под «естественным путем»? Как насчет вашего результата «просто неправильно»?

+0

Привет, соответствующий код немного большой для этого окна комментариев. Поэтому я разместил его по адресу: http://www.qlands.com/other_files/transform_code.txt Естественно, я имею в виду, что он должен вести себя так же, как любое другое графическое программное обеспечение, такое как inkcape, OO Draw и т. Д. – QLands

+0

Привет, Я также разместил скриншот, описывающий проблему: http://www.qlands.com/other_files/scale_error.pdf – QLands