Маштабирование. Рисование в Qt5 - трансформации. Часть 3 - АлтунинВВ.Блог - всё об IT-технологиях!
Вторник, 27 октября 2020 17:48

Маштабирование. Рисование в Qt5 - трансформации. Часть 3

Россия

В прошлой части – ссылка мы рассмотрели вращение. Сегодня мы рассмотрим новый тип трансформации – масштабирование.

Мы продолжим работать с проектом с прошлой части, его исходники вы можете найти на GitHub.

Для начала, обеспечим себя чистым рабочим столом.

В методе

void MainWindow::paintEvent(QPaintEvent *)

закомментируем

drawChildPicRel();

В настройках формы главного окна изменим его размер до 400х400

Запустим, получим чистое окно:

2020-10-26_14-38-22.png

Мы будем рисовать облака разного размера. Облако будет состоять из кругов.

Добавим метод для отрисовки облака.

void MainWindow::drawCloud()
{
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    painter->translate(QPoint(150,150));
    painter->setBrush(Qt::white);
    //
    apen = QPen(QColor("white"));
    apen.setWidth(3);
    painter->setPen(apen);
    painter->drawArc(0,0,30,30,360*16,360*16);
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(20,-10));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(20,0));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(20,10));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(-20,10));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(-20,0));
    painter->drawEllipse(0, 0, 30,30);
}

Запустим, у нас получится:

2020-10-26_14-51-26.png

Закомментируем строку

painter→setBrush(Qt::white);

Запустим еще раз:

2020-10-26_14-52-25.png

Как видите, облако состоим из кругов, которые накладываются друг на друга, а так как они одинакового цвета, то переходов между ними не видно.

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

Наше облако выглядит неплохо, но несколько «синтетически». Давайте сделаем его менее симметричным.

Изменим код таким образом:

void MainWindow::drawCloud()
{
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    painter->translate(QPoint(150,150));
    painter->setBrush(Qt::white);
    
    apen = QPen(QColor("white"));
    apen.setWidth(3);
    painter->setPen(apen);
    painter->drawEllipse(0, 0, 40,40);
    painter->translate(QPoint(20,-10));
    painter->drawEllipse(0, 0, 35,35);
    painter->translate(QPoint(20,0));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(20,10));
    painter->drawEllipse(0, 0, 35,35);
    painter->translate(QPoint(-20,10));
    painter->drawEllipse(0, 0, 30,30);
    painter->translate(QPoint(-20,0));
    painter->drawEllipse(0, 0, 35,35);
}

У нас получиться:

2020-10-26_17-18-54.png

или

2020-10-26_17-19-13.png

Намного лучше, но нам нужны облака разных типов. Для этого усовершенствуем наш код.

void MainWindow::drawCloud(int seed)
{
    QRandomGenerator prng(seed);
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    painter->translate(QPoint(150,150));
    painter->setBrush(Qt::white);
    //
    apen = QPen(QColor("white"));
    apen.setWidth(3);
    painter->setPen(apen);
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
    painter->translate(QPoint(20,-10));
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
    painter->translate(QPoint(20,0));
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
    painter->translate(QPoint(20,10));
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
    painter->translate(QPoint(-20,10));
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
    painter->translate(QPoint(-20,0));
    painter->drawEllipse(0, 0, 30+prng.bounded(10),30+prng.bounded(10));
}

Изменим вызов нашего метода на

drawCloud(123);

Запустим, получим:

2020-10-26_17-29-16.png

или

2020-10-26_17-29-41.png

Давайте разберемся в коде.

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

Это позволяет каждый раз получать от генератора одну и туже последовательность чисел, что очень удобно, так как позволяет установить строгую зависимость результата генерации от некого числа!

Далее при отрисовке каждого эллипса используется вызов методакоторый генерирует случайное число в диапазоне от 0 до 10.

prng.bounded(10)

Таким образом у нас получаются отлично выглядящие облака!

Нарисуем несколько облаков.

void MainWindow::drawClouds()
{
    painter->translate(QPoint(0,20));
    painter->save();
    painter->translate(QPoint(20,20));
    drawCloud(121);
    painter->restore();
    painter->save();
    painter->translate(QPoint(120,40));
    drawCloud(122);
    painter->restore();
    painter->save();
    painter->translate(QPoint(220,70));
    drawCloud(123);
    painter->restore();
    painter->save();
    painter->translate(QPoint(200,2));
    drawCloud(124);
    painter->restore();
}

Запустим:

2020-10-26_17-49-54.png

Красивые облака это, конечно, хорошо, но облака бывают разными по размеру, давайте нарисуем несколько облаков разного размера, но, прежде чем продолжить, обновим код рисования домика, радуги и солнышка.

void MainWindow::drawChildPicRel()
{
    painter->translate(QPoint(50,70));
    painter->save();
    QColor linesColor(0, 0, 255, 255);
    QPen apen = QPen(linesColor);
    apen.setWidth(3);
    painter->setPen(apen);
    painter->translate(QPoint(150,220));
    //Квадрат - дом
    painter->drawRect(-50,-50,100,100);
    //Квадрат - окно
    painter->drawRect(-30,-30,60,60);
    //Полигон - крыша и дымоход
    static const QPointF points[7] = {
        QPointF(-50.0, -50.0),
        QPointF(0.0, -100.0),
        QPointF(20.0, -80.0),
        QPointF(20.0, -110.0),
        QPointF(30.0, -110.0),
        QPointF(30.0, -70.0),
        QPointF(50.0, -50.0)
    };
    painter->drawConvexPolygon(points, 7);
    //Линия - крышка дымохода
    painter->drawLine(QLine(15,-110,35,-110));
    painter->drawLine(QLine(15,-110,25,-120));
    painter->drawLine(QLine(25,-120,35,-110));
    painter->restore();
    painter->translate(QPoint(50,50));
    int rainbowWidth = 200;
    int rainbowHeight = 200;
    int rainbowInRad = 30 * 16;
    int rainbowOutRad = 120 * 16;
    int rainbowArcHeight = 4;
    //Дуга - Радуга
    apen = QPen(QColor("red"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,0,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen.setWidth(rainbowArcHeight);
    apen = QPen(QColor("orange"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen = QPen(QColor("yellow"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight*2,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen = QPen(QColor("green"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight*3,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen = QPen(QColor("lightblue"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight*4,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen = QPen(QColor("blue"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight*5,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    apen = QPen(QColor("violet"));
    apen.setWidth(rainbowArcHeight);
    painter->setPen(apen);
    painter->drawArc(0,rainbowArcHeight*6,rainbowWidth,rainbowHeight,rainbowInRad,rainbowOutRad);
    painter->restore();
    painter->translate(QPoint(220,-90));
    drawSun();
}

Обратите внимание, я использовал методы save() и restore().

Запустим:

2020-10-26_17-53-18.png

Выглядит лучше, а теперь изменим метод отрисовки облаков и изменим их размер:

void MainWindow::drawClouds()
{
    painter->translate(QPoint(0,20));
    painter->save();
    painter->translate(QPoint(20,20));
    painter->scale(0.8,0.8);
    drawCloud(121);
    painter->restore();
    painter->save();
    painter->translate(QPoint(100,60));
    painter->scale(1.2,1.2);
    drawCloud(122);
    painter->restore();
    painter->save();
    painter->translate(QPoint(240,100));
    painter->scale(0.6,0.6);
    drawCloud(123);
    painter->restore();
    painter->save();
    painter->translate(QPoint(225,2));
    painter->scale(0.9,0.9);
    drawCloud(124);
    painter->restore();
}

Для изменения размера мы использовали метод painter->scale

Данный метод принимает числа типа real, таким образом, что, например, обычный размер будет 1,1. 50% меньше будет 0.5,0.5. 150% соответственно 1.5,1.5 и так далее.

Мы получим картинку.

2020-10-26_18-03-18.png

 Влияние масштабирования на трансформацию

Обратите внимание, масштабирование влияет на трансляцию, которая будет производиться после вызова метода scale()

Рассмотрим это на примере, нарисуем круг по центру экрана.

painter->translate(QPoint(200,200));
painter->setBrush(Qt::red);
painter->drawEllipse(-25, -25, 50,50);

Получим результат:

2020-10-27_17-07-31.png 

Изменим код:

painter->scale(0.5,0.5);
painter->translate(QPoint(200,200));
painter->setBrush(Qt::red);
painter->drawEllipse(-25, -25, 50,50);

Получим результат:

2020-10-27_17-08-38.png

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

Заключение

Сегодня мы рассмотрели последнюю трансформацию — масштабирование.

Добавили метод для рисования облака. Модернизировали его, таким образом, чтобы он позволял генерировать облака разных размеров.

Добавили зерно (seed) для метода, чтобы гарантировать одинаковый результат при каждом запуске программы.

Обновили метод отрисовки дома, с использованием методов save() и restore() и добавили облака.

Рассмотрели влияние масштабирования на трансляцию в QPainter.

Прочитано 155 раз Последнее изменение Вторник, 27 октября 2020 18:08