参考原文
https://www.iteye.com/blog/westice-745173
为了把一串点连成光滑的曲线,先研究贝塞尔曲线,又搞B样条插值。。。。都没有成功(数学没那么强)。
后来在
“[翻译] AGG 之贝塞尔插值”http://liyiwen.iteye.com/blog/705489 。看到一种比较好的方法:
运用贝塞尔曲线的光滑性来穿过这些点。
大致思路就是 先算出相邻原始点的中点,在把相邻中点连成的线段平移到对应的原始点,以平移后的中点作为控制点,相邻原始点为起始点画贝塞尔曲线,这样就保证了连接处的光滑。而贝塞尔曲线本身是光滑的,所以就把这些原始点用光滑曲线连起来了。
lua代码
---@class Bezier
local Bezier = Lib.class("Bezier")
local new = Bezier.new
function Bezier:ctor()
end
-- local originPoint = {
-- [1] = Lib.v2(0,600),
-- [2] = Lib.v2(100,500),
-- [3] = Lib.v2(200,300),
-- [4] = Lib.v2(600,0),
-- }
function Bezier:createCurve(originPoint)
local scale = 0.6
-- 控制点收缩系数 ,经调试0.6较好,CvPoint是opencv的,可自行定义结构体(x,y)
local originCount = #originPoint
local midpoints = {}
-- 生成中点
for i = 1, originCount do
midpoints[i] = {}
local nexti = i+1;
if nexti == originCount + 1 then
nexti = 1
end
local prei = i-1;
if prei == 0 then
prei = originCount
end
midpoints[i].prei = prei
midpoints[i].nexti = nexti
midpoints[i].x = (originPoint[i].x + originPoint[nexti].x)/2.0;
midpoints[i].y = (originPoint[i].y + originPoint[nexti].y)/2.0;
end
-- 平移中点,originPoint[i] 的前后两个中点,也就是midpoints[i] 和 midpoints[i-1] 的平移点
local extrapoints1 = {}
local extrapoints2 = {}
for i = 1, originCount do
extrapoints1[i] = {}
extrapoints2[i] = {}
end
for i = 1, originCount do
local prei = midpoints[i].prei;
local midinmid = {}
midinmid.x = (midpoints[i].x + midpoints[prei].x)/2.0;
midinmid.y = (midpoints[i].y + midpoints[prei].y)/2.0;
local offsetx = originPoint[i].x - midinmid.x;
local offsety = originPoint[i].y - midinmid.y;
extrapoints1[i].x = midpoints[i].x + offsetx
extrapoints1[i].y = midpoints[i].y + offsety
--朝 originPoint[i]方向收缩
local addx = (extrapoints1[i].x - originPoint[i].x) * scale;
local addy = (extrapoints1[i].y - originPoint[i].y) * scale;
extrapoints1[i].x = originPoint[i].x + addx
extrapoints1[i].y = originPoint[i].y + addy
extrapoints2[prei].x = midpoints[prei].x + offsetx
extrapoints2[prei].y = midpoints[prei].y + offsety
--朝 originPoint[i]方向收缩
addx = (extrapoints2[prei].x - originPoint[i].x) * scale
addy = (extrapoints2[prei].y - originPoint[i].y) * scale
extrapoints2[prei].x = originPoint[i].x + addx
extrapoints2[prei].y = originPoint[i].y + addy
end
local curvePoint = {}
local controlList = {}
--生成4控制点,产生贝塞尔曲线
for i = 1, originCount-1 do
local controlPoint = {}
controlPoint[1] = originPoint[i]
controlPoint[2] = extrapoints1[i]
controlPoint[3] = extrapoints2[i]
local nexti = i+1;
if i == originCount then
nexti = 1
end
controlPoint[4] = originPoint[nexti]
local u = 1
while(u >= 0) do
local px = self:bezier3funcX(u,controlPoint)
local py = self:bezier3funcY(u,controlPoint)
--u的步长决定曲线的疏密
u = u - 0.05
local tempP = Lib.v2(px,py)
--存入曲线点
table.insert(curvePoint, tempP)
end
table.insert(controlList, controlPoint)
end
return curvePoint
end
-- 三次贝塞尔曲线
function Bezier:bezier3funcX(uu, controlP)
local part0 = controlP[1].x * uu * uu * uu;
local part1 = 3 * controlP[2].x * uu * uu * (1 - uu);
local part2 = 3 * controlP[3].x * uu * (1 - uu) * (1 - uu);
local part3 = controlP[4].x * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
end
function Bezier:bezier3funcY(uu, controlP)
local part0 = controlP[1].y * uu * uu * uu;
local part1 = 3 * controlP[2].y * uu * uu * (1 - uu);
local part2 = 3 * controlP[3].y * uu * (1 - uu) * (1 - uu);
local part3 = controlP[4].y * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
end
return Bezier