2010-08-19 27 views
0

J'essaye d'écrire un programme graphique en C++ avec QT où les utilisateurs peuvent mettre à l'échelle et faire tourner les objets avec la souris (comme le fait inkscape ou CorelDraw), mais après plusieurs mois Que cela arrive, je ne peux toujours pas le faire fonctionner. Il fonctionne actuellement par exemple en tournant juste ou simplement en mettant à l'échelle, mais pas quand l'utilisateur veut transformer l'objet d'une manière arbitraire. Il y a un exemple de transformation affine dans QT mais il est très simple (par exemple, il utilise un seul facteur non pas les facteurs x et Y), il ne fournit pas de directions d'échelle ou de point d'échelle fixe). ou utilisez-le.Mise à l'échelle et rotation à volonté dans un espace 2D

Voici comment le programme devrait se comporter:

  1. L'utilisateur déposer un polygone dans la toile.
  2. Si l'utilisateur clique sur le polygone, un ensemble de cases bleues apparaît autour de l'objet. Ces cases sont utilisées pour redimensionner l'objet dans n'importe quelle direction (par exemple, haut, bas, gauche, droite, etc.)
  3. Si l'utilisateur clique à nouveau dans le polygone, un ensemble de zones rouges apparaît autour de l'objet. Ces boîtes sont utilisées pour faire pivoter l'objet dans n'importe quelle direction.

Alors, comment puis-je mettre en œuvre au moins les éléments suivants:

  1. Si l'utilisateur clique sur la boîte bleue haut (échelle vers le haut), maintenez le bouton gauche et déplace la souris vers, comment puis-je faire évoluer le polygone vers le haut? Ai-je besoin d'une direction d'échelle? Ai-je besoin d'un point fixe de mise à l'échelle? Comment puis-je calculer les facteurs d'échelle lorsque la souris se déplace vers le haut pour que le polygone soit mis à l'échelle en "temps réel"?

Voici le code qui dans mon point de vue pourrait le faire fonctionner: See the code here Mais cela ne fonctionne pas :-(Si vous pouvez me aider à une meilleure mise en œuvre, je l'apprécierai

Désolé de mettre.. à de nombreuses questions, mais je suis complètement frustré.

Merci, Carlos.

+0

La question n'est pas claire. "Le résultat est juste faux" ne décrit pas votre problème. – SigTerm

Répondre

2

ne peut pas faire fonctionner

le résultat est tout simplement faux

ne décrit pas votre problème très bien.

En fait, je ne sais pas ce qui est nécessaire en termes de concaténation/multiplications de matrices

En magasin d'objets: 1. Position

2. rotation
3. échelle

Lorsque vous devez dessiner un objet, effectuez les opérations dans cet ordre:
1. Échelle utilisant le facteur d'échelle enregistré
2. Faites pivoter à l'aide angle stocké
3. Traduire en position

facteur d'échelle Compte tenu de et l'angle de rotation r, pour faire pivoter/objet d'échelle (réseau de points, ou autre) autour du point arbitraire (px, py), faire :
1. Traduire l'objet en -px, -py. C'est à dire. pour chaque sommet, faire le sommet - = p;
2. Objet d'échelle. Pour chaque sommet, faites le vertex * = s
3. Faites pivoter l'objet. Tourner chaque sommet autour du point zéro en utilisant l'angle r.
4. Traduire l'objet en p.x, p.y.

Je vous recommande également de jeter un oeil à la démo "Affine Transformations" dans Qt 4. Pour voir la démo, lancez qtdemo, sélectionnez "Démonstrations-> Affine Transformations".
Envisagez l'embauche d'un tuteur de géométrie. "Mois" est trop long pour traiter le problème de rotation/échelle/traduction.

Mais, je n'ai pas la moindre idée sur la façon de combiner de ces fonctions dans un ordre approprié

Si vous êtes en rotation et mise à l'échelle autour de même point, l'ordre des opérations n'a pas d'importance.

--EDIT--

Un exemple concret:

Picture

Points indiquent pivot, début de transformation, et à la fin de la transformation. Les lettres filaires représentent l'image originale.
La lettre rouge représente la transformation "rotation et échelle uniforme".
Les lettres vertes représentent la transformation "échelle 2D".

Pour les deux transformer, vous avez besoin d'un pivot, pointez là où vous avez commencé à faire glisser la forme et pointez là où vous avez arrêté de faire glisser la forme.

Je ne l'expliquerai plus jamais.

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

Salut, voici une capture d'écran décrivant le problème: http://www.qlands.com/other_files/scale_error .pdf. Le code correspondant est utilisé ici: http://www.qlands.com/other_files/transform_code.txt Merci – QLands

+0

Salut, vous ont suggéré d'utiliser un facteur d'échelle S, mais comment peut-il travailler avec des facteurs horizontaux et verticaux indépendants et une échelle direction? Merci. – QLands

+0

@qlands: "Bonjour, voici une capture d'écran décrivant le problème:" indique clairement un bug dans votre code. "Le code utilisé est ici" Je suis un peu occupé à le vérifier (ou à écrire une implémentation alternative), mais à mon avis c'est plus compliqué qu'il ne devrait l'être. Vous pouvez simplement créer une fonction qui renvoie QTransform en fonction du pivot, du point de départ et du point de fin d'échelle | rotation. Si vous voulez que quelqu'un vérifie le code, vous devriez le mettre dans votre question, de préférence avec la capture d'écran. – SigTerm

1

Fondamentalement, vous avez un point (ou une série de points) que vous voulez transformer avec deux transformations linéaires, R (rotation) et S (mise à l'échelle). Donc, vous essayez de c alculer quelque chose comme

R(S(x)) 

où x est un point. Si vous représentez ces opérations à l'aide de matrices, puis effectuer des opérations consécutives équivaut à multiplier les matrices, à savoir

R*S*x 

Malheureusement, vous n'avez pas assez d'information donné pour moi d'être plus précis ... vous pouvez poster un code (juste les petites parties pertinentes) montrant ce que vous faites? Qu'entendez-vous par "façon naturelle"? Qu'en est-il de votre résultat "juste faux"?

+0

Salut, le code pertinent est un peu gros pour cette boîte de commentaire. Alors je l'ai posté à: http://www.qlands.com/other_files/transform_code.txt Par nature, je veux dire, plus il doit se comporter comme tout autre logiciel graphique comme inkcape, OO Draw, etc. – QLands

+0

Salut, J'ai aussi posté une capture d'écran décrivant le problème: http://www.qlands.com/other_files/scale_error.pdf – QLands