Qt 实现钢笔画线效果示例及详细原理

前言

上一篇文章:Qt 实现画线笔锋效果详细原理,根据这篇介绍的实现笔锋效果的原理,我们很容易实现另外一种笔效:钢笔。

所谓的钢笔笔效,就是真实还原钢笔书写出来的线条效果,其特征就是:根据笔的绘制速度而线条的宽度会逐渐变化,写得越快,线条越细,并且在收笔时带有笔锋效果。

那么,在上一篇文章的基础上,稍微修改一下,就可以实现这个效果,看下效果图:

实现原理

从上一篇文章我们知道,绘制的曲线是通过每两个点形成一条贝塞尔曲线,所以在不松手的情况下连续画线,整条线段是包含很多条path组合而成的。而要实现钢笔效果的关键,是要让线条的粗细跟随绘制的速度来变化。

之前看过很多Android上实现钢笔或者毛笔的算法,都是需要计算去画线速度,根据速度来动态改变线条的粗细。但是我这里没有计算速度,而是直接通过每一段path的长度来计算一个合理的宽度值出来。

众所周知,长度(也就是距离)= 速度*时间,在单位时间内,速度和距离是成正比的,所以我们通过两点间的距离来做判断也是一样的,没多大区别,并且还不用单独去计算速度了,简单省事儿。

那么,我们要实现的效果是,画线速度越快线条会越细,而画线速度越快,所采集到的两点间的距离就会越大,而我们是通过两点间距离来做参考依据,也就是说,两点间距和线条粗细是成反比的,两点间距越长,对应的这条path就越细,距离越短,path就越粗,二者是线性关系。当然,这里path的宽度会有一个最大值和最小值,需要在实际的场景中进行调试。

OK,根据以上分析,我们可以得到以下的示意图:

每条path都是通过两个坐标点实时生成的贝塞尔曲线。
在绘制这条曲线的时候,先获取到曲线的长度,然后线性计算出一个宽度值。
如何获取path的长度呢?
这个好办,QPainterPath有自带的接口length()

计算曲线的宽度,我写了一个简单的计算方法:

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

  qreal t = length/10. - 1;
  width = PENWIDTH - t;
  if(width < 3){ //最小宽度
    width = 3;
  }
  return width;
} 

PENWIDTH是一个宏定义,曲线最大宽度;

根据以上步骤,我们来看一下效果:

为了方便看效果,每条path用了不同的颜色来区分。我们可以很明显的看到,path的宽度是不一样的,并且每条path的连接处的宽度变化非常明显,那么要怎么使其连接处变得平滑呢?

这时候就要用到上一篇介绍的方法进行补点了。这里的补点比上一篇文章中说的稍微麻烦点,需要将中间那根线条的两头都要补充点,其原理是一样的。

看一下示意图:

以上红色圈圈部分,就是补充的点。
从以上图可以看到,path2是倒数第二条path,path3是最后一条path。

需要注意的是,图中补充的两个地方,并不是同一时间补充的,当有新的path到来,只需要判断最新的path和上一个path的宽度,从而决定是补充到上一个path还是当前最新的path上。

这段话有点拗口,拆解一下:

假如这里path2是最后的一条path,而path1是倒数第二条,判断出来path2宽度笔path1小,那么就在path2的路径上补充点;

再看一种情况:

同样,这里path2是最后的一条path,而path1是倒数第二条,判断出来path2宽度笔path1大,那么就在path1的路径上补充点;

这样描述就很容易理解了。

OK,我们看一下补充点的代码:

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

看一下补充点后的效果:

这里的黑色部分就是动态补充上去的点。

好了, 整理原理分析完成,其实和前面一篇文章原理差不多,只是多了一步判断距离然后计算线宽的过程。

到此这篇关于Qt 实现钢笔画线效果示例及详细原理的文章就介绍到这了,更多相关Qt 钢笔画线内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Qt 实现画线笔锋效果详细原理及示例代码

    前言 之前写过一篇文章介绍Qt中绘制平滑曲线的两种方式,文章在这里.这篇文章详细介绍了绘制的原理和实现方式,那么,如果要在此曲线上实现笔锋效果怎么做呢? 所谓的笔锋效果,就是钢笔书写抬笔时的笔尖,也就是说,绘制曲线抬笔时形成一个笔尖的效果. 话不多说,直接来看效果: 动画效果如下: 实现原理 要实现该效果,需要完成以下几个关键步骤: 1.每两个点形成一个贝塞尔曲线path进行绘制 2.最新的一条path绘制细线(笔锋最细处的宽度) 3.倒数第二条path绘制粗线(正常的线条宽度) 4.在两条pa

  • Qt 实现钢笔画线效果示例及详细原理

    前言 上一篇文章:Qt 实现画线笔锋效果详细原理,根据这篇介绍的实现笔锋效果的原理,我们很容易实现另外一种笔效:钢笔. 所谓的钢笔笔效,就是真实还原钢笔书写出来的线条效果,其特征就是:根据笔的绘制速度而线条的宽度会逐渐变化,写得越快,线条越细,并且在收笔时带有笔锋效果. 那么,在上一篇文章的基础上,稍微修改一下,就可以实现这个效果,看下效果图: 实现原理 从上一篇文章我们知道,绘制的曲线是通过每两个点形成一条贝塞尔曲线,所以在不松手的情况下连续画线,整条线段是包含很多条path组合而成的.而要实

  • 用JavaScript实现使用鼠标画线的示例代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

  • 在vue中使用Echarts画曲线图的示例

    现实的工作中, 数据不可能写死的,所有的数据都应该通过发送请求进行获取. 所以本项目的需求是请求服务器获得二维数组,并生成曲线图.曲线图的横纵坐标均从获得的数据中取得. Echarts官方文档: https://ecomfe.github.io/echarts-doc/public/en/index.html 前端框架使用vue,服务器使用express搭建,交互使用axios. 一.引入vue-resource 通过npm下载vue-resource npm install vue-resou

  • js 实现Material UI点击涟漪效果示例

    目录 正文 HTML CSS JS 实现效果 正文 我个人而言还是挺喜欢Material UI这套设计风格的.一些细节方面做的还不错.就比如今天要给大家分享的点击涟漪效果.Material UI里面叫做Ripples.好了,话不多说,开始吧. HTML <div class="example">Click me</div> CSS .example { position: relative; width: 300px; height: 300px; line-h

  • WPF实现画线动画效果

    本文实例为大家分享了WPF实现画线动画的具体代码,供大家参考,具体内容如下 需求:一条直线(不是曲线),模范笔画一样在画布上逐渐画出来.但前提是,用后台代码实现,并非WPF标签 效果: 上代码: /// <summary> /// Window2.xaml 的交互逻辑 /// </summary> public partial class Window2 : Window { public Window2() { InitializeComponent(); var canvas

  • QT中在QLabel显示图片并且利用鼠标点击画线问题

    目录 在QLabel显示图片并且利用鼠标点击画线 以下为我的代码,供参考 在QLabel显示图片并且利用鼠标点击画线 最近在做在Label上显示图片并且通过鼠标点击画线,在网上查了很多零零散散的东西,收获也多 很多初学者更希望直接贴代码,这样可以模仿来写,我下面直接贴出我的项目中自己写的maLabel类 (如果只是实现利用鼠标绘制, 重写void paintEvent(QPaintEvent *event);void mousePressEvent(QMouseEvent *e); void m

  • Qt实现绘制网格背景的示例代码

    目录 现有功能 运行结果 源码 window.h window.cpp main.cpp 现有功能 使用滚轮缩放. 缩放到达一定阈值后恢复网格大小. 窗口大小调整时网格背景也自动调整重绘. 运行结果 源码 window.h #ifndef WINDOW_H #define WINDOW_H #include <QWidget> #include <QPen> #include <QPainter> #include <QPaintEvent> class W

  • Delphi下OpenGL2d绘图之画线的方法

    一.前言: Delphi画线方法与画点基本上是相同的.区别在于glBegin()的参数.绘制的框架代码可以参考Delphi下OpenGL2d绘图初始化中的代码,地址为http://www.jb51.net/article/52141.htm.修改的部份为 Draw 函数的内容. 二.画线步骤: GL_LINES:把每一个顶点作为一个独立的线段,顶点2n-1和2n之间共定义了n条线段,总共绘制N/2条线段 GL_LINE_STRIP:绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个

  • Android开发实现绘制淘宝收益图折线效果示例

    本文实例讲述了Android开发实现绘制淘宝收益图折线效果.分享给大家供大家参考,具体如下: 实现的效果我一会贴上,我先说下原理,我们知道要实现在canvas上画线,不就是要搞一个paint嘛,然后首先肯定要设置下paint的属性,那么画文字呢,不就是Textpaint吗,对,就是这么简单,接下来怎么画,折线图主要分为X轴和y轴,x轴表示日期,y表示收益,好,说道这里,大家应该知道怎么去做了,下面直接贴代码 这个方法是,画x,y坐标系的,以及上面的日期和收益了 private void draw

随机推荐