【问题标题】:How to plot a smooth line through a sequence of points with gnuplot?如何使用gnuplot通过一系列点绘制一条平滑线?
【发布时间】:2021-09-03 08:14:06
【问题描述】:

让我们假设一系列点。如果我想通过这些点绘制一条平滑曲线, 我认为情节选项smooth 可以完成这项工作。

来自help smooth

语法:

  smooth {unique | frequency | fnormal | cumulative | cnormal | bins
                 | kdensity {bandwidth}
                 | csplines | acsplines | mcsplines | bezier | sbezier
                 | unwrap}

因此,贝塞尔曲线不会通过这些点,但某些splines 应该。 但是,在 gnuplot splines 中需要单调的 x 值。如果它们不是单调的,gnuplot 会使它们单调, (在这种情况下)产生不希望的结果。

如何通过点绘制平滑曲线?

示例:

### smooth curve through points?
reset session
set size ratio -1

$Data <<EOD
0 0
2 3
4 2
9 3
5 7
3 6
4 5
5 5
4 4
1 6
1 4
3 10
EOD

set key out
set ytics 1

plot $Data u 1:2 w lp pt 7            lc "red" dt 3 ti "data", \
        '' u 1:2 w l smooth bezier    lc "green"    ti "bézier", \
        '' u 1:2 w l smooth csplines  lc "orange"   ti "csplines", \
        '' u 1:2 w l smooth mcsplines lc "magenta"  ti "mcsplines", \
        '' u 1:2 w l smooth acsplines lc "yellow"   ti "acsplines"
### end of code

结果:smooth 选项都不会给出所需的结果)

【问题讨论】:

  • 仅供参考,“smooth path”已在开发版本中实现。
  • @binzo 感谢您提供此信息。很高兴知道。尚未检查开发版本。

标签: gnuplot curve smoothing


【解决方案1】:

这是一个有点冗长的解决方案,至少给出了一些可接受的结果。 它基于来自here 的代码。 该代码将创建一个包含三次贝塞尔曲线参数的表格。

如果有更简单的解决方案,请告诉我。

代码:

### plot smoothed curve through given points
reset session
set size ratio -1

$Data <<EOD
0 0
2 3
4 2
9 3
5 7
3 6
4 5
5 5
4 4
1 6
1 4
3 10
EOD

set angle degrees
Angle(dx,dy) = (_l=sqrt(dx**2 + dy**2), _l==0 ? NaN : dy/_l >= 0 ? acos(dx/_l) : -acos(dx/_l) )

# get points and angles of segments
set table $PointsAndAngles
    array Dummy[1]
    plot x1=x2=y1=y2=NaN $Data u (x0=x1,x1=x2):(y0=y1,y1=y2):(x2=$1):(y2=$2): \
         (dx1=x1-x0, dy1=y1-y0, dx2=x2-x1, dy2=y2-y1, \
         dx2==dx2 && dy2==dy2 && dx1==dx1 && dy1==dy1 ? \
         (d1=sqrt(dx1**2+dy1**2), d2=sqrt(dx2**2+dy2**2), \
         a2=Angle(dx2,dy2), a3=Angle(dx1/d1+dx2/d2,dy1/d1+dy2/d2)) : \
         (d2=sqrt(dx2**2+dy2**2), a2=Angle(dx2,dy2))) : (d2) w table
    plot Dummy u (x2):(y2):(NaN):(NaN):(a2):(NaN) w table
unset table

# create table with smooth parameters
# Cubic Bézier curves function with t[0:1] as parameter
# p0: start point, p1: 1st ctrl point, p2: 2nd ctrl point, p3: endpoint
# a0, a3: angles
# r0, r3: radii
#n   p0x   p0y    a0   r0   p3x   p3y   a3   r3     color
set print $SmoothLines
    do for [i=1:|$PointsAndAngles|-1] {
        p0x = word($PointsAndAngles[i],1)
        p0y = word($PointsAndAngles[i],2)
        a0  = word($PointsAndAngles[i],5)
        r0  = 0.3
        p3x = word($PointsAndAngles[i],3)
        p3y = word($PointsAndAngles[i],4)
        a3  = word($PointsAndAngles[i+1],5)
        r3  = 0.3
        color = 0x0000ff
        print sprintf("%d   %s %s %s %g   %s %s %s %g   %d %d", \
                       i, p0x, p0y, a0, r0, p3x, p3y, a3, r3, color)
    }
set print

p0v(n,v) = word($SmoothLines[n],2+v)       # v=0 --> x, v=1 --> y
a0(n)    = word($SmoothLines[n],4)
r0(n)    = word($SmoothLines[n],5)
p3v(n,v) = word($SmoothLines[n],6+v)       # v=0 --> x, v=1 --> y
a3(n)    = word($SmoothLines[n],8)
r3(n)    = word($SmoothLines[n],9)
color(n) = int(word($SmoothLines[n],10))

Length(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)
d03(n)   = Length(p0v(n,0),p0v(n,1),p3v(n,0),p3v(n,1))
p1v(n,v) = p0v(n,v) + (v==0 ? r0(n)*d03(n)*cos(a0(n)) : r0(n)*d03(n)*sin(a0(n)) )
p2v(n,v) = p3v(n,v) - (v==0 ? r3(n)*d03(n)*cos(a3(n)) : r3(n)*d03(n)*sin(a3(n)) )

# parametric cubic Bézier:
pv(n,v,t) = t**3 * (  -p0v(n,v) + 3*p1v(n,v) - 3*p2v(n,v) + p3v(n,v)) + \
            t**2 * ( 3*p0v(n,v) - 6*p1v(n,v) + 3*p2v(n,v)           ) + \
            t    * (-3*p0v(n,v) + 3*p1v(n,v)                        ) + p0v(n,v)

set key noautotitles
set ytics 1

plot $Data u 1:2 w lp pt 7 lc "red" dt 3 ti "data", \
     for [i=2:|$SmoothLines|] [0:1] '+' u (pv(i,0,$1)):(pv(i,1,$1)) w l lc rgb color(i), \
     keyentry w l lc "blue" ti "Cubic Bézier through points"
### end of code

结果:

【讨论】:

    【解决方案2】:

    smooth path,正如@binzo 在 cmets 中所写的那样,可能会给你想要的结果。 我想分享一种我为个人需求开发的替代方法,它与您自己的答案类似,手动定义平滑函数并迭代所有点。

    这里我选择了一条连接两个连续点并且水平起点和终点的样条线(即它在 (x1,y1) 和 (x2,y2) 处的导数为 0)

    spline(x,x1,y1,x2,y2) = y1+x1**2*(y1-y2)*(3*x2-x1)/(x1-x2)**3 + 6*x1*x2*(y2-y1)/(x1-x2)**3*abs(x) + 3*(y1-y2)*(x1+x2)/(x1-x2)**3*abs(x)**2 + 2*(y2-y1)/(x1-x2)**3*abs(x)**3
    

    样条曲线将在每对连续的点之间迭代绘制。为此,可以方便地将数据块存储为数组以供以后索引:

    $Data <<EOD
    0 0
    2 3
    4 2
    9 3
    5 7
    3 6
    4 5
    5 5
    4 4
    1 6
    1 4
    3 10
    EOD
    
    stats $Data noout
    array xvals[STATS_records]
    array yvals[STATS_records]
    do for [i=1:|xvals|] {
      stats $Data every ::i-1::i-1 u (xvals[i]=$1,yvals[i]=$2) noout
    }
    

    在迭代图中,每条样条仅在其各自的 [x1:x2] 范围内绘制。为了比较,还包括smooth path

    plot $Data w lp pt 7 lc "red" dt 3,\
     for [i=1:|xvals|-1] [xvals[i]:xvals[i+1]] spline(x,xvals[i],yvals[i],xvals[i+1],yvals[i+1]) lc "blue" not,\
     keyentry w l lc "blue" t "user-defined splines",\
     $Data smooth path lc "black" t "smooth path"
    

    或者,可以绕过转换为数组并直接访问数据块的元素。例如。 $Data[2] 将第二行作为字符串,可以被word() 分割。为了最终得到正确的浮点运算(而不是整数),数字必须用real() 包裹,这使得绘图命令更加冗长:

    plot $Data w lp pt 7 lc "red" dt 3,\
     for [i=1:|$Data|-1] [word($Data[i],1):word($Data[i+1],1)] spline(x, real(word($Data[i],1)), real(word($Data[i],2)), real(word($Data[i+1],1)), real(word($Data[i+1],2)) ) w l lc "blue" not,\
     keyentry w l lc "blue" t "user-defined splines",\
     $Data smooth path lc "black" t "smooth path"
    

    这两个平滑选项中哪个更好取决于您最终想要实现的目标。显然,当两个连续的点具有相同的 x 值时,这种样条尝试会失败。

    【讨论】:

    • 感谢您的建议。我还没有研究样条线。就我而言,我正在寻找一条在某些点上不会“改变方向”的平滑曲线,即连续一阶导数。我很确定您可以通过某种方式使您的方法也满足此要求,并且还可以用于具有相同 x 值的两个连续点。
    • @theozh 严格来说,我的答案中的一阶导数已经是连续的;-) 但是,在我付出太多努力之前,您能否指定您想要哪种类型的平滑/路径,或者最终曲线的解析表达式应该如何定义?或者smooth path 已经满足您的需求?
    • 你是对的!抱歉,我的意思是不改变尖角的路径方向。你会如何用数学来描述这个? gnuplot 5.5 中的smooth path 肯定是最简单且足够的方法,尽管我比smooth path 更喜欢贝塞尔曲线的形状,并且您有一些参数可以稍微塑造曲线。也许你还有一些额外的参数smooth path...我还没有安装gnuplot 5.5。
    • @theozh 我不知道如何在数学上表达“方向”,因为最终路径不是一个函数(一个 x 有几个 y 值)。我的解决方案提供分段函数,其中拐角处的二阶导数不连续。对于path,衍生工具的概念不适用。无论如何,我还是想澄清一下:对于每两个连续的点,你想画一个贝塞尔曲线(因为贝塞尔曲线只经过第一个和最后一个点),对吧?您定义中间控制点的概念是什么?有什么限制?
    • 是的,“二阶导数连续”,这就是我要找的。对于平滑路径,要求是您可以在每个点创建切线(不能在尖角处创建切线)。在我的 Bézier 方法中,1. 路径点的角度是 2 个相邻段的角度的平均值。 2.控制点是相对于路径中的两个连续点和1的平均角度计算的。一些更详细的解释在这里:stackoverflow.com/a/60389081。控制点没有特殊限制。 r=0.3 给出合理的平滑曲线。
    猜你喜欢
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-09
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多