Menu

#451 More control over the placement of 3D plots on the canvas

None
open
nobody
None
2016-06-17
2016-05-31
Dan Sebald
No

Is the following a bug?

gnuplot> splot x*y
[looks fine]
gnuplot> set lmargin screen 0.4
gnuplot> set tmargin screen 0.6
gnuplot> splot x*y
[the lmargin looks to be screen 0.4, while the rmargin seems to have moved screen 0.2 off the plot, similarly the tmargin looks to be screen 0.4 from top, while the bmargin seems to have moved screen 0.2 off the plot]
gnuplot> set rmargin screen 0.6
gnuplot> set bmargin screen 0.4
gnuplot> splot x*y
[ok, looks as expected again]

Basically, gnuplot seems to be giving precedent to plot width than automatic margin selection.

Discussion

  • Dan Sebald

    Dan Sebald - 2016-05-31

    The 2D case works as I'd expect, keeping the opposite margins on screen:

    gnuplot> reset
    gnuplot> plot x
    gnuplot> set lmargin screen 0.4
    gnuplot> set tmargin screen 0.6
    gnuplot> plot x
    gnuplot> set rmargin screen 0.6
    gnuplot> set bmargin screen 0.4
    gnuplot> plot x
    
     
  • Dan Sebald

    Dan Sebald - 2016-05-31

    And neither is the following what I would expect even for all margins being set:

    gnuplot> reset
    gnuplot> set lmargin screen 0
    gnuplot> set rmargin screen 1
    gnuplot> set bmargin screen 0
    gnuplot> set tmargin screen 1
    gnuplot> splot x*y
    

    The plot 3D plot is expanded beyond the edges of the screen.

     
  • Dan Sebald

    Dan Sebald - 2016-05-31

    My observation is that the following code is important to the margin/scaling computation:

        /* HBB: Magic number alert! */
        xscaler = ((plot_bounds.xright - plot_bounds.xleft) * 4L) / 7L;
        yscaler = ((plot_bounds.ytop - plot_bounds.ybot) * 4L) / 7L;
    
        /* EAM Aug 2006 - Allow explicit control via set {}margin screen */
        if (tmargin.scalex == screen || bmargin.scalex == screen)
    {
    fprintf(stderr, "yscaler = %d\n", yscaler);
    fprintf(stderr, "ytop = %d  ybot = %d\n", plot_bounds.ytop, plot_bounds.ybot);
    fprintf(stderr, "surface_scale = %g\n", surface_scale);
        yscaler = (plot_bounds.ytop - plot_bounds.ybot) / surface_scale;
    fprintf(stderr, "yscaler = %d\n", yscaler);
    }
    

    I placed some print code around the line for which if I comment it out, the 3d plot behavior seems to behave as I expected. I'm using as a test example:

    gnuplot> set tmargin screen 0.8
    gnuplot> splot x*y
    yscaler = 1979
    ytop = 3840  ybot = 376
    surface_scale = 1
    yscaler = 3464
    gnuplot> 
    

    The case of lmargin=0, rmargin=1, bmargin=0, tmargin=1 also seems to look improved after commenting out the yscaler and xscalar re-computations.

    I didn't investigate much beyond that because there seems to be the use of global variables here which takes me a bit of extra time to follow. Should it be && instead of || in the screen test? Is there something down stream that now treats that 4/7 scaling different than it once did for the screen/screen margin case? I don't know.

     
  • Ethan Merritt

    Ethan Merritt - 2016-05-31

    Starting in version 5 the 'set margin' commands try to act on "set view map" 3D plots approximately as they do for normal 2D plots. The result is not a perfect match but closer than it used to be.

    True 3D plots (general case of "set view") don't have margins per se so it is not clear what "set margin" is expected to do, exactly. As you point out, they currently have the effect of shifting the plot around on the page rather than changing its size. Remember that unlike 2D plots, 3D plots have a separate pair of scale factors: the 3rd and 4th parameters to "set view".

     
  • Dan Sebald

    Dan Sebald - 2016-05-31

    I experimented with the scale control last night. But I figured so long as it is set at 1, the result should look reasonbly good.

    OK, it sounds like this is trying to make the best of a bad situation. So if I were to take what I have that looks too big in a non-map view and rotate that so it becomes map view...yeah, I see now that does happen.

    I take it that no matter the initial chosen view, gnuplot doesn't want to change the 3D scale or center point with rotation because that would appear odd when the mouse is used to move the view. Furthermore, this magic number stuff is something to get a reasonable good guess at sizing for 3D plots in general.

    I can see that, but at the same time, it really would be beneficial in some cases to have finer control over sizing. In order to do that, it seems that gnuplot should pick a unity scale factor, run the hidden line code and compute the extent of the view. Then take the {}margin target and compute some afine translations for drawing so that the plot comes out with balanced margins. That would get rid of the magic number, correct?

     
  • Ethan Merritt

    Ethan Merritt - 2016-06-01

    I really have no idea where that magic number 4/7 came from.
    The 3D code already works just as you describe. The plot contents are rescaled into a unit cube, and then the view + scale parameters are applied to that unit cube.
    Isn't a scale factor sufficient "fine control" of the size?

     

    Last edit: Ethan Merritt 2016-06-01
    • Hans-Bernhard Broeker

      7/4 is an approximation of sqrt(3.0), which is the factor between a cube's shortest and longest projected extent (edge versus diagonal). That means the graph cube has to be scaled down by 4/7 relative to the square on-screen region to make sure its projected view fits into that window at arbitrary rotations.

       
  • Dan Sebald

    Dan Sebald - 2016-06-01

    Well, as far as scaling, I don't know if setting the scale is fine enough control because it is a bit on the obscure side and perhaps arbitrary just exactly what "scale" represents. I mean, there are three dimensions, so three scalings of an axes, yet there are only two scale factors when maneuvering a 3D plot,

           set view <rot_x>{,{<rot_z>}{,{<scale>}{,<scale_z>}}}
    

    Now, the above may be a complete specification for those three scalings, but not orthogonal in the sense that "scale" controls both the x and y axis, while scale_z is the third. Those, in combination with the view angle are adequate to specify axis projection (i.e., what direction is the x y or z axis pointing in relation to the view?) and units along that axis projection. But the initial relationship between x and y scaling is what's missing. For example, do

        splot x*y
    

    and rotate so that the orthographic plane is visible. That aspect ratio is not 1 to 1 so something in gnuplot chose that. Was it inherent in first drawing the plot then looking at it's extent? I'm sure there is a logical reason behind it.

    Specifying the margins is something more understandable to the user, I think. It's helpful for laying out things for visual balance, say, when doing a multiplot. Of course, it is just another way of specifying the eventual x, y, z-axis unit scaling with a different set of parameterized values. And it sort of obviates the 4/7 technique. For example, if gnuplot were to have {}margin set, the view angle and axis range would then dictate the x, y, z-axis projections and scalings. From there, things are fixed for mouse rotation (does mouse rotation use "plot" or "replot"?) and rotating might end up with the view going outside of user-specified bounds... that is, until another "plot" is done when the view and {}margin are set. Anyway, what I'm describing is a bit like the new 5-version behavior but rather than the map view being used it's the overall extend being used...and if someone set {}margin along with "set view 0,0" or "set view map" it would behave the same as current version 5.x does.

    I just submitted a change to Octave to address an issue it had with rotating something that was set to orthographic view, i.e., "set view map". They apparently wanted that map view, but they also wanted the ability to rotate with the mouse after having set that. So, I changed it to something like:

    set view map
    [create an splot]
    set view 0,0
    

    which works, but as soon as a mouse rotation is done gnuplot goes back to the 4/7 rule and the size of the plot jumps suddenly. That's not awful behavior, but not smooth either.

    I'm looking at an alternate drawing toolkit for Octave and it does this some sort of thing by changing the scaling along with rotation so that it always occupies the same visual extent. That actually isn't too bad of behavior. So maybe that is how the {}margin-set mode could be. Just always make the plot extend occupy the same 2D space rather than use it to compute map view and fix scaling from there on. If no {}margin is set, then go back to the 4/7 scaling mode.

    BTW, just to note, if I do

    gnuplot> set view equal xyz
    gnuplot> splot x*y
    

    z extends pretty far off screen (which is explained in the documentation), but interestingly the center mouse button does nothing to the z-axis even though the z-axis scaling value keeps changing. If in "equal xyz" mode, isn't z_scale tied to the scale? Shouldn't both left-right and up-down mouse movement be able to control scale and z_scale simultaneously in that mode?

     
    • Dan Sebald

      Dan Sebald - 2016-06-01

      Actually, why not simplify things and just add a new rotate mode control option? For example,

      set view <rot_x>{,{<rot_z>}{,{<scale>}{,<scale_z>}}} {fixedscale | fixedextent}
      

      In fixedextent mode gnuplot does what I described above, i.e., changes the scale so that the plot fills the whole space specified by {}margin. In fixedscale (default) mode, the 1/sqrt(3) rule is used with a fixed scale.

      With that, there is no special behavior dependent up {}margin or odd distinguishing of plot vs. replot with mouse movement. Users will get the max-sized map view by consequence of fixedextent mode. That is, "set view map" would be the same as "set view 0,0 fixedextent". (This brings up another issue I'll put in a different post.)

      I sort of like that, as it doesn't sound like too much of a change to make that work from what is already present in the code.

       
  • Dan Sebald

    Dan Sebald - 2016-06-01

    The other thing I just remembered is the annotation of the perpendicular axis in an orthographic view. The current difference between "set view map" and simply rotating a 3D splot so that it has angle 0,0 (or some other orthographic view) is that the former does not plot the tick mark annotation--all those characters just end up on one another and are illegible. I wonder if we can leave off that perpendicular (to view) axis tick mark and annotation whenever the view is set orthogonal in some way, e.g., "set view 0,0" would leave off the blob of characters in the lower left corner.

     
    • Ethan Merritt

      Ethan Merritt - 2016-06-14

      Sounds reasonable.
      That's a one-liner:

      --- gnuplot/src/graph3d.c       2016-06-13 12:00:20.370081654 -0700
      +++ gnuplot-cvs/src/graph3d.c   2016-06-14 11:27:25.152033874 -0700
      @@ -2533,6 +2533,7 @@ draw_3d_graphbox(struct surface_points *
               * two-part grid drawing process: */
              && (FRONTGRID != whichgrid)
              && (splot_map == FALSE)
      +       && (surface_rot_x != 0)
              && (draw_surface
                  || (draw_contour & CONTOUR_SRF)
                  || strchr(pm3d.where,'s') != NULL
      
       
  • Ethan Merritt

    Ethan Merritt - 2016-06-17
    • summary: splot opposite margin moving with set of lmargin, tmargin, etc. --> More control over the placement of 3D plots on the canvas
    • Group: -->
    • Priority: -->
     
  • Ethan Merritt

    Ethan Merritt - 2016-06-17

    Ticket moved from /p/gnuplot/bugs/1802/

    Can't be converted:

    • _milestone:
    • _priority:
     

Log in to post a comment.