gnuplot 等高线图阴影线

gnuplot contour plot hatched lines

我正在使用 gnuplot 绘制多个函数的等高线图。这是针对优化问题。 我有 3 个功能:

  1. f(x,y)
  2. g1(x,y)
  3. g2(x,y)

g1(x,y)g2(x,y) 都是约束条件,想在 f(x,y).

的等高线图之上绘制

这里是课本示例:

这是我在 gnuplot 中复制它的尝试,感谢@theozh。

### contour lines with labels
reset session

f(x,y)=(x**2+y-11)**2+(x+y**2-7)**2
g1(x,y)=(x-5)**2+y**2
g2(x,y) = 4*x+y

set xrange [0:6]
set yrange [0:6]
set isosample 250, 250
set key outside

set contour base
set cntrparam levels disc 10,30,75,150,300,500,850,1500 
unset surface
set table $Contourf
    splot f(x,y)
unset table

set contour base
set cntrparam levels disc 26
unset surface
set table $Contourg1
    splot g1(x,y)
unset table

set contour base
set cntrparam levels disc 20
unset surface
set table $Contourg2
    splot g2(x,y)
unset table

set style textbox opaque noborder

set datafile commentschar " "
plot for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5)
replot $Contourg1 u 1:2:(1) skip 5 index 0 w l lw 4 lc 0 title columnheader(5)
replot $Contourg2 u 1:2:(1) skip 5 index 0 w l lw 4 lc 0 title columnheader(5)

我想复制gnuplot例子中的教科书图片。如何在函数 g1g2 上画阴影线,上图中的粗黑线。

@theozh 在下面提供了一个很好的解决方案。但是,该方法不适用于陡峭的曲线。例如

reset session
unset key

set size square

g(x,y) = -0.8-1/x**3+y

set xrange [0:4]
set yrange [0:4]
set isosample 250, 250
set key off

set contour base
unset surface

set cntrparam levels disc 0
set table $Contourg
    splot g(x,y)
unset table

set angle degree
set datafile commentschar " "

plot $Contourg u 1:2 skip 5 index 0 w l lw 2 lc 0 title columnheader(5)

set style fill transparent pattern 4
replot $Contourg u 1:2:(+0.2) skip 5 index 0 w filledcurves lc 0 notitle 

得到下图。有没有办法使用不同的偏移量,例如 x < 1.3 的偏移 x 值和 x > 1.3 的偏移 y 值。这将产生更好的填充曲线。可以在这里找到我正在寻找的 matlab 实现:https://www.mathworks.com/matlabcentral/fileexchange/29121-hatched-lines-and-contours.

在复制@Ethans 程序时,我得到以下信息,与@Ethan 相比,dashtype 相对较粗不知道为什么,我使用的是 gnuplot v5.2 和 wxt 终端。

当我复制@theozh 代码时,它工作得很好,除了封闭的轮廓,不知道为什么?见下面的例子:

f(x,y)=x*exp(-x**2-y**2)+(x**2+y**2)/20
g1(x,y)= x*y/2+(x+2)**2+(y-2)**2/2-2

set xrange [-7:7]
set yrange [-7:7]
set isosample 250, 250
set key outside

set contour base
unset surface

set cntrparam levels disc 4,3.5,3,2.5,2,1.5,1,0.5,0 
set table $Contourf
    splot f(x,y)
unset table

set cntrparam levels disc 0
set table $Contourg1
    splot g1(x,y)
unset table

# create some extra offset contour lines
# macro for setting contour lines
ContourCreate = '\
    set cntrparam levels disc Level; \
    set table @Output; \
        splot @Input; \
    unset table'

Level = 0.45
Input = 'g1(x,y)'
Output = '$Contourg1_ext'
@ContourCreate


# Macro for ordering the datapoints of the contour lines which might be split
ContourOrder = '\
stats @DataIn skip 6 nooutput; \
N = STATS_blank-1; \
set table @DataOut; \
    do for [i=N:0:-1] { plot @DataIn u 1:2 skip 5 index 0 every :::i::i with table }; \
unset table'

DataIn = '$Contourg1'
DataOut = '$Contourg1_ord'
@ContourOrder

DataIn = '$Contourg1_ext'
DataOut = '$Contourg1_extord'
@ContourOrder


# Macro for reversing a datablock
ContourReverse = '\
set print @DataOut; \
    do for [i=|@DataIn|:1:-1] { print @DataIn[i]}; \
set print'

DataIn = '$Contourg1_extord'
DataOut = '$Contourg1_extordrev'
@ContourReverse

# Macro for adding datablocks
ContourAdd = '\
set print @DataOut; \
    do for [i=|@DataIn1|:1:-1] { print @DataIn1[i]}; \
    do for [i=|@DataIn2|:1:-1] { print @DataIn2[i]}; \
set print'

DataIn1 = '$Contourg1_ord'
DataIn2 = '$Contourg1_extordrev'
DataOut = '$Contourg1_add'
@ContourAdd


set style fill noborder 
set datafile commentschar " "
plot \
    for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5), \
    $Contourg1 u 1:2 skip 5 index 0 w l lw 2 lc 0 title columnheader(5), \
    $Contourg1_add u 1:2 w filledcurves fs transparent pattern 5 lc rgb "black" notitle

我不知道 gnuplot 中的一个功能会生成这样的阴影线。 一种解决方法如下:将曲线稍微移动一些值并填充它 with filledcurves 和填充图案。但是,这仅在曲线是直线或不太弯曲时才有效。 不幸的是,gnuplot 中的填充图案数量也非常有限(请参阅 )并且它们不可自定义。 您需要使用偏移值和阴影填充图案。

代码:

### contour lines with hatched side
reset session

f(x,y)=(x**2+y-11)**2+(x+y**2-7)**2
g1(x,y)=(x-5)**2+y**2
g2(x,y) = 4*x+y

set xrange [0:6]
set yrange [0:6]
set isosample 250, 250
set key outside

set contour base
unset surface

set cntrparam levels disc 10,30,75,150,300,500,850,1500 
set table $Contourf
    splot f(x,y)
unset table

set cntrparam levels disc 26
set table $Contourg1
    splot g1(x,y)
unset table

set cntrparam levels disc 20
set table $Contourg2
    splot g2(x,y)
unset table
set angle degree
set datafile commentschar " "
plot for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5)
replot $Contourg1 u 1:2 skip 5 index 0 w l lw 4 lc 0 title columnheader(5)
replot $Contourg2 u 1:2 skip 5 index 0 w l lw 4 lc 0 title columnheader(5)

set style fill transparent pattern 5
replot $Contourg1 u 1:2:(+0.2) skip 5 index 0 w filledcurves lc 0 notitle
set style fill transparent pattern 4
replot $Contourg2 u 1:2:(+0.5) skip 5 index 0 w filledcurves lc 0 notitle
### end of code

结果:

加法:

使用 gnuplot,您可能会在大多数时候找到解决方法。这只是您允许它变得多么复杂或丑陋的问题。 对于如此陡峭的函数,请使用以下 "trick"。基本思想很简单:采用原始曲线和移动后的曲线,将这两条曲线组合起来,将它们绘制成填充图。但是你必须反转其中一条曲线(类似于我之前描述的:)。

然而,这里出现了一个新的"problem"。无论出于何种原因,等高线数据由几个由空行分隔的块组成,并且它不是 x 中的连续序列。我不知道为什么,但这就是 gnuplot 创建的轮廓线。为了获得正确的顺序,将数据绘制到一个新的数据块 $ContourgOnePiece 中,从最后一个块 (every :::N::N) 到第一个块 (every :::0::0)。通过stats $ContourgSTATS_blank确定这些"blocks"的个数。对移动到 $ContourgShiftedOnePiece 的等高线执行相同的操作。 然后将这两个数据块逐行打印到一个新的数据块 $ClosedCurveHatchArea 中,从而将它们组合起来,实际上您在其中反转了其中一个。 此过程适用于严格单调的曲线,但我猜你会遇到振荡或闭合曲线的问题。但我想可能还有其他一些奇怪的解决方法。 我承认,这不是 "clean" 和 "robust" 的解决方案,但它确实有效。

代码:

### lines with one hatched side
reset session
set size square

g(x,y) = -0.8-1/x**3+y

set xrange [0:4]
set yrange [0:4]
set isosample 250, 250
set key off

set contour base
unset surface

set cntrparam levels disc 0
set table $Contourg
    splot g(x,y)
unset table

set angle degree
set datafile commentschar " "

# determine how many pieces $Contourg has
stats $Contourg skip 6 nooutput  # skip 6 lines
N = STATS_blank-1                # number of empty lines

set table $ContourgOnePiece
    do for [i=N:0:-1] {
        plot $Contourg u 1:2 skip 5 index 0 every :::i::i with table
    }
unset table
# do the same thing with the shifted $Contourg
set table $ContourgShiftedOnePiece
    do for [i=N:0:-1] {
        plot $Contourg u (+0.1):(+0.1):2 skip 5 index 0 every :::i::i with table
    }
unset table
# add the two curves but reverse the second of them
set print $ClosedCurveHatchArea append
    do for [i=1:|$ContourgOnePiece|:1] {
        print $ContourgOnePiece[i]
    }
    do for [i=|$ContourgShiftedOnePiece|:1:-1] {
        print $ContourgShiftedOnePiece[i]
    }
set print

plot $Contourg u 1:2 skip 5 index 0 w l lw 2 lc 0 title columnheader(5)
set style fill transparent pattern 5 noborder
replot $ClosedCurveHatchArea u 1:2 w filledcurves lc 0
### end of code

结果:

加法2:

实际上,我喜欢@Ethan 创建额外水平轮廓线的方法。只要梯度不是太大,这种方法就可以很好地工作。否则,您可能会看到第二条等高线的明显变形(请参见下面的红色曲线)。但是,在上面使用 g1g2 的示例中,您不会注意到差异。另一个优点是影线垂直于曲线。缺点是您可能会打断常规模式。

在 x and/or y 和填充区域中对原始曲线进行微小偏移的解决方案不适用于振荡线或闭合线。

下方的黑色阴影曲线是这些方法的混合。

程序:

  1. 创建一条等高线
  2. 创建扩展 (ext) 或移动 (shf) 轮廓线(通过新轮廓值或通过移动现有轮廓线)
  3. 等高线顺序(ord)
  4. 反转轮廓线 (rev)
  5. 添加有序 (ord) 和扩展、有序、反转 (extordrev)
  6. filledcuves
  7. 绘制添加的轮廓线(添加)

注意:如果你想将等高线移动 x,y 你必须先排序然后移动它,否则宏 @ContourOrder 不能再排序了。

你看,它会变得复杂。综上所述,目前一共有三种做法:

(a) 额外的等高线和粗虚线(@Ethan)

pro: 简而言之,适用于振荡和闭合曲线; con: 如果梯度大则不好

(b) x,y 移动轮廓线和阴影填充曲线 (@theozh)

pro:参数少,画面清晰; con: 冗长,只有 4 个填充图案)

(c) 数据点的导数 (@Dan Sp.)

pro: 倾斜填充图案可能具有灵活性; con: 需要导数(如果没有函数但数据点,则为数值),模式取决于规模

黑色曲线实际上是 (a) 和 (b) 的混合。 蓝色曲线是 (b)。 (a) 和 (b) 在红色曲线上都不会很好地工作。也许(c)? 您可以考虑进一步混合这些方法……但这可能也很冗长。

代码:

### contour lines with hashed side
set term wxt butt
reset session

f(x,y)=(x**2+y-11)**2+(x+y**2-7)**2
g1(x,y)=(x-5)**2+y**2
g2(x,y) = 4*x+y
g3(x,y) = -0.8-1/x**3+y

set xrange [0:6]
set yrange [0:6]
set isosample 250, 250
set key outside

set contour base
unset surface

set cntrparam levels disc 10,30,75,150,300,500,850,1500 
set table $Contourf
    splot f(x,y)
unset table

set cntrparam levels disc 26
set table $Contourg1
    splot g1(x,y)
unset table

set cntrparam levels disc 20
set table $Contourg2
    splot g2(x,y)
unset table

set cntrparam levels disc 0
set table $Contourg3
    splot g3(x,y)
unset table


# create some extra offset contour lines
# macro for setting contour lines
ContourCreate = '\
    set cntrparam levels disc Level; \
    set table @Output; \
        splot @Input; \
    unset table'

Level = 27.5
Input = 'g1(x,y)'
Output = '$Contourg1_ext'
@ContourCreate

Level = 20.5
Input = 'g2(x,y)'
Output = '$Contourg2_ext'
@ContourCreate

Level = 10
Input = 'f(x,y)'
Output = '$Contourf0'
@ContourCreate

Level = 13
Input = 'f(x,y)'
Output = '$Contourf0_ext'
@ContourCreate


# Macro for ordering the datapoints of the contour lines which might be split
ContourOrder = '\
stats @DataIn skip 6 nooutput; \
N = STATS_blank-1; \
set table @DataOut; \
    do for [i=N:0:-1] { plot @DataIn u 1:2 skip 5 index 0 every :::i::i with table }; \
unset table'

DataIn = '$Contourg1'
DataOut = '$Contourg1_ord'
@ContourOrder

DataIn = '$Contourg1_ext'
DataOut = '$Contourg1_extord'
@ContourOrder

DataIn = '$Contourg2'
DataOut = '$Contourg2_ord'
@ContourOrder

DataIn = '$Contourg2_ext'
DataOut = '$Contourg2_extord'
@ContourOrder

DataIn = '$Contourg3'
DataOut = '$Contourg3_ord'
@ContourOrder

set table $Contourg3_ordshf
    plot $Contourg3_ord u (+0.15):(+0.15) w table   # shift the curve
unset table

DataIn = '$Contourf0'
DataOut = '$Contourf0_ord'
@ContourOrder

DataIn = '$Contourf0_ext'
DataOut = '$Contourf0_extord'
@ContourOrder

# Macro for reversing a datablock
ContourReverse = '\
set print @DataOut; \
    do for [i=|@DataIn|:1:-1] { print @DataIn[i]}; \
set print'

DataIn = '$Contourg1_extord'
DataOut = '$Contourg1_extordrev'
@ContourReverse

DataIn = '$Contourg2_extord'
DataOut = '$Contourg2_extordrev'
@ContourReverse

DataIn = '$Contourg3_ordshf'
DataOut = '$Contourg3_ordshfrev'
@ContourReverse

DataIn = '$Contourf0_extord'
DataOut = '$Contourf0_extordrev'
@ContourReverse

# Macro for adding datablocks
ContourAdd = '\
set print @DataOut; \
    do for [i=|@DataIn1|:1:-1] { print @DataIn1[i]}; \
    do for [i=|@DataIn2|:1:-1] { print @DataIn2[i]}; \
set print'

DataIn1 = '$Contourg1_ord'
DataIn2 = '$Contourg1_extordrev'
DataOut = '$Contourg1_add'
@ContourAdd

DataIn1 = '$Contourg2_ord'
DataIn2 = '$Contourg2_extordrev'
DataOut = '$Contourg2_add'
@ContourAdd

DataIn1 = '$Contourg3_ord'
DataIn2 = '$Contourg3_ordshfrev'
DataOut = '$Contourg3_add'
@ContourAdd

DataIn1 = '$Contourf0_ord'
DataIn2 = '$Contourf0_extordrev'
DataOut = '$Contourf0_add'
@ContourAdd

set style fill noborder 
set datafile commentschar " "
plot \
    for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5), \
    $Contourg1 u 1:2 skip 5 index 0 w l lw 3 lc 0 title columnheader(5), \
    $Contourg2 u 1:2 skip 5 index 0 w l lw 3 lc 0 title columnheader(5), \
    $Contourg3 u 1:2 skip 5 index 0 w l lw 3 lc 0 title columnheader(5), \
    $Contourg1_add u 1:2 w filledcurves fs transparent pattern 4 lc rgb "black" notitle, \
    $Contourg2_add u 1:2 w filledcurves fs transparent pattern 5 lc rgb "black" notitle, \
    $Contourg3_add u 1:2 w filledcurves fs transparent pattern 5 lc rgb "blue" notitle, \
    $Contourf0_add u 1:2 w filledcurves fs transparent pattern 6 lc rgb "red" notitle, \
### end of code

结果:

加法3:

如果你用 filledcurves 绘制一条线,我猜 gnuplot 会用一条直线连接第一个点和最后一个点并填充封闭区域。 在您的 circle/ellipse 示例中,外曲线在图表的顶部边界处被切割。我想这就是脚本在这种情况下不起作用的原因。您必须确定外曲线开始和结束的这些点,并安排连接的曲线,使这些点成为起点和终点。 你看它越来越好化...

下面应该说明它应该如何工作:在你开始的地方画一条曲线,例如使用从点 1 到 100 的内部曲线,然后再次添加内部曲线的点 1,继续使用外部曲线的点 1(方向相反)到点 100,然后再次添加外部曲线的点 1。然后 gnuplot 将通过连接外曲线的点 1 和内曲线的点 1 来闭合曲线。然后将其绘制为填充填充图案。

顺便说一下,如果您将函数 g1(x,y) 更改为 g1(x,y)= x*y/2+(x+2)**2+(y-1.5)**2/2-2 (注意区别 y-1.5 而不是 y-2)一切正常。见下文。

代码:

### Hatching on a closed line
reset session

f(x,y)=x*exp(-x**2-y**2)+(x**2+y**2)/20
g1(x,y)= x*y/2+(x+2)**2+(y-1.5)**2/2-2

set xrange [-7:7]
set yrange [-7:7]
set isosample 250, 250
set key outside

set contour base
unset surface

set cntrparam levels disc 4,3.5,3,2.5,2,1.5,1,0.5,0 
set table $Contourf
    splot f(x,y)
unset table

set cntrparam levels disc 0
set table $Contourg1
    splot g1(x,y)
unset table

# create some extra offset contour lines
# macro for setting contour lines
ContourCreate = '\
    set cntrparam levels disc Level; \
    set table @Output; \
        splot @Input; \
    unset table'

Level = 1
Input = 'g1(x,y)'
Output = '$Contourg1_ext'
@ContourCreate

# Macro for ordering the datapoints of the contour lines which might be split
ContourOrder = '\
    stats @DataIn skip 6 nooutput; \
    N = STATS_blank-1; \
    set table @DataOut; \
        do for [i=N:0:-1] { plot @DataIn u 1:2 skip 5 index 0 every :::i::i with table }; \
    unset table'

DataIn = '$Contourg1'
DataOut = '$Contourg1_ord'
@ContourOrder

DataIn = '$Contourg1_ext'
DataOut = '$Contourg1_extord'
@ContourOrder

# Macro for reversing a datablock
ContourReverse = '\
set print @DataOut; \
    do for [i=|@DataIn|:1:-1] { print @DataIn[i]}; \
set print'

DataIn = '$Contourg1_extord'
DataOut = '$Contourg1_extordrev'
@ContourReverse

# Macro for adding datablocks
ContourAdd = '\
set print @DataOut; \
    do for [i=|@DataIn1|:1:-1] { print @DataIn1[i]}; \
    do for [i=|@DataIn2|:1:-1] { print @DataIn2[i]}; \
set print'

DataIn2 = '$Contourg1_ord'
DataIn1 = '$Contourg1_extordrev'
DataOut = '$Contourg1_add'
@ContourAdd

set style fill noborder 
set datafile commentschar " "
plot \
    for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5), \
    $Contourg1 u 1:2 skip 5 index 0 w l lw 2 lc 0 title columnheader(5), \
    $Contourg1_add u 1:2 w filledcurves fs transparent pattern 5 lc rgb "black" notitle
### end of code

结果:

如果你真的想要画出好的阴影线,你可以画一大堆没有头的箭头。

下面的示例计算了循环中每个影线标记的位置和斜率,使它们几乎垂直于绘制的线(以数值精度)。它还沿线将它们隔开(再次达到基本的数值精度,但对于绘图来说已经足够好了。

reset
set grid
set sample 1000

set xrange [0:6]
set yrange [0:6]

# First, plot the actual curve
plot 1/log(x)

# Choose a length for your hatch marks, this will 
# depend on your axis scale.
Hlength = 0.2

# Choose a distance along the curve for the hatch marks. Again
# will depend on you axis scale.
Hspace = 0.5

# Identify one end of the curve on the plot, set x location for
# first hatch mark.
# For this case, it is when 1/log(x) = 4
x1point = exp(0.25)
y1point = 1/log(x1point)

# Its just easier to guess how many hatch marks you need instead
# of trying to compute the length of the line.
do for [loop=1:14] {

# Next, find the slope of the function at this point.
# If you have the exact derivative, use that.
# This example assumes you perhaps have a user defined funtion
# that is likely too difficult to get a derivative so it 
# increments x by a small amount to numerically compute it
slope = (1/log(x1point+0.001)-y1point)/(0.001)
#slopeAng = atan2(slope)
slopeAng = atan2((1/log(x1point+.001)-y1point),0.001)

# Also find the perpendicular to this slope
perp = 1/slope
# Get angle of perp from horizontal
perpAng = atan(perp)


# Draw a small hatch mark at this point
x2point = x1point + Hlength*cos(perpAng)
y2point = y1point - Hlength*sin(perpAng)
# The hatch mark is just an arrow with no heads
set arrow from x1point,y1point to x2point,y2point nohead

# Move along the curve approximately a distance of Hspace
x1point = x1point + Hspace*cos(slopeAng)
y1point = 1/log(x1point)

# loop around to do next hatch mark
}

replot

你会得到这样的东西

请注意,您可以轻松调整阴影线长度和它们之间的间距。此外,如果您的 x 轴和 y 轴具有明显不同的比例,则缩放箭头的 x 或 y 长度并不难,因此它们 'look' 等长。


编辑:

绘制等值线图会更加复杂。我已经完成了你需要做的事情。我在你想要约束的轮廓级别解析了你的 g1 和 g2 函数,并命名了两个新函数 g1_26 和 g2_20 并为每个函数求解了 y。

我还发现,当斜率的符号改变时,上面的简单程序中的影线标记会改变边,所以我在计算影线标记的 x2 和 y2 点时添加了 sgn(slope) 并且还添加了翻转变量,以便您可以轻松控制绘制影线标记的线的哪一侧。这是代码:

### contour lines with labels
reset session

f(x,y)=(x**2+y-11)**2+(x+y**2-7)**2
g1(x,y)=(x-5)**2+y**2
g2(x,y) = 4*x+y

set xrange [0:6]
set yrange [0:6]
set isosample 250, 250
set key outside

set contour base
set cntrparam levels disc 10,30,75,150,300,500,850,1500 
unset surface
set table $Contourf
    splot f(x,y)
unset table

set contour base
set cntrparam levels disc 26
unset surface
set table $Contourg1
    splot g1(x,y)
unset table

set contour base
set cntrparam levels disc 20
unset surface
set table $Contourg2
    splot g2(x,y)
unset table

set style textbox opaque noborder

set datafile commentschar " "
plot for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5)
replot $Contourg1 u 1:2:(1) skip 5 index 0 w l lw 4 lc 0 title columnheader(5)
replot $Contourg2 u 1:2:(1) skip 5 index 0 w l lw 4 lc 0 title columnheader(5)

###############################
# Flip should be -1 or 1 depending on which side you want hatched.
flip = -1

# put hatches on g1
# Since your g1 constraint is at g1(x,y) = 26, lets
# get new formula for this specific line.
#g1(x,y)=(x-5)**2+y**2 = 26
g1_26(x) = sqrt( -(x-5)**2 + 26)

# Choose a length for your hatch marks, this will 
# depend on your axis scale.
Hlength = 0.15

# Choose a distance along the curve for the hatch marks. Again
# will depend on you axis scale.
Hspace = 0.2

# Identify one end of the curve on the plot, set x location for
# first hatch mark.
x1point = 0
y1point = g1_26(x1point)

# Its just easier to guess how many hatch marks you need instead
# of trying to compute the length of the line.
do for [loop=1:41] {

# Next, find the slope of the function at this point.
# If you have the exact derivative, use that.
# This example assumes you perhaps have a user defined funtion
# that is likely too difficult to get a derivative so it 
# increments x by a small amount to numerically compute it
slope = (g1_26(x1point+0.001)-y1point)/(0.001)
#slopeAng = atan2(slope)
slopeAng = atan2((g1_26(x1point+.001)-y1point),0.001)

# Also find the perpendicular to this slope
perp = 1/slope
# Get angle of perp from horizontal
perpAng = atan(perp)


# Draw a small hatch mark at this point
x2point = x1point + flip*sgn(slope)*Hlength*cos(perpAng)
y2point = y1point - flip*sgn(slope)*Hlength*sin(perpAng)
# The hatch mark is just an arrow with no heads
set arrow from x1point,y1point to x2point,y2point nohead lw 2

# Move along the curve approximately a distance of Hspace
x1point = x1point + Hspace*cos(slopeAng)
y1point = g1_26(x1point)

# loop around to do next hatch mark
}

###############################
# Flip should be -1 or 1 depending on which side you want hatched.
flip = -1

# put hatches on g2
# Since your g2 constraint is at g2(x,y) = 20, lets
# get new formula for this specific line.
#g2(x,y) = 4*x+y = 20
g2_20(x) = 20 - 4*x

# Choose a length for your hatch marks, this will 
# depend on your axis scale.
Hlength = 0.15

# Choose a distance along the curve for the hatch marks. Again
# will depend on you axis scale.
Hspace = 0.2

# Identify one end of the curve on the plot, set x location for
# first hatch mark.
x1point =3.5
y1point = g2_20(x1point)

# Its just easier to guess how many hatch marks you need instead
# of trying to compute the length of the line.
do for [loop=1:32] {

# Next, find the slope of the function at this point.
# If you have the exact derivative, use that.
# This example assumes you perhaps have a user defined funtion
# that is likely too difficult to get a derivative so it 
# increments x by a small amount to numerically compute it
slope = (g2_20(x1point+0.001)-y1point)/(0.001)
slopeAng = atan2((g2_20(x1point+.001)-y1point),0.001)

# Also find the perpendicular to this slope
perp = 1/slope
# Get angle of perp from horizontal
perpAng = atan(perp)


# Draw a small hatch mark at this point
x2point = x1point + flip*sgn(slope)*Hlength*cos(perpAng)
y2point = y1point - flip*sgn(slope)*Hlength*sin(perpAng)
# The hatch mark is just an arrow with no heads
set arrow from x1point,y1point to x2point,y2point nohead lw 2

# Move along the curve approximately a distance of Hspace
x1point = x1point + Hspace*cos(slopeAng)
y1point = g2_20(x1point)

# loop around to do next hatch mark
}

replot

结果如下:

另一种可能性是使用自定义破折号模式,如下所示: 顺便说一句,用"replot"来组成一个数字几乎是不正确的。

# Additional contour levels displaced by 0.2 from the original
set contour base
set cntrparam levels disc 20.2
unset surface
set table $Contourg2d
    splot g2(x,y)
unset table

set contour base
set contour base
set cntrparam levels disc 26.2
unset surface
set table $Contourg1d
    splot g1(x,y)
unset table

set linetype 101 lc "black" linewidth 5 dashtype (0.5,5)

plot for [i=1:8] $Contourf u 1:2:(i) skip 5 index i-1 w l lw 1.5 lc var title columnheader(5), \
        $Contourg1 u 1:2:(1) skip 5 index 0 w l lw 1 lc "black" title columnheader(5), \
        $Contourg2 u 1:2:(1) skip 5 index 0 w l lw 1 lc "black" title columnheader(5), \
        $Contourg1d u 1:2:(1) skip 5 index 0 w l linetype 101 notitle, \
        $Contourg2d u 1:2:(1) skip 5 index 0 w l linetype 101 notitle

已修改以显示等高线偏移的使用,以便破折号仅在线的一侧。

这是您(和我)所希望的解决方案。 您只需输入阴影参数:TiltAngle 度数(>0°:曲线方向左侧,<0° 右侧),以及 HatchLengthHatchSeparation 像素。该过程变得有点冗长,但它可以满足您的要求。我已经用 gnuplot 5.2.6 和 wxtqt 终端测试了它。您需要确定其他终端的比例因子。

程序的主要作用:

  1. 确定数据输入曲线的两个连续点之间的夹角
  2. 根据HatchSeparation
  3. 沿着曲线插入数据点
  4. 缩放所有独立于图形比例和终端大小的东西(然而,这需要一个虚拟 plot x 来获取 gnuplot 变量 GPVAL_X_MAXGPVAL_X_MINGPVAL_TERM_XMAX, GPVAL_TERM_XMIN.

限制:

  • 不适用于对数轴
  • 不适用于输入数据块中的注释行或空行

如果您将它与等高线一起使用,您必须确保等高线数据点的顺序正确(请参阅我的第一个回答中的评论)。

为了使代码更清晰,生成测试圆 tbCreateCircleData.gpp 和填充图案 tbHatchLineGeneration.gpp 的过程被放入单独的 gnuplot 过程文件中。 这些子过程中的变量以 CC_HLG_ 为前缀,以避免在您将其与现有主绘图例程一起使用时可能出现的变量名冲突。 玩得开心!欢迎提出意见和改进!

子程序:"tbCreateCircleData.gpp"

### create circle data
# example usage: call "tbCreateCircleData.gpp "$OutputData" 0.5 0.5 1.0 0 360 180
# Note: negative numbers have to be put into ""
CC_outputdata  = ARG1
CC_center_x    = ARG2
CC_center_y    = ARG3
CC_radius      = ARG4
CC_angle_start = ARG5
CC_angle_end   = ARG6
CC_samples     = ARG7

set print @CC_outputdata
    do for [CC_i = 1:CC_samples] {
        CC_angle = CC_angle_start + (CC_angle_end -CC_angle_start)/(CC_samples - 1.0)*(CC_i-1)
        CC_x = CC_center_x + CC_radius*cos(CC_angle)
        CC_y = CC_center_y + CC_radius*sin(CC_angle)
        print sprintf("%f\t%f",CC_x,CC_y)
    }
set print
### end of gnuplot procedure

子程序:"tbHatchLineGeneration.gpp"

### create dataset for hatch pattern
# example usage:
# call "tbHatchLineGeneration.gpp" "$Circle" "$Hatch" TiltAngle HatchLength HatchSeparation
# Note: negative numbers have to be put into ""
HLG_InputData       = ARG1
HLG_OutputData      = ARG2
HLG_TiltAngle       = ARG3
HLG_HatchLength     = ARG4
HLG_HatchSeparation = ARG5

# different terminal units per pixel
HLG_Rtupx = 1.                                   # for pngcairo terminal: 1 tu/px
if (GPVAL_TERM eq "wxt") { HLG_Rtupx = 20. }     # 20 tu/px, 20 terminal-units per pixel
if (GPVAL_TERM eq "qt")  { HLG_Rtupx = 10. }     # 10 tu/px, 10 terminal-units per pixel
# Ratio: axis units per terminal units
# print GPVAL_X_MAX, GPVAL_X_MIN, GPVAL_TERM_XMAX, GPVAL_TERM_XMIN
HLG_Rxautu = (GPVAL_X_MAX-GPVAL_X_MIN)/(GPVAL_TERM_XMAX-GPVAL_TERM_XMIN)
HLG_Ryautu = (GPVAL_Y_MAX-GPVAL_Y_MIN)/(GPVAL_TERM_YMAX-GPVAL_TERM_YMIN)

# Angle by dx,dy (range: -90°<= angle < 270°), NaN if dx=dy=0
HLG_Angle(dx,dy) = dx==0 ? (dy==0 ? NaN : sgn(dy)*90) : dx<0 ? 180+atan(dy/dx) : atan(dy/dx)

HLG_dx_px(n) = HLG_dx/HLG_Rxautu/HLG_Rtupx
HLG_dy_px(n) = HLG_dy/HLG_Ryautu/HLG_Rtupx
HLG_length_px(n) = sqrt(HLG_dx_px(n)**2 + HLG_dy_px(n)**2)

# create path data with 4 columns: x y cumulated_length angle
HLG_x1 = HLG_y1 = NaN
HLG_PathLength = 0
set table $HLG_Path
    plot @HLG_InputData u \
    (HLG_x0=HLG_x1,HLG_x1=,HLG_dx=HLG_x1-HLG_x0,): \
    (HLG_y0=HLG_y1,HLG_y1=,HLG_dy=HLG_y1-HLG_y0,): \
    ([=11=]>0?HLG_PathLength=HLG_PathLength+HLG_length_px(0):HLG_PathLength): \
    (HLG_Angle(HLG_dx,HLG_dy)): (HLG_dx) : (HLG_dy) w table
unset table
# print $HLG_Path
HLG_Resamples = HLG_PathLength/HLG_HatchSeparation          # density of hatch lines

# resample data in equidistant steps along the length of the path
HLG_x0(n) = real(word(@HLG_InputData[n],1))
HLG_y0(n) = real(word(@HLG_InputData[n],2))
HLG_r0(n) = real(word($HLG_Path[n],3))
HLG_a0(n) = n+1>|$HLG_Path| ? real(word($HLG_Path[n],4)) : real(word($HLG_Path[n+1],4))
HLG_Frac(n) = (HLG_ri-HLG_r0(n))/(HLG_r0(n+1)-HLG_r0(n))

# hatch line start point
HLG_hsx(n) = HLG_x0(n) + HLG_Frac(n)*(HLG_x0(n+1)-HLG_x0(n))
HLG_hsy(n) = HLG_y0(n) + HLG_Frac(n)*(HLG_y0(n+1)-HLG_y0(n))
# delta x,y scaled  
HLG_dx_px(n) = cos(HLG_a0(n))/HLG_Rxautu*HLG_Rtupx
HLG_dy_px(n) = sin(HLG_a0(n))/HLG_Ryautu*HLG_Rtupx
HLG_AngleNew(n) = HLG_Angle(HLG_dx_px(n),HLG_dy_px(n))+HLG_TiltAngle
HLG_dx2_px(n) = cos(HLG_AngleNew(n))*HLG_Rxautu
HLG_dy2_px(n) = sin(HLG_AngleNew(n))*HLG_Ryautu
HLG_L2_px(n) = HLG_HatchLength*sqrt(HLG_dx2_px(n)**2 + HLG_dy2_px(n)**2)
HLG_AngleNew2(n) = HLG_Angle(HLG_dx2_px(n),HLG_dy2_px(n))
HLG_hlx(n) = HLG_L2_px(n) * cos(HLG_AngleNew2(n))*HLG_Rtupx
HLG_hly(n) = HLG_L2_px(n) * sin(HLG_AngleNew2(n))*HLG_Rtupx

# generate hatch lines output datablock
set print @HLG_OutputData
    HLG_j = 1
    do for [HLG_i=1:HLG_Resamples] {
        HLG_ri = (HLG_i-1)*HLG_PathLength/(HLG_Resamples-1)
        while ( !(HLG_r0(HLG_j+1) > HLG_ri) && HLG_j<|$HLG_Path|-1) { HLG_j=HLG_j+1 }
        print sprintf("%.4f\t%.4f\t%.4f\t%.4f\t%.4f", HLG_hsx(HLG_j), HLG_hsy(HLG_j), HLG_hlx(HLG_j), HLG_hly(HLG_j), HLG_a0(HLG_j) )
    }
set print
### end of gnuplot procedure

代码:

### Add hatch pattern to a curve
reset session
set term wxt size 720,360
set angle degree
unset key

set xrange[0:5]
set yrange[-1.2:1.2]

# plot some dummy to get the gnuplot variables: 
# GPVAL_X_MAX, GPVAL_X_MIN, GPVAL_TERM_XMAX, GPVAL_TERM_XMIN
plot x

# Circle parameters:
# CenterX, CenterY, Radius, StartAngle, StopAngle, NoOfDatapoints
# Note: negative numbers need to be put into ""
call "tbCreateCircleData.gpp" "$Circle01" 1.0   0.3  0.6 0 360 120
call "tbCreateCircleData.gpp" "$Circle02" 2.4   0.3  0.6 0 360 120
call "tbCreateCircleData.gpp" "$Circle03" 3.8   0.3  0.6 0 360 120
call "tbCreateCircleData.gpp" "$Circle04" 1.7 "-0.3" 0.6 0 360 120
call "tbCreateCircleData.gpp" "$Circle05" 3.1 "-0.3" 0.6 0 360 120

# Hatch parameters:
# $InputData       data you want to add hatched lines
# $OutputData      data containing the hatched lines
# TiltAngle        >0°: left side, <0° right side
# HatchLength      length in pixels
# HatchSeparation  separation of hatch lines in pixels
# "$InputData", "$OutputData", TiltAngle, HatchLength, HatchSeparation
# Note: negative numbers have to be put into ""
call "tbHatchLineGeneration.gpp" "$Circle01" "$Hatch01" "-90" 10  5
call "tbHatchLineGeneration.gpp" "$Circle02" "$Hatch02" "-30" 15 10
call "tbHatchLineGeneration.gpp" "$Circle03" "$Hatch03"   90   5  3
call "tbHatchLineGeneration.gpp" "$Circle04" "$Hatch04"   45  25 12
call "tbHatchLineGeneration.gpp" "$Circle05" "$Hatch05" "-60" 10  7

plot \
    $Circle01 u 1:2 w l lc rgb "web-blue", \
    $Hatch01 u 1:2:3:4 w vec lw 1 lc rgb "web-blue" nohead, \
    $Circle02 u 1:2 w l lc rgb "black", \
    $Hatch02 u 1:2:3:4 w vec lw 1 lc rgb "black" nohead, \
    $Circle03 u 1:2 w l lc rgb "red", \
    $Hatch03 u 1:2:3:4 w vec lw 1 lc rgb "red" nohead, \
    $Circle04 u 1:2 w l lc rgb "yellow", \
    $Hatch04 u 1:2:3:4 w vec lw 1 lc rgb "yellow" nohead, \
    $Circle05 u 1:2 w l lc rgb "web-green", \
    $Hatch05 u 1:2:3:4 w vec lw 1 lc rgb "web-green" nohead

### end of code

结果: