2010-08-19 8 views
0

나는 마우스로 객체를 스케일링하고 회전시킬 수있는 QT를 사용하여 C++에서 그래픽 프로그램을 작성하려고합니다. (예 : inkscape 또는 CorelDraw가 그렇듯이) 그러나 여러 달 후에 그것은 여전히 ​​작동하게 만들 수 없습니다. 현재는 예를 들어 회전 또는 크기 조정만으로 작동하지만 사용자가 임의의 방식으로 객체를 변형하려는 경우에는 사용할 수 없습니다. 아핀 변환에 대한 QT의 예제가 있지만 매우 간단합니다 (예 : x 및 Y 요소가 아닌 단일 요소를 사용하여 크기 조정). 확장 방법이나 고정 된 조정 점을 제공하지 않습니다. 따라서 확장 방법을 모르겠습니다. 또는 그것을 사용하십시오.2 차원 공간에서 스케일링 및 회전 예정

이 프로그램이 동작 할 것으로 예상된다 방법은 다음과 같습니다

  1. 사용자는 캔버스에 다각형을 놓습니다.
  2. 사용자가 다각형을 클릭하면 파란색 상자 집합이 개체 주위에 나타납니다. 이 상자는 임의의 방향 (예 : 위, 아래, 왼쪽, 오른쪽 등)으로 오브젝트를 스케일하는 데 사용됩니다.
  3. 사용자가 다각형에서 다시 클릭하면 오브젝트 주위에 빨간색 상자 세트가 나타납니다. 이 상자는 어떤 방향으로도 물체를 회전시키는 데 사용됩니다. 상단 파란색 상자에 사용자가 클릭 (최대 향한 규모), 왼쪽 버튼을 누른 채 위로 방향으로 마우스를 이동

    1. , 어떻게 : 그래서

    , 어떻게 최소한 다음 구현할 수 있습니다 다각형을 위로 향하게 할 수 있습니까? 규모의 방향이 필요합니까? 일반적인 고정 소수점 조정이 필요합니까? 마우스를 위로 움직이면 어떻게 스케일 인자를 계산할 수 있습니까? 그래서 폴리곤은 "실시간"으로 크기가 조정됩니까? 여기

내 관점에서 작동 할 수있는 코드 : See the code here를하지만 내가 그것을 감사합니다 더 나은 구현 나를 도울 수 있다면 그것은 :-(작동하지 않습니다 넣어 죄송합니다

.. 그러나 많은 질문에 나는 완전히 좌절입니다.

감사합니다, 카를로스. 기본적으로

+0

질문이 명확하지 않습니다. "결과가 잘못되었습니다"문제를 설명하지 않습니다. – SigTerm

답변

2

는 결과가

아주 잘 당신의 문제를 설명하지 않습니다 단지 잘못

작동 할 수 없습니다.

기본적으로 나는 연결/객체 저장소에서 행렬

의 곱셈의 관점에서 필요한 것을 모르는 :
1. 위치
2 회전
3. 규모

개체를 그리려면 다음 순서로 작업을 수행하십시오.
1. 저장 축척 계수
을 사용하여 축척 저장 각도
3. 사용 2 회전

감안할 때 규모 요인의 회전 각도 R, 임의의 점 (PX, PY)의 주위에/규모의 오브젝트 (포인트 배열, 또는 무엇이든)을 회전 수행이 위치를 번역 :
1. 개체를 -px, -py로 변환합니다. 나는. 모든 정점에 대해 정점 do - = p;
2. 개체 크기 조정. 모든 정점에 대해 정점 * = s를 수행합니다.
3. 개체를 회전합니다. 각도 r을 사용하여 점 0 주위의 모든 정점을 회전합니다.
4. 개체를 p.x, p.y로 변환합니다.

또한 Qt 4에서 "Affine Transformations" 데모를 살펴 보는 것이 좋습니다. 데모를 보려면 qtdemo를 실행하고 "Demonstrations-> Affine Transformations"를 선택하십시오.
기하학 교사를 고용하는 것이 좋습니다. '개월'이 너무 길어서 회전/크기 조정/번역 문제를 처리 할 수 ​​없습니다.

는하지만, 내가 올바른 순서

당신이 회전과 같은 점을 중심으로 확장하는 경우에 이러한 기능의 결합 방법에 대한 단서가 없다, 작업의 순서는 중요하지 않습니다.

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