1

I am wondering if gnuplot offers more hatched patterns than those 8 patterns which you see when typing "test" (e.g. in wxt terminal)

Maybe there are more than 8 fill patterns? ...apparently not, as the code below shows... I'm not talking about patterns combined with different colors, I just mean the type of patterns.

I was hoping that you can realize, for example horizontally or vertically hatched patterns. Maybe even set the distance between the hatch lines or even set the angle. Maybe you can?

### hatched pattern fill
reset session
set colorsequence classic
N = 28
set samples N
set table $Data
   plot [1:N] x
unset table
plot for [i=1:N] $Data u 1:1:(1) every ::i-1::i-1 with boxes fs pattern i not
### end of code

enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72

2 Answers2

0

Suppose we want to pattern an area between function y=f(x) >0 and y = 0. It is posiible to create different "patterns" by using the following method. (i) Create a file that has 4 columns (x, y, xdelta, ydelta). This file describes a set of lines with the same slope. (ii) Plot data from the file, using option "with vectors". We get a shaded (hatched) rectangle. (iii) Plot f(x), using option "with filledcurve below y=ymax fc 'white'". Repeating steps (i) and (ii) gives a "crossed" pattern. Here is an example of a code (a single-hatched pattern), gnuplot v.5.2:

f(x) = 1.5+sin(x)
# Plot a "pattern" (for 2 < x < 4) and f(x):
plot[1:5][0:3] 'a1.dat'u 1:2:3:4 w vect nohead lt 4 lw 3,\
  f(x) w l lt 3 lw 4
pause -1
# Plot a "pattern" and f(x), and white area above f(x):
plot[1:5][0:3] 'a1.dat'u 1:2:3:4 w vect nohead lt 4 lw 3,\
  f(x) w l lt 3 lw 4, f(x) w filledcur below y=3 fc 'white'

File 'a1.dat' has the following rows:

2.0 2.5 0.5 0.5
2.0 2.0 3   3
2.0 1.5 3   3
2.0 1.0 2   2
2.0 0.5 2   2
2.0 0.0 2   2
2.5 0.0 1.5 1.5
3.0 0.0 1   1
3.5 0.0 0.5 0.5

enter image description here

enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72
user2320292
  • 125
  • 1
  • 8
  • thank you for your suggestion. Well, this could be a starting point. "Painting" on top with "white" might work only in some cases. By the way, plotting using axes coordinates will make the hatch pattern plot size and ratio dependent. Actually, I had geographic maps with different hatch densities, angles, etc. in mind. Just to give you a flavour how "complicated" it can get, check the following example about hatch patterns (just hatch patterns at lines and no filled areas) https://stackoverflow.com/a/57685754/7295599 – theozh Dec 19 '19 at 18:38
  • @theozh : I agree this method is not very elegant. One need to use, say, Python or Excel to get the data-file for vectors. However, the method allows one to generate patterns, consisting of lines with _any_ slope (not only 45 degree slope, as in current versions of Gnuplot), including horizontal and vertical lines. – user2320292 Dec 20 '19 at 04:50
  • yes, not yet very user-friendliy, but pretty straightforward. Allow me to add the graphs and re-format your data such that is easier for people to copy&paste&test. I'm sure one can also implement something with gnuplot without external programs. Arbitrary hatched pattern under a function to zero might be the easiest. Hatched pattern under data curves will be more difficult. And arbitrary hatched fill of arbitrary areas will probably be the most difficult. – theozh Dec 20 '19 at 05:37
0

Here is a (somehwat cumbersome) procedure to realize "non-standard" hatch patterns. The procedure is straightforward but still somewhat lengthy in gnuplot. Improvements are welcome.

Procedure:

  1. Determine the bounding box of the datapoints
  2. create a datablock $HatchBBox which fully covers the bounding box with hatch lines (see first image below)
  3. cut the hatch lines by looking for intersections of hatch lines with the path and write it to datablock $Hatch.
  4. plot the datablock $Data and $Hatch.

Example of $HatchBBox, i.e. hatch lines covering the bounding box of a closed path:

enter image description here

Requirements, limitations and improvements:

  • requires a closed path
  • works for convex areas and limited to concave areas as long as there are only 2 intersections of hash lines with the path
  • room for improvement: hatch pattern should not depend on bounding box size but should be equal on pixel level. Certainly, somehow possible, but probably even more complicated to realize.

Edit: Here is a revised version with a new illustrative example. A random pathwork grid with random hatch patterns.

In order to keep the overview, the actual hatch generation is put into an external procedure tbHatchArea.gpp and called from the main code.

Code:

Subprocedure: tbHatchArea.gpp

### create hatched areas from a datablock
# input ARG1: input datablock
# input ARG2: hatch parameters
# input ARG3: output datablock

# some necessary functions
# orientation of 3 points a,b,c: -1=clockwise, 0=linear, +1=counterclockwise
Orientation(a,b,c) = sgn((word(b,1)-word(a,1))*(word(c,2)-word(a,2)) - \
                         (word(c,1)-word(a,1))*(word(b,2)-word(a,2)))

# check for intersection of segment a-b with segment c-d,
# 0=no intersection, 1=intersection
IntersectionCheck(a,b,c,d) = \
    (Orientation(a,c,b)==Orientation(a,d,b)) || (Orientation(c,a,d)==Orientation(c,b,d)) ? 0 : 1

# calculate coordinates of intersection point, "" if identical points
M(a,b) = real(word(a,1)*word(b,2) - word(a,2)*word(b,1))
N(a,b,c,d) = (word(a,1)-word(b,1))*(word(c,2)-word(d,2)) - \
             (word(a,2)-word(b,2))*(word(c,1)-word(d,1))
Intersection(a,b,c,d) = N(a,b,c,d) !=0 ? sprintf("%g %g", \
    (M(a,b)*(word(c,1)-word(d,1)) - (word(a,1)-word(b,1))*M(c,d))/N(a,b,c,d), \
    (M(a,b)*(word(c,2)-word(d,2)) - (word(a,2)-word(b,2))*M(c,d))/N(a,b,c,d)) : ""

myHatchAngle(n,m)   = word(@ARG2[n+1],m+1)  # Hatch angle 1,2
myHatchSteps(n)     = word(@ARG2[n+1],4)    # Hatch steps
myHatchLinewidth(n) = word(@ARG2[n+1],5)    # Hatch linewidth
myHatchColor(n)     = word(@ARG2[n+1],6)    # Hatch color

# create datablock hatch pattern
IndexStart = 0
IndexEnd = |@ARG2|-1
set print @ARG3
do for [k=IndexStart:IndexEnd] {    # loop all sub-datablocks if there is a line in $HatchParam
    set table $SingleCurve
        plot @ARG1 u 1:2 index k w table
    unset table
    stats $SingleCurve u 1:2 nooutput
    xmin = STATS_min_x
    ymin = STATS_min_y
    xmax = STATS_max_x
    ymax = STATS_max_y
    xrange = xmax-xmin
    yrange = ymax-ymin
    Diagonal = sqrt(xrange**2 + yrange**2)
    
    # create hatch lines covering the whole bounding box
    set samples myHatchSteps(k)+1
    amax = myHatchAngle(k,2) == myHatchAngle(k,2) ? 2 : 1  # in case there are two hatch angles
    set table $HatchBBox
        do for [a=1:amax] {
            ystart = myHatchAngle(k,a) > 0 ? ymax : ymin
            Pix(i) = xmin + xrange/myHatchSteps(k)*i
            Piy(i) = ystart - sgn(myHatchAngle(k,a))*yrange/myHatchSteps(k)*i
            plot '+' u (Pix($0)-Diagonal*cos(myHatchAngle(k,a))): \
                       (Piy($0)-Diagonal*sin(myHatchAngle(k,a))): \
                       (Pix($0)+Diagonal*cos(myHatchAngle(k,a))): \
                       (Piy($0)+Diagonal*sin(myHatchAngle(k,a))) w table
        }
    unset table 

    # looping data segments for finding intersections
    do for [i=1:|$HatchBBox|] {
        a = sprintf("%s %s", word($HatchBBox[i],1),word($HatchBBox[i],2))
        b = sprintf("%s %s", word($HatchBBox[i],3),word($HatchBBox[i],4))
        Line = ''
        Intersection0 = ""
        do for [j=1:|$SingleCurve|-1] {
            c = $SingleCurve[j]
            d = $SingleCurve[j+1]
            if (IntersectionCheck(a,b,c,d)) {
                Intersection1 = Intersection(a,b,c,d)
                if ((Intersection0 ne Intersection1)) {
                    print sprintf("%s %s",Intersection0, Intersection1)
                }
                Intersection0 = Intersection1
            }
        }
    }
    print ""; print ""
}
set print
### end of code

Main code:

### random hatched patchwork
reset session

# create some random patchwork grid points
set print $Patchwork
do for [i=0:10] {
    do for [j=0:10] { 
        print sprintf("%g %g %g %g",i,j,i+rand(0)*0.8-0.4, j+rand(0)*0.8-0.4)
    }
}
set print

# create patchwork areas from patchwork points
set print $PatchworkFrames
do for [i=0:9] {
    do for [j=0:9] {
        k = i*11+j
        print sprintf("%s %s",word($Patchwork[i*11+j+1],3),word($Patchwork[i*11+j+1],4))
        print sprintf("%s %s",word($Patchwork[i*11+j+2],3),word($Patchwork[i*11+j+2],4))
        print sprintf("%s %s",word($Patchwork[(i+1)*11+j+2],3),word($Patchwork[(i+1)*11+j+2],4))
        print sprintf("%s %s",word($Patchwork[(i+1)*11+j+1],3),word($Patchwork[(i+1)*11+j+1],4))
        print sprintf("%s %s",word($Patchwork[i*11+j+1],3),word($Patchwork[i*11+j+1],4))
        print "";  print ""
    }
}
set print

# create random angles, linecounts, linewidths and colors
# subdatablockNo, angle1, angle2, linesCount, lineWidth, color
set print $HatchParams
do for [i=1:100] {
    print sprintf("%g %g %s %g %g %s", \
      i, a=rand(0)*180-90, rand(0)>0.5 ? sprintf("%g",-a) : "NaN", \
      int(rand(0)*10)+5, rand(0)+0.5, sprintf("0x%06x",rand(0)*0xffffff))
}
set print

set size ratio -1
set angle degrees
set xrange[-1:11]
set yrange[-1:11]

call "tbHatchArea.gpp" "$PatchworkFrames" "$HatchParams" "$Hatch"

plot $PatchworkFrames u 1:2 w l lc rgb "black" notitle, \
     for [i=0:|$HatchParams|-1] $Hatch u 1:2:($3-$1):($4-$2) index i w vec \
     lc rgb myHatchColor(i) lw myHatchLinewidth(i) nohead notitle
### end of code

Result: (might take a while to generate)

enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72