Graphics 光滑凸包

Graphics 光滑凸包,graphics,computational-geometry,graphics2d,convex-hull,Graphics,Computational Geometry,Graphics2d,Convex Hull,我已经开始使用凸包算法,并且想知道我可以使用什么方法来平滑多边形边。船体的轮廓不平滑。我想做的是使通过顶点的线更平滑,这样它们就不会有角度 我尝试过实现Beziers(只是意识到形状与船体的形状完全不同)和b样条曲线(同样,形状与之完全不同,事实上,我无法使b样条曲线成为闭合形状) 我失败了,希望有人能提供指导。(注意!这不是解决办法) 我试图找到极坐标系下的精确解,但发现有时“平滑曲线”位于凸多边形内。通过在[0:2*pi]区间的θ外添加额外的可移动不可见点,一阶导数匹配条件(在起始点)基本

我已经开始使用凸包算法,并且想知道我可以使用什么方法来平滑多边形边。船体的轮廓不平滑。我想做的是使通过顶点的线更平滑,这样它们就不会有角度

我尝试过实现Beziers(只是意识到形状与船体的形状完全不同)和b样条曲线(同样,形状与之完全不同,事实上,我无法使b样条曲线成为闭合形状)

我失败了,希望有人能提供指导。

(注意!这不是解决办法)

我试图找到极坐标系下的精确解,但发现有时“平滑曲线”位于凸多边形内。通过在[0:2*pi]区间的θ外添加额外的可移动不可见点,一阶导数匹配条件(在起始点)基本上是可解的。但在我看来,上述问题无论如何都无法解决

以下是带有我的尝试的Lua脚本(使用qhullrbox(来自qhull工具链)和gnuplot实用程序):

函数使用()
返回错误('使用:'..arg[0]..')
结束
功能点来自文件(内嵌)
局部点={}
本地填充=io.open(填充'r')
本地d=infle:read(“*number”)
如果d~=2,那么
错误('维度不是两个')
结束
本地n=infle:read('*number')
尽管如此
本地x,y=infle:read('*number','*number'))
如果不是x也不是y,那么
打破
结束
如果不是x或者不是y,那么
错误('输入文件格式错误:行不包含两个坐标')
结束
表.插入(点,{x,y})
结束
infle:close()
如果n~=#点,则
错误('第二行不包含实际点数')
结束
返回点
结束
如果不是arg,那么
错误(“脚本应作为独立脚本使用”)
结束
如果#arg~=1,则
使用()
结束
本地n=tonumber(arg[1])
如果不是n那么
使用()
结束
局部边界盒=math.sqrt(math.pi)/2.0
本地fnp=os.tmpname()
本地fnchp=os.tmpname()
操作系统执行('rbox'..n..'B'..bounding|u box..'D2 n t|tee'..fnp..'qhull p|tee'..fnchp...nul')--Windows特定部分为“>nul”
本地sp=_文件中的点(fnp)——源点
移除操作系统(fnp)
局部chp=_文件中的点_(fnchp)——凸包点
操作系统移除(fnchp)
本地m=#chp
如果m<3,则
io.stderr:write('凸壳由少于三个点组成')
返回
结束
局部极点={0.0,0.0}--极坐标原点相对于笛卡尔坐标原点的偏移
对于u,ipairs(chp)do中的点
杆[1]=杆[1]+点[1]
杆[2]=杆[2]+点[2]
结束
杆[1]=杆[1]/m
杆[2]=杆[2]/m
打印(“极=”,极[1],极[2])
本地chcc={}
对于u,ipairs(chp)do中的点
表.插入(chcc,{点[1]-极点[1],点[2]-极点[2]})
结束
局部θ_min=2.0*math.pi——笛卡尔坐标的横坐标和极坐标的横坐标之间的角度
局部rho_平均值=0.0
局部rho_max=0.0
局部chpc={}--{theta,rho}对
对于u3;,IPAIR中的点(chcc)do
局部rho=math.sqrt(点[1]*点[1]+点[2]*点[2])
局部θ=math.atan2(点[2],点[1])
如果θ<0.0,则--[-pi:pi]->[0:2*pi]
θ=θ+2.0*math.pi
结束
表.插入(chpc,{θ,rho})
如果θ_min>θ,则
θ_min=θ
结束
rho_平均值=rho_平均值+rho
如果rho_max[0:2]
局部ρ=点[2]/rho_平均值
表。移除(chpc,pos)
表.插入(chpc,pos,{theta,rho})
结束
表.排序(chpc,函数(lhs,rhs)返回lhs[1]function using()
    return error('using: ' .. arg[0] .. ' <number of points>')
end

function points_from_file(infile)
    local points = {}
    local infile = io.open(infile, 'r')
    local d = infile:read('*number')
    if d ~= 2 then
        error('dimensions is not two')
    end
    local n = infile:read('*number')
    while true do
        local x, y = infile:read('*number', '*number')
        if not x and not y then
            break
        end
        if not x or not y then
            error('wrong format of input file: line does not contain two coordinates')
        end
        table.insert(points, {x, y})
    end
    infile:close()
    if n ~= #points then
        error('second line not contain real count of points')
    end
    return points
end

if not arg then
    error("script should use as standalone")
end
if #arg ~= 1 then
    using()
end
local n = tonumber(arg[1])
if not n then
    using()
end
local bounding_box = math.sqrt(math.pi) / 2.0
local fnp = os.tmpname()
local fnchp = os.tmpname()
os.execute('rbox ' .. n .. ' B' .. bounding_box .. ' D2 n t | tee ' .. fnp .. ' | qhull p | tee ' .. fnchp .. ' > nul') -- Windows specific part is "> nul"
local sp = points_from_file(fnp) -- source points
os.remove(fnp)
local chp = points_from_file(fnchp) -- convex hull points
os.remove(fnchp)
local m = #chp
if m < 3 then
    io.stderr:write('convex hull consist of less than three points')
    return
end
local pole = {0.0, 0.0} -- offset of polar origin relative to cartesian origin
for _, point in ipairs(chp) do
    pole[1] = pole[1] + point[1]
    pole[2] = pole[2] + point[2]
end
pole[1] = pole[1] / m
pole[2] = pole[2] / m
print("pole = ", pole[1], pole[2])
local chcc = {}
for _, point in ipairs(chp) do
    table.insert(chcc, {point[1] - pole[1], point[2] - pole[2]})
end
local theta_min = 2.0 * math.pi -- angle between abscissa ort of cartesian and ort of polar coordinates
local rho_mean = 0.0
local rho_max = 0.0
local chpc = {} -- {theta, rho} pairs
for _, point in ipairs(chcc) do
    local rho = math.sqrt(point[1] * point[1] + point[2] * point[2])
    local theta = math.atan2(point[2], point[1])
    if theta < 0.0 then -- [-pi:pi] -> [0:2 * pi]
        theta = theta + 2.0 * math.pi
    end
    table.insert(chpc, {theta, rho})
    if theta_min > theta then
        theta_min = theta
    end
    rho_mean = rho_mean + rho
    if rho_max < rho then
        rho_max = rho
    end
end
theta_min = -theta_min
rho_mean = rho_mean / m
rho_max = rho_max / rho_mean
for pos, point in ipairs(chpc) do
    local theta = (point[1] + theta_min) / math.pi -- [0:2 * pi] -> [0:2]
    local rho = point[2] / rho_mean
    table.remove(chpc, pos)
    table.insert(chpc, pos, {theta, rho})
end
table.sort(chpc, function (lhs, rhs) return lhs[1] < rhs[1] end)
-- table.insert(chpc, {chpc[#chpc][1] - 2.0 * math.pi, chpc[#chpc][2]})
table.insert(chpc, {2.0, chpc[1][2]})
-- table.sort(chpc, function (lhs, rhs) return lhs[1] < rhs[1] end)

local solution = {}
solution.x = {}
solution.y = {}
for _, point in ipairs(chpc) do
    table.insert(solution.x, point[1])
    table.insert(solution.y, point[2])
end
solution.c = {}
for i, xi in ipairs(solution.x) do
    local c = solution.y[i]
    for j, xj in ipairs(solution.x) do
        if i ~= j then
            c = c / (xi - xj)
        end
    end
    solution.c[i] = c
end
function solution:monomial(i, x)
    local y = self.c[i]
    for j, xj in ipairs(solution.x) do
        if xj == x then
            if i == j then
                return self.y[i]
            else
                return 0.0
            end
        end
        if i ~= j then
            y = y * (x - xj)
        end
    end
    return y
end
function solution:polynomial(x)
    local y = self:monomial(1, x)
    for i = 2, #solution.y do
        y = y + self:monomial(i, x)
    end
    return y
end

local gnuplot = io.popen('gnuplot', 'w')

gnuplot:write('reset;\n')
gnuplot:write('set terminal wxt 1;\n')
gnuplot:write(string.format('set xrange [%f:%f];\n', -bounding_box, bounding_box))
gnuplot:write(string.format('set yrange [%f:%f];\n', -bounding_box, bounding_box))
gnuplot:write('set size square;\n')
gnuplot:write(string.format('set xtics %f;\n', 0.1))
gnuplot:write(string.format('set ytics %f;\n', 0.1))
gnuplot:write('set grid xtics ytics;\n')
gnuplot:write('plot "-" using 1:2 notitle with points, "-" using 1:2:3:4 notitle with vectors;\n')
for _, point in ipairs(sp) do
    gnuplot:write(string.format('%f %f\n', point[1], point[2]))
end
gnuplot:write('e\n')
for _, point in ipairs(chcc) do
    gnuplot:write(string.format('%f %f %f %f\n', pole[1], pole[2], point[1], point[2]))
end
gnuplot:write('e\n')
gnuplot:flush();

gnuplot:write('reset;\n')
gnuplot:write('set terminal wxt 2;\n')
gnuplot:write('set border 0;\n')
gnuplot:write('unset xtics;\n')
gnuplot:write('unset ytics;\n')
gnuplot:write('set polar;\n')
gnuplot:write('set grid polar;\n')
gnuplot:write('set trange [-pi:2 * pi];\n')
gnuplot:write(string.format('set rrange [-0:%f];\n', rho_max))
gnuplot:write('set size square;\n')
gnuplot:write('set view equal xy;\n')
-- gnuplot:write(string.format('set xlabel "%f";\n', rho_mean - 1.0))
gnuplot:write(string.format('set arrow 1 from 0,0 to %f,%f;\n', rho_max * math.cos(theta_min), rho_max * math.sin(theta_min)))
gnuplot:write(string.format('set label 1 " origin" at %f,%f left rotate by %f;\n', rho_max * math.cos(theta_min), rho_max * math.sin(theta_min), math.deg(theta_min)))
gnuplot:write('plot "-" using 1:2:3:4 notitle with vectors, "-" using 1:2 notitle with lines, "-" using 1:2 notitle with lines;\n')
for _, point in ipairs(chpc) do
    gnuplot:write(string.format('0 0 %f %f\n', point[1] * math.pi, point[2]))
end
gnuplot:write('e\n')
for _, point in ipairs(chpc) do
    gnuplot:write(string.format('%f %f\n', point[1] * math.pi, point[2]))
end
gnuplot:write('e\n')
do
    local points_count = 512
    local dx = 2.0 / points_count
    local x = 0.0
    for i = 1, points_count do
        gnuplot:write(string.format('%f %f\n', x * math.pi, solution:polynomial(x)))
        x = x + dx
    end
    gnuplot:write('e\n')
end
gnuplot:flush();

gnuplot:write('reset;\n')
gnuplot:write('set terminal wxt 3;\n')
gnuplot:write(string.format('set xrange [-1:2];\n'))
gnuplot:write(string.format('set yrange [0:2];\n'))
gnuplot:write(string.format('set size ratio %f;\n', rho_max / 3.0))
gnuplot:write(string.format('set xtics %f;\n', 0.5))
gnuplot:write(string.format('set ytics %f;\n', 0.5))
gnuplot:write('set grid xtics ytics;\n')
gnuplot:write(string.format('set arrow 1 nohead from 0,%f to 2,%f linetype 3;\n', chpc[1][2], chpc[1][2]))
gnuplot:write(string.format('set label 1 "glue points " at 0,%f right;\n', chpc[1][2]))
gnuplot:write('plot "-" using 1:2 notitle with lines, "-" using 1:2 notitle with lines;\n')
for _, point in ipairs(chpc) do
    gnuplot:write(string.format('%f %f\n', point[1], point[2]))
end
gnuplot:write('e\n')
do
    local points_count = 512
    local dx = 2.0 / points_count
    local x = 0.0
    for i = 1, points_count do
        gnuplot:write(string.format('%f %f\n', x, solution:polynomial(x)))
        x = x + dx
    end
    gnuplot:write('e\n')
end
gnuplot:flush();

os.execute('pause');
gnuplot:write('exit\n');
gnuplot:flush();
gnuplot:close()