qt rysowanie krzywej beziera - problem ze zrozumieniem przesuwania pikseli

Odpowiedz Nowy wątek
2019-04-02 22:58
0

Na zajeciach z programowania przerabialismy kod z rysowania krzywej beziera niestety mnie na tych zajeciach nie było i staram się nadrobic zaleglosci niestety sam nie potrafie sobie poradzic a przegladajac internet nie potrafie znalezc satysfakcjonujacych mnie odpowiedzi. Głownie chodzi mi o dwie funkcje mianowicie:

void MyWindow::mousePressEvent(QMouseEvent *event)
void MyWindow::mouseMoveEvent(QMouseEvent *event)

Czy ktos byłby chętny pomoc w zrozumieniu kodu ?


#include "mywindow.h"
#include "ui_mywindow.h"
#include <qmath.h>

MyWindow::MyWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MyWindow)
{
    ui->setupUi(this);

    szer = ui->rysujFrame->width();
    wys = ui->rysujFrame->height();
    poczX = ui->rysujFrame->x();
    poczY = ui->rysujFrame->y();

    img = new QImage(szer,wys,QImage::Format_RGB32);

    punktprzesun = -1;
}

MyWindow::~MyWindow()
{
    delete ui;
}

void MyWindow::on_exitButton_clicked()
{
    qApp->quit();
}

void MyWindow::paintEvent(QPaintEvent*)
{
    QPainter p(this);

    p.drawImage(poczX,poczY,*img);

    for(int i=0; i<wektor.size(); i++){
        p.drawImage(wektor[i].x(),wektor[i].y(),QImage(15,15,QImage::Format_RGB32));
    }
}

void MyWindow::on_cleanButton_clicked()
{
    czysc();
    update();
    wektor.resize(0);
    licz_klikniecia = 0;
}

void MyWindow::czysc()
{
    unsigned char *ptr;

    ptr = img->bits();

    int i,j;

    for(i=0; i<wys; i++)
    {
        for(j=0; j<szer; j++)
        {
            ptr[szer*4*i + 4*j]=255;
            ptr[szer*4*i + 4*j + 1] = 255;
            ptr[szer*4*i + 4*j + 2] = 255;
        }
    }
}

void MyWindow::zapalPiksel(int x, int y)
{
    unsigned char *ptr;
    ptr = img->bits();

    if ((x>=0)&&(y>=0)&&(x<szer)&&(y<wys))
    {
        ptr[szer*4*y + 4*x] = 0;
        ptr[szer*4*y + 4*x + 1] = 0;
        ptr[szer*4*y + 4*x + 2] = 0;
    }

    update();
}

void MyWindow::rysujBeziera(QPoint p0, QPoint p1, QPoint p2, QPoint p3){
    QPoint punkt;

    for(double t = 0.0; t<=1.0; t+=0.001){
        punkt = pow((1-t),3) * p0 + 3 * pow((1-t),2) * t * p1 + 3 * (1-t) * pow(t,2) * p2 + pow(t,3) * p3;
        zapalPiksel(punkt.x(), punkt.y());
    }
}

void MyWindow::mousePressEvent(QMouseEvent *event)
{
    x0 = event->x();
    y0 = event->y();
    x0 -= poczX;
    y0 -= poczY;

    if(event->buttons() == Qt::RightButton){
        QPoint punkt;

        punkt.setX(x0);
        punkt.setY(y0);

        wektor.push_back(punkt);
        licz_klikniecia++;

        if(licz_klikniecia==4){
            rysujBeziera(wektor[0],wektor[1],wektor[2], wektor[3]);
        }
        if(licz_klikniecia > 4 && ((licz_klikniecia-1)%3)==0){
            rysujBeziera(wektor[licz_klikniecia-4],wektor[licz_klikniecia-3],wektor[licz_klikniecia-2], wektor[licz_klikniecia-1]);
        }
    }
    else if(event->buttons() == Qt::LeftButton){
        for(int i=0; i<wektor.size(); i++){
            if(abs(x0 - wektor[i].x()) < 15 && abs(y0 - wektor[i].y()) < 15){
                punktprzesun = i;
            }
        }
    }

    qDebug() << wektor;
    update();
}

void MyWindow::mouseMoveEvent(QMouseEvent *event)
{
    x0 = event->x();
    y0 = event->y();
    x0 -= poczX;
    y0 -= poczY;
    czysc();
    if(punktprzesun != -1){
        wektor[punktprzesun] = QPoint(x0, y0);
    }
    if(wektor.size()>=4){
        for(int i=3; i<wektor.size(); i+=3){
            rysujBeziera(wektor[i-3],wektor[i-2],wektor[i-1], wektor[i]);
        }
    }
    update();
}

void MyWindow::mouseReleaseEvent(QMouseEvent *event)
{
    punktprzesun = -1;
}

void MyWindow::rysujOdcinek(int x0, int y0, int x1, int y1){
    float a, b;

    a = (float)(y1 - y0)/(x1 -x0);
    b = (float)(y0 - a * x0);
    if(x0 == x1)
    {
        if(y0>y1){
            int temp;
            temp = y0;
            y0 = y1;
            y1 = temp;

        }

        for(int i = y0; i <= y1; i++){
            zapalPiksel(x0,i);
        }
    }

    else if(x0 < x1)
    {
        if(abs(a) < 1)
        {
            for(int x = x0; x <=x1; x++)
            {
                int y = a * x + b;
                zapalPiksel(x,y);
            }
        }
        else if(abs(a) >= 1 && y0 < y1 )
        {
            for(int y = y0; y <=y1; y++)
            {
                int x = (y - b)/a;
                zapalPiksel(x,y);
            }
        }
        else
        {
            for(int y = y1; y <=y0; y++)
            {
                int x = (y - b)/a;
                zapalPiksel(x,y);
            }
        }
    }
    else
    {
        if(abs(a) < 1)
        {
            for(int x = x1; x <=x0; x++)
            {
                int y = a * x + b;
                zapalPiksel(x,y);
            }
        }
        else if(abs(a) >= 1 && y1 < y0)
        {
            for(int y = y1; y <=y0; y++)
            {
                int x = (y - b)/a;
                zapalPiksel(x,y);
            }
        }
        else
        {
            for(int y = y0; y <=y1; y++)
            {
                int x = (y - b)/a;
                zapalPiksel(x,y);
            }
        }
    }
}

Pozostało 580 znaków

2019-04-02 23:18

Ogarniasz coś z obsługi zdarzeń?

No więc tutaj masz metodę void MyWindow::mousePressEvent(QMouseEvent *event), do której przekazujesz zdarzenie kliknięcia myszą. Przypisujesz do x0 i y0 współrzędne tego zdarzenia, po czym odejmujesz współrzędne początku okna, w którym będziesz rysować krzywą - da nam to współrzędne względem owego okna.
Następnie tworzysz zmienną, która przechowa Ci te wspołrzędne i wpisujesz je do tablicy. Zwiększasz również licznik kliknięć o 4. Do narysowania krzywej Beziera potrzebujesz 4 punktów, więc kiedy licznik kliknięć osiągnie 4 bądź wielokrotność 4 (tutaj są to 2 osobne warunki), to wywołuje metodę rysujBeziera z czterema ostatnimi elementami tablicy jako parametrami (a każdy z tych parametrów to punkt o określonych współrzędnych).

Pozostało 580 znaków

2019-04-02 23:37
0

dziękuję za pomoc wiele mi to rozjasniło :) , rozumiesz też moze ten fragment kodu?

 else if(event->buttons() == Qt::LeftButton){
        for(int i=0; i<wektor.size(); i++){
            if(abs(x0 - wektor[i].x()) < 15 && abs(y0 - wektor[i].y()) < 15){
                punktprzesun = i;
            }

Pozostało 580 znaków

2019-04-03 00:21
0
Crazy_Rockman napisał(a):

Do narysowania krzywej Beziera potrzebujesz 4 punktów.

A nie wystarczą trzy?

A quadratic Bézier curve is the path traced by the function B(t), given points P0, P1, and P2.

https://en.wikipedia.org/wiki[...]#Quadratic_B%C3%A9zier_curves

edytowany 3x, ostatnio: Spine, 2019-04-03 00:26

Pozostało 580 znaków

2019-04-03 00:25
0

Aby narysować pierwszą krzywą potrzebne są 4 punkty gdy krzywa jest juz narysowana "wyklikuje" 3 punkty i dorysowuje się kolejna krzywa, "Jak go usuniesz, to jak to wpłynie na działanie aplikacji?"
nie mogę przesuwać punktów

edytowany 1x, ostatnio: dcielak, 2019-04-03 00:27

Pozostało 580 znaków

2019-04-03 00:27
1
dcielak napisał(a):

rozumiesz też moze ten fragment kodu?

Sprawdzasz, który kwadracik został kliknięty i zapamiętujesz jego indeks.
Żeby przy ruchu myszką, program wiedział, który punkt przesuwasz.

edytowany 4x, ostatnio: Spine, 2019-04-03 00:29

Pozostało 580 znaków

2019-04-03 00:36
0

dziękuję ideę rozumiem, ale i tak mam problem ze zrozumieniem tego warunku " if(abs(x0 - wektor[i].x()) < 15 && abs(y0 - wektor[i].y()) < 15)" tu chodzi o wielkosc klikniecia tego kwadracika?

"

Pozostało 580 znaków

2019-04-03 01:10
1

Tak.
Wartość bezwzględna jest zastosowana po to, żeby nie pisać podwójnych warunków w stylu: (x0 - wektor[i].x() > -15) && (x0 - wektor[i].x() < 15).

Pozycja x kliknięcia nie może być bardziej odległa niż 15 w lewo lub prawo, względem sprawdzanego punktu wektor[i].
To samo dla y. I to sprawia, że mamy klikalny kwadracik wokół punktu. Inaczej trzeba by klikać z dokładnością co do piksela ;)

edytowany 5x, ostatnio: Spine, 2019-04-03 01:13

Pozostało 580 znaków

2019-04-03 01:36
3

offtopic:
Jak widzę paintEvent i/lub mousePressEvent w klasie dziedziczącej po QMainWindow to mi się płakać chce.
QMainWindow pełni rolę kontrolera. Ma siebie wbudowane, obsługę menu, status bar, dokowania itp.
Te cuda z rysowaniem powinno się robić w klasie dziedziczącej po QWidget, której instancja jest ustawiona jako centralWidget w pochodnej QMainWindow.
Tak to zaprojektowali ludzie od Qt-ka.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 1x, ostatnio: MarekR22, 2019-04-03 01:37
ja się tylko dostosowywuje do zalecen prowadzącego, sam nie rozumiem dlaczego takimi metodami to robimy - dcielak 2019-04-03 02:00
zadaję sobie z tego sprawę. Napisałem to tylko po to byś był świadomy, że ten kod ma pewne wady. Zapewne napisałem więcej kodu w Qt niż autor tego kodu i to mimo tego, że stanowi to mniej niż 20% mojego doświadczenia. - MarekR22 2019-04-03 10:52
w sumie ten kod ma więcej błędów technicznych. Dziś nie mam czasu, może jutro wieczorem napiszę to tak jak to powinno wyglądać (jeśli nie zapomnę). - MarekR22 2019-04-03 11:00

Pozostało 580 znaków

2019-04-03 02:03
0

dziękuję za odpowiedz, pomógłbyś mi jeszcze w zrozumieniu
void MyWindow::mouseMoveEvent(QMouseEvent *event) ?
I mam dodatkowe pytanie dlaczego punktprzesun jest ustawiony na -1 ? Wiesz moze?

void MyWindow::mouseMoveEvent(QMouseEvent *event)
{
    x0 = event->x();
    y0 = event->y();
    x0 -= poczX;
    y0 -= poczY;
    czysc();
    if(punktprzesun != -1){
        wektor[punktprzesun] = QPoint(x0, y0);
    }
    if(wektor.size()>=4){
        for(int i=3; i<wektor.size(); i+=3){
            rysujBeziera(wektor[i-3],wektor[i-2],wektor[i-1], wektor[i]);
        }
    }
    update();
}

void MyWindow::mouseReleaseEvent(QMouseEvent *event)
{
    punktprzesun = -1;
}

Pozostało 580 znaków

2019-04-03 02:20
0

Jest -1, bo po puszczeniu punktu, nie chcemy, żeby myszka już cokolwiek przestawiała...

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0