Search code examples
rotationlabelalignmentgnuplot

How to keep alignment and line spacing with rotated labels in gnuplot?


You can rotate text labels as well as ticlabels. However, if I have multiline labels, how can I keep alignment and line spacing with rotated labels which have a rotation angle other than 0°, 90°, 180° or 270°? As you can see in the example below, aligment and line spacing are not kept with other than the above mentioned angles. What's going wrong there? I haven't checked yet whether this maybe depends on the aspect ratio of x- and y-axes? Any ideas how to fix this?

Script:

### how to keep alignment and line spacing with rotated labels?
reset session

$Data <<EOD
 1    0    "multiline\nlabel"
 2   30    "multiline\nlabel"
 3   45    "multiline\nlabel"
 4   60    "multiline\nlabel"
 5   90    "multiline\nlabel"
EOD

set termoption font ",14"
set offset 1,1,2,2
set key noautotitle

plot $Data u 1:(1):3:2 w labels rotate var right , \
        '' u 1:(2):3:2 w labels rotate var center, \
        '' u 1:(3):3:2 w labels rotate var left  
### end of script

Result:

enter image description here

Here another illustration that something does not work as expected: I filed a bug report. Actually, 180° is not correct as well, line1 and line2 are swapped.

enter image description here


Solution

  • Here are two attempts to workaround the problem that apparently gnuplot rotates multiline labels around the anchor point for each line independently instead of a common anchor point for the whole label. Although, there are exceptions for 90° and 270° as you can see in the jumping text in the animation.

    1. Attempt: split the multiline labels and plot them separately and add x- and y-offset depending on angle and line number. This seems to work for wxt terminal, but I haven't tested whether this works for all terminals and graph sizes.

    Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
    dx(n,a)   = n==1 ?    -sin(a) :      sin(a)
    dy(n,a)   = n==1 ? 0.5*cos(a) : -0.5*cos(a)
    

    2. Attempt: the first attempt will not work for ticlabels because you would create two shifted tics. Hence, in the second example, in addition to xtics with an offset for the first line, x2tics with an offset for the second line are used. However, depending on the graph height, you have to set the y-offset manually which is rather tedious. I haven't found yet a way to do this automatically (maybe with help of the GPVAL_ variables and a replot).

    As a 3rd option, you could also play with multiplot and overlay differently shifted xtics, which I would consider a painful workaround as well. I hope that there are (or will be) better solutions.

    Script 1:

    ### rotated multiline labels (two lines)
    reset session
    
    set key noautotitle
    set angle degrees
    
    s = "multiline\nlabel"
    
    dx(n,a)   = n==1 ?    -sin(a) :      sin(a)
    dy(n,a)   = n==1 ? 0.5*cos(a) : -0.5*cos(a)
    Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
    
    set yzeroaxis ls 1
    
    set term gif animate size 640,480 delay 10 font ",11"
    set output "SO74000192_2.gif"
    
    do for [a=0:355:5] {
        s = sprintf("multi line:\nAngle: % 4d°",a)
        set multiplot layout 1,2 ti "rotated multiline labels"
            
            set title "as is"
            plot 0, 1, 3, 5, 6, \
                '+' u (0):(1):(s):(a) every ::0::0 w labels rotate var right, \
                '+' u (0):(3):(s):(a) every ::0::0 w labels rotate var center, \
                '+' u (0):(5):(s):(a) every ::0::0 w labels rotate var left
                
            set title "workaround"
            plot 0, 1, 3, 5, 6, \
                for [j=1:2] '+' u (0):(1):(Line(s,j)):(a) every ::0::0 \
                    w labels rotate var right offset dx(j,a), dy(j,a), \
                for [j=1:2] '+' u (0):(3):(Line(s,j)):(a) every ::0::0 \
                    w labels rotate var center offset dx(j,a), dy(j,a), \
                for [j=1:2] '+' u (0):(5):(Line(s,j)):(a) every ::0::0 \
                    w labels rotate var left   offset dx(j,a), dy(j,a)
        unset multiplot
    }
    set output
    ### end of script
    

    Result 1:

    enter image description here

    Script 2:

    ### rotated multiline xticlabels (two lines)
    reset session
    
    set angle degrees
    set key noautotitle
    
    $Data <<EOD
     1   2   label
     2   5   "multiple\nlines"
     3   4   "another label"
     4   7   "another two\nline label"
     5   4   "single line again"
     6   3   "multiline\nagain"
    EOD
    
    Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
    dx(n,a) = n==1 ?    -sin(a) :      sin(a)
    dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
    
    set boxwidth 0.8
    set style fill solid 0.3
    set yrange[0:]
    
    a = 65
    set xtic out nomirror rotate by a right offset -sin(a),0.5*cos(a) font ",11"
     
    set multiplot layout 1,2 title "rotated multine labels"
    
        set title "as is"
        plot $Data u 1:2:xtic(3) w boxes
    
        set title "workaround"
        set link x2 via x inverse x
        set x2tics rotate by a right font ",11" offset sin(a), graph -1.14   # manually adjust y-offset depending on graph
        plot $Data u 1:2 w boxes, \
                '' u 1:(-1):xtic(Line(strcol(3),1)), \
                '' u 1:(-1):x2tic(Line(strcol(3),2))
    unset multiplot
    ### end of script
    

    Result 2:

    enter image description here