QT implementation of pen line drawing effect examples and detailed principles

Time:2021-3-11

preface

Last article: QT to achieve line stroke effect detailed principle, according to this article to achieve the principle of stroke effect, we can easily achieve another kind of stroke effect: pen.

The so-called pen effect is to truly restore the line effect written by the pen. Its characteristics are: the width of the line will gradually change according to the drawing speed of the pen. The faster the pen is written, the thinner the line will be, and it will have a pen edge effect when the pen is closed.

Then, on the basis of the previous article, a little modification can achieve this effect. Take a look at the renderings:

Implementation principle

As we know from the last article, the curve drawn is a Bessel curve formed by every two points, so we draw a line continuously without letting go. The whole line segment is composed of many paths. The key to achieve the pen effect is to let the thickness of the line follow the speed of drawing.

I’ve seen many algorithms for pen or brush on Android before. They all need to calculate the speed of drawing lines, and dynamically change the thickness of lines according to the speed. But I don’t calculate the speed here, but directly calculate a reasonable width by the length of each path.

As we all know, length (that is, distance) = speed * time. In unit time, speed is directly proportional to distance, so it’s the same for us to judge by the distance between two points. There’s no big difference, and we don’t need to calculate speed separately. It’s simple and easy.

Then, the effect we want to achieve is that the faster the line drawing speed is, the thinner the line will be, and the faster the line drawing speed is, the greater the distance between the two points collected will be. We use the distance between the two points as a reference, that is, the distance between the two points is inversely proportional to the thickness of the line. The longer the distance between the two points is, the thinner the corresponding path is. The shorter the distance is, the thicker the path is It is a linear relationship. Of course, there will be a maximum and minimum width of path, which needs to be debugged in the actual scene.

OK, according to the above analysis, we can get the following diagram:

Each path is a real-time Bezier curve generated by two coordinate points.
When drawing this curve, first get the length of the curve, and then calculate a linear width value.
How to get the length of path?
This is easy to do. Qpainterpath has its own interfacelength()

To calculate the width of the curve, I wrote a simple calculation method:

qreal WbCanvasItem::calPathWidth(QPainterPath path)
{
  qreal length = path.length();
  qreal width = PENWIDTH;

  qreal t = length/10. - 1;
  width = PENWIDTH - t;
  If (width < 3) {// minimum width
    width = 3;
  }
  return width;
}

Penwidth is a macro definition, the maximum width of the curve;

According to the above steps, let’s take a look at the effect:

In order to see the effect conveniently, each path is distinguished by different colors. We can clearly see that the width of the path is different, and the width of the junction of each path changes significantly. So how to make the junction smooth?

At this time, we need to use the method introduced in the previous article to supplement the points. The supplementary points here are a little more troublesome than those in the previous article. We need to supplement the two ends of the middle line. The principle is the same.

Take a look at the diagram

The above red circle is the supplementary point.
As can be seen from the above figure, path2 is the penultimate path, and path3 is the last path.

It should be noted that the two places added in the figure are not added at the same time. When a new path arrives, you only need to judge the width of the latest path and the previous path, so as to decide whether to add to the previous path or the current latest path.

This passage is a bit awkward

If path2 is the last path and path1 is the penultimate path, we can judge that path2 is smaller than path1, then we should add some points on the path of path2;

Let’s look at another situation

Similarly, here path2 is the last path, and path1 is the penultimate path. If you judge that path2 is wider than path1, you can add a point on the path of path1;

So the description is easy to understand.

OK, let’s take a look at the code of the supplementary point


void WbCanvasItem::drawPatchPoint2(QPainter *painter, QPainterPath lastPath,
                  QPainterPath curPath,
                  qreal lastWidth, qreal curWidth)
{
  qreal tPatchLength = 100.;
  if(lastWidth < curWidth){
    tPatchLength = calPatchLength(curPath.length());

    qreal temp = (curWidth-lastWidth)/tPatchLength;
    int k = 0;

    for (qreal i = 1;i > (100-tPatchLength)/100.; i-=0.01) {
      k++;
      painter->setPen(QPen(Qt::black,curWidth-temp*k, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
      painter->drawPoint(lastPath.pointAtPercent(i));
    }
  }
  else if(lastWidth > curWidth){

    tPatchLength = calPatchLength(curPath.length());

    qreal temp = (lastWidth-curWidth)/tPatchLength;
    int k = 0;

    for (qreal i = 0;i < tPatchLength/100.; i+=0.01) {
      k++;
      painter->setPen(QPen(Qt::black,lastWidth-temp*k, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
      painter->drawPoint(curPath.pointAtPercent(i));
    }
  }
}

Take a look at the effect of the supplementary points:

The black part here is the point of dynamic supplement.

Well, the principle analysis is finished. In fact, it’s similar to the principle of the previous article. It’s just one more step to judge the distance and then calculate the line width.

This article about QT to achieve pen line effect examples and detailed principles of the article introduced here, more related QT pen line content please search previous articles or continue to browse the following related articles, I hope you can support developer in the future!