且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

穿过已知点画平滑曲线(3次贝塞尔曲线)

更新时间:2022-06-02 06:16:40

运用贝塞尔曲线的光滑性来穿过这些点。

大致思路就是 先算出相邻原始点的中点,在把相邻中点连成的线段平移到相应的原始点,以平移后的中点作为控制点。相邻原始点为起始点画贝塞尔曲线。这样就保证了连接处的光滑。而贝塞尔曲线本身是光滑的,所以就把这些原始点用光滑曲线连起来了。


穿过已知点画平滑曲线(3次贝塞尔曲线)

我封装了一个函数,留着以后用。

(c++版。其他语言仅仅要把数组和可变数组略微变一下就能用)

 

  1. void createCurve(CvPoint *originPoint,int originCount,vector<CvPoint> &curvePoint){  
  2.     //控制点收缩系数 。经调试0.6较好,CvPoint是opencv的,可自行定义结构体(x,y)  
  3.     float scale = 0.6;  
  4.     CvPoint midpoints[originCount];  
  5.     //生成中点       
  6.     for(int i = 0 ;i < originCount ; i++){      
  7.         int nexti = (i + 1) % originCount;  
  8.         midpoints[i].x = (originPoint[i].x + originPoint[nexti].x)/2.0;  
  9.         midpoints[i].y = (originPoint[i].y + originPoint[nexti].y)/2.0;  
  10.     }      
  11.       
  12.     //平移中点  
  13.     CvPoint extrapoints[2 * originCount];   
  14.     for(int i = 0 ;i < originCount ; i++){  
  15.          int nexti = (i + 1) % originCount;  
  16.          int backi = (i + originCount - 1) % originCount;  
  17.          CvPoint midinmid;  
  18.          midinmid.x = (midpoints[i].x + midpoints[backi].x)/2.0;  
  19.          midinmid.y = (midpoints[i].y + midpoints[backi].y)/2.0;  
  20.          int offsetx = originPoint[i].x - midinmid.x;  
  21.          int offsety = originPoint[i].y - midinmid.y;  
  22.          int extraindex = 2 * i;  
  23.          extrapoints[extraindex].x = midpoints[backi].x + offsetx;  
  24.          extrapoints[extraindex].y = midpoints[backi].y + offsety;  
  25.          //朝 originPoint[i]方向收缩   
  26.          int addx = (extrapoints[extraindex].x - originPoint[i].x) * scale;  
  27.          int addy = (extrapoints[extraindex].y - originPoint[i].y) * scale;  
  28.          extrapoints[extraindex].x = originPoint[i].x + addx;  
  29.          extrapoints[extraindex].y = originPoint[i].y + addy;  
  30.            
  31.          int extranexti = (extraindex + 1)%(2 * originCount);  
  32.          extrapoints[extranexti].x = midpoints[i].x + offsetx;  
  33.          extrapoints[extranexti].y = midpoints[i].y + offsety;  
  34.          //朝 originPoint[i]方向收缩   
  35.          addx = (extrapoints[extranexti].x - originPoint[i].x) * scale;  
  36.          addy = (extrapoints[extranexti].y - originPoint[i].y) * scale;  
  37.          extrapoints[extranexti].x = originPoint[i].x + addx;  
  38.          extrapoints[extranexti].y = originPoint[i].y + addy;  
  39.            
  40.     }      
  41.       
  42.     CvPoint controlPoint[4];  
  43.     //生成4控制点。产生贝塞尔曲线  
  44.     for(int i = 0 ;i < originCount ; i++){  
  45.            controlPoint[0] = originPoint[i];  
  46.            int extraindex = 2 * i;  
  47.            controlPoint[1] = extrapoints[extraindex + 1];  
  48.            int extranexti = (extraindex + 2) % (2 * originCount);  
  49.            controlPoint[2] = extrapoints[extranexti];  
  50.            int nexti = (i + 1) % originCount;  
  51.            controlPoint[3] = originPoint[nexti];      
  52.            float u = 1;  
  53.            while(u >= 0){  
  54.                int px = bezier3funcX(u,controlPoint);  
  55.                int py = bezier3funcY(u,controlPoint);  
  56.                //u的步长决定曲线的疏密  
  57.                u -= 0.005;  
  58.                CvPoint tempP = cvPoint(px,py);  
  59.                //存入曲线点   
  60.                curvePoint.push_back(tempP);  
  61.            }      
  62.     }  
  63. }  
  64. //三次贝塞尔曲线  
  65. float bezier3funcX(float uu,CvPoint *controlP){  
  66.    float part0 = controlP[0].x * uu * uu * uu;  
  67.    float part1 = 3 * controlP[1].x * uu * uu * (1 - uu);  
  68.    float part2 = 3 * controlP[2].x * uu * (1 - uu) * (1 - uu);  
  69.    float part3 = controlP[3].x * (1 - uu) * (1 - uu) * (1 - uu);     
  70.    return part0 + part1 + part2 + part3;   
  71. }      
  72. float bezier3funcY(float uu,CvPoint *controlP){  
  73.    float part0 = controlP[0].y * uu * uu * uu;  
  74.    float part1 = 3 * controlP[1].y * uu * uu * (1 - uu);  
  75.    float part2 = 3 * controlP[2].y * uu * (1 - uu) * (1 - uu);  
  76.    float part3 = controlP[3].y * (1 - uu) * (1 - uu) * (1 - uu);     
  77.    return part0 + part1 + part2 + part3;   
  78. }   

翻译] AGG 之贝塞尔插值

文章分类:综合技术

原文地址:http://www.antigrain.com/research/ 
bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION

Interpolation with Bezier Curves  贝塞尔插值

A very simple method of smoothing polygons 一种很easy的多边形平滑方法

翻译:唐风

之前 comp.graphic.algorithms 上有一个讨论,是关于怎么样使用曲线对多边形进行插值处理,使得终于产生的曲线是光滑的并且能通过全部的顶点。

Gernot Hoffmann 建议说使用著名的 B-Spline 来进行插值。这里有他当时的文章 。B-Spline 在这里效果非常好,它看起来就像是一个固定在多边形顶点上的橡皮尺(elastic ruler)。

穿过已知点画平滑曲线(3次贝塞尔曲线)    穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

穿过已知点画平滑曲线(3次贝塞尔曲线)但我有个大胆的猜測,我认为肯定还存在更简单的方法。比方,使用三次贝塞曲线(cubic Bezier)进行近似。贝塞尔曲线有两个固定点(起点和终点)。另加两个决定曲线形状的控制点(CP)。关于贝塞尔曲线的很多其它知识能够在搜索引擎中找到,比方,你能够參考Paul Bourke 的网站 

如今给贝塞尔曲线的锚点(固定点),也就是多边形的某一对顶点。那么问题是,我们怎么计算控制点的位置?我执行 Xara X 然后画出了右边这个图形,这非常easy,所以我决定尝试下计算出它们的坐标。非常显然,多边形两条相邻边的两个控制点与这两个控制点之间的顶点应该在一条直线 上。仅仅有这样,两条相邻的插值曲线才干平滑地连接在一起。所以。这两个控制点应该是相对于顶点是对称的,只是,也不全是……。由于真正的对称就要求它们与 中心点的距离应该是相等的,但对于我们的情况中并不全然是这种。一開始。我试着先算出多边形两条边的角平分线。然后把控制点放在这条角平分线的垂直线 上。但从图上能够看到。控制点的连线并不会总是垂直于角平分线的。

 


 

终于,我找到一个很easy的办法,不须要不论什么复杂的数学计算。首先。我们计算出多边形全部边线的中点,Ai。

穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

然后连接起相邻边中点,得到非常多线段,记为 Ci 。并用图记的方法计算出 Bi 点。

穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

最后一步,仅仅须要简单地将 Ci 进行平移,平移的路径就是每条线段上 Bi 到相应顶点的路径。

就这样,我们计算出了贝塞尔曲线的控制点。平滑的结果看起来也非常棒。

穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

这里还能够做一点小小的改进。由于我们已经得到了一条决定控制点的直线,所以,我们能够依据须要。使控制点在这条直线上移动,这样能够改变插值曲线 的状态。我使用了一个与控制点和顶点初始距离相关的系数 K ,用来沿直线移动控制点。

控制点离顶点越远,图形看起来就越锐利。

穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

以下是用原始形状和系统K=1.0的贝塞尔插值两种方法来描画的 SVG 的狮子。

穿过已知点画平滑曲线(3次贝塞尔曲线)   穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

以下是放大图

穿过已知点画平滑曲线(3次贝塞尔曲线)   穿过已知点画平滑曲线(3次贝塞尔曲线)

 


 

不积跬步无以至千里,闲来无事。搞一个属于自己的小站,假设看到这篇文章感觉对你有帮助的话,就支持关注一下我的小站:我的小站

 


 

这种方法仅仅是探索和经验式的。假设从严格的数学模型的角度看它可能是错误的。但在实际使用中的效果已经足够好了,而 且这种方法仅仅须要最小的计算量。以下的代码就是用来画出上面狮子图像的。这些代码并没有进行优化。仅仅是用来演示的。里面有些变量计算了两次,在实际程序 中,假设连续的步骤中都用到同一个变量值,我们能够先缓存变量值进行复用(以避免反复的计算)。

你能够下载一个能执行的画狮子的样例,对它进行旋转和缩放。也能够生成一些随机的多边形。点左键并拖动它能够环绕中 心点旋转和缩放图像。点右键并从左向右拖动。能够改变系统数K。

K=1时大约是距窗体左边100像素处。每次双击会产生一个随机的多边形。对于这些多边形。也能够进行旋转、缩放以及改变K值的操作









本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5062590.html,如需转载请自行联系原作者