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-03 02:23
0

a dlaczego akurat "-1" ??

Pozostało 580 znaków

2019-04-03 02:28
1

Jeśli Cię to zadowoli, to możesz dać nawet -54334 i w warunku >= 0 zamiast != -1.

Po prostu najmniejszy możliwy indeks tablicy to 0, więc liczba ujemna jest ok dla zmiennej przechowującej indeks, żeby ostrzec inną część programu, by nic nie robić z tą tablicą.

edytowany 1x, ostatnio: Spine, 2019-04-03 02:29

Pozostało 580 znaków

2019-04-04 00:58
2

Pomijając ręczne licznie punktów beizera to może to wyglądać tak:

#ifndef MANBEZEIRWIDGET_H
#define MANBEZEIRWIDGET_H

#include <QWidget>

class ManBezeirWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ManBezeirWidget(QWidget *parent = nullptr);

    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);

    void paintEvent(QPaintEvent *event);

signals:

public slots:

private:
    void paintBezeirCurve(QPainter *painter);
    void paintControlPoints(QPainter *painter);
    void paintHelperLines(QPainter *painter);
    bool activePointHit(QPoint p);
    int findPoint(QPoint p) const;
private:
    QVector<QPoint> mPoints;
    int mActivePoint = -1;
};

#endif // MANBEZEIRWIDGET_H

cpp

#include "manbezeirwidget.h"
#include <QPainter>
#include <QMouseEvent>

namespace {
const int PointVisualRadius = 3;
const int PointMouseRadius = 5;
}

ManBezeirWidget::ManBezeirWidget(QWidget *parent)
    : QWidget(parent)
    , mPoints{ { 10, 10 }, { 200, 10 }, { 200, 200 }, { 10, 200 } }
{
}

void ManBezeirWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton) {
        QWidget::mousePressEvent(event);
        return;
    }
    auto p = event->pos();
    if (activePointHit(p)) {
        event->accept();
        return;
    }
    mActivePoint = findPoint(p);
}

void ManBezeirWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if (mActivePoint < 0) return;
    mPoints[mActivePoint] = event->pos();
    update();
}

void ManBezeirWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (mActivePoint < 0) return;
    if (!event->buttons().testFlag(Qt::LeftButton)) return;
    mPoints[mActivePoint] = event->pos();
    update();
}

void ManBezeirWidget::paintEvent(QPaintEvent *)
{
    QPainter painter { this };
    paintHelperLines(&painter);
    paintBezeirCurve(&painter);
    paintControlPoints(&painter);
}

void ManBezeirWidget::paintBezeirCurve(QPainter *painter)
{
    QPainterPath path;
    path.moveTo(mPoints.front());
    path.cubicTo(mPoints[1], mPoints[2], mPoints[3]);
    painter->setBrush({});
    painter->setPen(Qt::green);
    painter->drawPath(path);
}

void ManBezeirWidget::paintControlPoints(QPainter *painter)
{
    for (int i = 0; i < mPoints.size(); ++i) {
        if (i == mActivePoint) {
            painter->setPen(Qt::red);
            painter->setBrush(Qt::magenta);
        } else {
            painter->setPen(Qt::darkCyan);
            painter->setBrush(Qt::darkRed);
        }
        painter->drawEllipse(mPoints[i], PointVisualRadius, PointVisualRadius);
    }
}

void ManBezeirWidget::paintHelperLines(QPainter *painter)
{
    QPen pen{Qt::gray, 1, Qt::DashLine};
    painter->setPen(pen);
    QPainterPath path;
    path.moveTo(mPoints.front());
    for (int i = 1; i < mPoints.size(); ++i) {
        path.lineTo(mPoints[i]);
    }
    painter->drawPath(path);
}

bool ManBezeirWidget::activePointHit(QPoint p)
{
    if (mActivePoint >= 0) {
        auto diff = mPoints[mActivePoint] - p;
        if (QPoint::dotProduct(diff, diff) <= PointMouseRadius*PointMouseRadius) {
            return true;
        }
    }
    return false;
}

int ManBezeirWidget::findPoint(QPoint p) const
{
    for (int i = 0; i < mPoints.size(); ++i) {
        auto diff = mPoints[i] - p;
        if (QPoint::dotProduct(diff, diff) <= PointMouseRadius*PointMouseRadius) {
            return i;
        }
    }
    return -1;
}

MainWindow:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setCentralWidget(new ManBezeirWidget(this));
}

screenshot-20190404005734.png


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.

Pozostało 580 znaków

2019-04-15 17:27
0

Powracam do tematu gdyz dla mnie jeszcze nie do konca jest wszystko jasne mianowicie
Chciałbym zrozumiec jak działa przesuwanie a nastepnie rysowanie krzywej, probóję to swoimi słowami opisac ale tak srednio jestem pewny tego co piszę, proszę o pomoc..

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();
}

wchodzę do funkcji gdzie wywoływana jest metoda przemieszczania myszy
gdy nacisnę na dany kwadracik i przesunę go (bez puszczania lewego przycisku myszy) do tablicy wektor zostaja przypisane współrzedne puszczenia myszy?
No i gdy tych kwadracikow jest wiecej niz 4

 for(int i=3; i<wektor.size(); i+=3){
            rysujBeziera(wektor[i-3],wektor[i-2],wektor[i-1], wektor[i]);

tego nie rozumiem.. Prosiłbym o wytłumaczenie funckcji

Pozostało 580 znaków

2019-04-16 15:35
1

Napisałem ci, że ten kod ma błędy techniczne!
Dałem przykład jak to powinno wyglądać:

  • akcja myszki -> aktualizuj dane poproś o odświeżenie ekranu (update();)
  • odświeżenie ekranu (paintEvent) -. narysuj wszystko korzystając z bieżących danych

A ten kod co pokazałeś rysuje po bitmapie, a potem w paintEvent rysowana jest ta bitmapa.

Krzywe Beziera można łączyć i to właśnie robi ta pętla. Co trzeci punkt jest wspólny dla sąsiednich segmentów krzywej. Pierwszy segment używa punktów kontrolnych 0 1 2 3 następny segment 3 4 5 6, następny 6 7 8 9 itd.
Im więcej masz punktów, tym więcej segmentów qubic Beizer jest rysowanych.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22, 2019-04-16 15:47

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