Drawing using GDI and QT

Posted by lmaster on Tue, 25 Jan 2022 15:35:42 +0100

Basic geometric drawing steps:

  • Create brush: default color BLACK

           CreatePen(...)

  • Create brush: the default brush color is NULL

         CreateSolidBrush(...)

         CreateHatchBrush(...) Shadow brush

         CreatePatternBrush(...) Not commonly used

  • Pick up the brush: SelectObject(...)
  • Draw lines:
    • LineTo(...) The parameter has only the end point. The current point is (0, 0) by default. After drawing, the end point becomes the current point
    • MoveToEx(...) Moves the current point of the brush
  • Draw a Rectangle(...)
  • Delete brush: DeleteObject(...)

Game random number system

Generally, computers can only generate pseudo-random numbers: the process of generating random numbers by computers is a series of numbers calculated by a recursive formula based on a number. However, when the series of numbers are large, they conform to the normal distribution, which is equivalent to generating random numbers

  • srand((unsigned)time(NULL)) initializes random seeds with system time
  • rand(...) Generate random number

Simple algorithms for several random numbers

See it when you need it

Algorithm + drawing

Design:

Effect: each time you run, you will get different brushes and brushes

Definition of global variables based on GDI

HPEN		g_hPen[7]={0}; 
HBRUSH	g_hBrush[7]={0}; 
int				g_iPenStyle[7] = {PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT,PS_NULL,PS_INSIDEFRAME};  
int				g_iBrushStyle[6] = {HS_VERTICAL,HS_HORIZONTAL,HS_CROSS,HS_DIAGCROSS,HS_FDIAGONAL,HS_BDIAGONAL};  
PlaySound(L"AIR - Xia Ying.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //Loop background music

Randomly generate the colors of brushes and brushes

BOOL Game_Init( HWND hwnd )
{
	g_hdc = GetDC(hwnd);
	srand((unsigned)time(NULL));

	
	for(int i=0;i<=6;i++)
	{
			g_hPen[i] = CreatePen(g_iPenStyle[i],1,RGB(rand()%256,rand()%256,rand()%256));
		if(i==6)
			g_hBrush[i] = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));
		else
			g_hBrush[i] = CreateHatchBrush(g_iBrushStyle[i],RGB(rand()%256,rand()%256,rand()%256));
	}

	Game_Paint(hwnd);
	ReleaseDC(hwnd,g_hdc);
	return TRUE;
}

Drawing logic

VOID Game_Paint( HWND hwnd )
{
	//Define a y coordinate value
	int y=0;

	//A for loop that draws lines with seven different brushes
	for(int i=0;i<=6;i++)
	{
		y = (i+1) * 70;

		SelectObject(g_hdc,g_hPen[i]);//Select the corresponding brush
		MoveToEx(g_hdc,30,y,NULL);	 //The cursor moves to the corresponding (30, y) coordinate
		LineTo(g_hdc,100,y);			 //Draw a line segment from (30, y) to (100, y)
	}

	/*Note that y=420 after drawing above, which is also useful when drawing rectangle below*/
	//Define two x coordinate values
	int x1 = 120;
	int x2 = 190;

	//Fill the rectangle with 7 different brushes
	for(int i=0;i<=6;i++)
	{
		SelectObject(g_hdc,g_hBrush[i]);  //Choose brush
		Rectangle(g_hdc,x1,70,x2,y);	 //Draw a closed rectangle. The coordinates of the upper left corner of the rectangle are (x1,50) and the coordinates of the lower right corner are (x2, y)
		x1 += 90;
		x2 += 90;
	}
}

Release handle

BOOL Game_CleanUp( HWND hwnd )
{
	//A for loop that releases all brush and brush handles
	for (int i=0;i<=6;i++)
	{
		DeleteObject(g_hPen[i]);
		DeleteObject(g_hBrush[i]);
	}
	return TRUE;
}

Drawing with QT frame

Graphics/View framework: store, display and process interactive commands that operate on graphic elements

QGraphicsView: it is responsible for displaying some or all graphic elements in a scene. It has its own coordinate system and is closely related to specific display devices

QGraphicsItem: graphic elements displayed

QGraphicsScene: a scene that includes all graphic elements

Maintain a list: record which graphic elements are selected

Data structure: record which graphic element or elements get focus

Collision of graphic elements

Detection principle:

The virtual function shape() in QGraphicsItem is responsible for returning its own contour,

colidingItems() in QGraphicsScene is responsible for querying the outline of each graphic element in the scene and judging which elements collide with a specified element

Colliding mice

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));//Initializing random seeds with system time 


    QGraphicsScene scene;//Scene object
    scene.setSceneRect(-300, -300, 600, 600);

    scene.setItemIndexMethod(QGraphicsScene::NoIndex);
    for (int i = 0; i < MouseCount; ++i) { //6 mice
        Mouse *mouse = new Mouse;//Mouse object
        mouse->setPos(::sin((i * 6.28) / MouseCount) * 200,
                      ::cos((i * 6.28) / MouseCount) * 200);//Set position
        scene.addItem(mouse);//Add to scene
    }
    QGraphicsView view(&scene);//Responsible for displaying some or all elements in a scene
    view.setRenderHint(QPainter::Antialiasing);
    view.setBackgroundBrush(QPixmap(":/images/cheese.jpg"));//Sweetheart picture

    view.setCacheMode(QGraphicsView::CacheBackground);
    view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    view.setDragMode(QGraphicsView::ScrollHandDrag);

    view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice"));
    view.resize(400, 300);
    view.show();

    QTimer timer; //Timer Objects 
    QObject::connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); //Signal slot (timer signal advance)
    timer.start(1000 / 30); //Timer (advance () of graphic elements at a frequency of 3s)

    return app.exec();
}

#include <QGraphicsItem>

class Mouse : public QGraphicsItem
{
public:
    Mouse();
    //Returns the smallest rectangle surrounding a mouse figure
    QRectF boundingRect() const override;
    //Returns the exact shape of the mouse
    QPainterPath shape() const override;
    //This function is called when a graphic element needs to be drawn
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget) override;

protected:
    //Update the position of the mouse and the color of the eyes
    void advance(int step) override;

private:
    qreal angle;
    qreal speed;
    qreal mouseEyeDirection;
    QColor color;
};

Function: returns the smallest rectangle surrounding a mouse figure

Implementation idea:

static qreal normalizeAngle(qreal angle)
{
    while (angle < 0)
        angle += TwoPi;
    while (angle > TwoPi)
        angle -= TwoPi;
    return angle;
}

Function: initialize the variable value and set the angle direction of the mouse

Mouse::Mouse()
    : angle(0), speed(0), mouseEyeDirection(0),
      color(qrand() % 256, qrand() % 256, qrand() % 256)
{
    setRotation(qrand() % 360 );
}

Function: returns the smallest rectangle surrounding a mouse figure

QRectF Mouse::boundingRect() const
{
    qreal adjust = 0.5;
    return QRectF(-18 - adjust, -22 - adjust,
                  36 + adjust, 60 + adjust);
}

Function: returns the exact shape of the mouse

QPainterPath Mouse::shape() const
{
    QPainterPath path;
    path.addRect(-10, -20, 20, 40);
    return path;
}

Function: when you need to draw a graphic element, this function will be called

Realization idea: splice each organ of the mouse by drawing ellipses and coordinate positions

void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    // Body
    painter->setBrush(color);
    painter->drawEllipse(-10, -20, 20, 40);

    // Eyes
    painter->setBrush(Qt::white);
    painter->drawEllipse(-10, -17, 8, 8);
    painter->drawEllipse(2, -17, 8, 8);

    // Nose
    painter->setBrush(Qt::black);
    painter->drawEllipse(QRectF(-2, -22, 4, 4));

    //Eyeball
    painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4));
    painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4));

    // Ears
    painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red);//collision detection 
    painter->drawEllipse(-17, -12, 16, 16);
    painter->drawEllipse(1, -12, 16, 16);

    // Tail
    QPainterPath path(QPointF(0, 20));
    path.cubicTo(-5, 22, -5, 22, 0, 25);
    path.cubicTo(5, 27, 5, 32, 0, 30);
    path.cubicTo(-5, 32, -5, 42, 0, 35);
    painter->setBrush(Qt::NoBrush);
    painter->drawPath(path);
}

Function: update the position of the mouse and the color of the eyes

Realization idea: to be studied

void Mouse::advance(int step)
{
    if (!step)
        return;

    // Don't move too far away

    QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0));
    if (lineToCenter.length() > 150) {
        qreal angleToCenter = ::acos(lineToCenter.dx() / lineToCenter.length());
        if (lineToCenter.dy() < 0)
            angleToCenter = TwoPi - angleToCenter;
        angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2);

        if (angleToCenter < Pi && angleToCenter > Pi / 4) {
            // Rotate left
            angle += (angle < -Pi / 2) ? 0.25 : -0.25;
        } else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4)) {
            // Rotate right
            angle += (angle < Pi / 2) ? 0.25 : -0.25;
        }
    } else if (::sin(angle) < 0) {
        angle += 0.25;
    } else if (::sin(angle) > 0) {
        angle -= 0.25;

    }


    // Try not to crash with any other mice

    QList<QGraphicsItem *> dangerMice = scene()->items(QPolygonF()
                                                       << mapToScene(0, 0)
                                                       << mapToScene(-30, -50)
                                                       << mapToScene(30, -50));
    foreach (QGraphicsItem *item, dangerMice) {
        if (item == this)
            continue;
        
        QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0));
        qreal angleToMouse = ::acos(lineToMouse.dx() / lineToMouse.length());
        if (lineToMouse.dy() < 0)
            angleToMouse = TwoPi - angleToMouse;
        angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2);

        if (angleToMouse >= 0 && angleToMouse < Pi / 2) {
            // Rotate right
            angle += 0.5;
        } else if (angleToMouse <= TwoPi && angleToMouse > (TwoPi - Pi / 2)) {
            // Rotate left
            angle -= 0.5;

        }

    }


    // Add some random movement

    if (dangerMice.size() > 1 && (qrand() % 10) == 0) {
        if (qrand() % 1)
            angle += (qrand() % 100) / 500.0;
        else
            angle -= (qrand() % 100) / 500.0;
    }
    speed += (-50 + qrand() % 100) / 100.0;

    qreal dx = ::sin(angle) * 10;
    mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5;

    setRotation(rotation() + dx);
    setPos(mapToParent(0, -(3 + sin(speed) * 3)));
}

a design chart

Renderings

 

Oh, my God, I'm so tired. Give yourself a compliment!

Topics: C++