Skip to content

Problem going back to a viewport with gridBase

12 messages · Paul Murrell, Gabor Grothendieck

#
On 6/2/05, Paul Murrell <p.murrell@auckland.ac.nz> wrote:
Thanks.  I have mucked around in vpTree structures and discovered its
actually quite easy to specify children so I have changed my example
so that instead of naming the children of 'layout' and then remembering 
coordinates linked to the names of the children of 'layout' in 
the 'coords' structure (which really just duplicates state information
already available in grid) it simply follows the order
of the children of 'layout' directly.  This permits elimination of 'coords' 
and the two naming functions.  Using the depth approach you advocate,
'with' also becomes shorter and I think I have it to the point where it works 
with both vpPath and viewport classes.  Once Deepayan implements 
the use.viewport= argument to print, 'with' can be eliminated too.  No 
questions this time but I thought I would post the latest version for
completeness. Regards.

[pushLayout is same as before except there are no names on the
children of 'layout' and the rest is new]

library(grid)
library(lattice)

pushLayout <- function(nr, nc, name="layout") {
  pushViewport(viewport(layout=grid.layout(nr, nc), name=name))
  for (i in 1:nr) {
    for (j in 1:nc) {
      pushViewport(viewport(layout.pos.row=i, layout.pos.col=j))
      upViewport()
    }
  }
  upViewport()
}

with.vpPath <- with.viewport <- function(data, expr, ...) {
      # if data is a vpPath it cannot be ROOT since NULL will never
dispatch here
      depth <- if (data$name == "ROOT") 0 else downViewport(data$name)
      result <- eval.parent(substitute(expr))
      upViewport(depth)
      invisible(result)
}

grid.newpage()

# specify number of cells to fill and number of rows
n <- 5; nr <- 3

nc <- ceiling(n/nr)
downViewport(pushLayout(nr, nc))

vpt <- current.vpTree(all = FALSE)
for(k in 1:n) with(vpt$children[[k]],
      print( xyplot(v ~ v, list(v = 1:k)), newpage = FALSE )
)
3 days later
#
Hi
Gabor Grothendieck wrote:
Ok.  I can see this working ... for now.  The disadvantage with this 
approach is that it makes use of the undocumented, internal structure of 
a viewport tree to grab a list of child viewports.  A worse example of 
the same thing is that the with() methods make use of a happy 
coincidence that both viewport objects and viewport path objects share a 
component called "name" (and an even happier coincidence that they 
contain the same information).  I think it would be cleaner and better 
practice, despite requiring longer code, to make use of the documented 
interface which requires specifying viewport names and viewport paths.
The internal structure of objects is not guaranteed to be stable.

Paul

  
    
#
On 6/6/05, Paul Murrell <p.murrell@auckland.ac.nz> wrote:
Perhaps accessor functions could be provided that allow one to 
retrieve the name of a viewport and the name of a vpPath in 
a safe way.  These could be as simple as:

names.viewport <- names.vpPath <- function(x) x$name

Similarly an accessor function to safely retrieve the children would 
be nice.    Again, it should ideally be possible to
have a generic with methods for various grid classes.

Then the  relevant line in the code concerning name
could be written in a safe way like this:

depth <- if (data$name == "ROOT") 0 else downViewport(names(data))

and similarly for the children.
#
Hi
Gabor Grothendieck wrote:
Fair enough.  If I say "use the API", I should provide a useful API :)

This is a reasonable request for viewports;  the "name" component of a 
viewport is a sensible thing to want.

OTOH, it is not necessarily reasonable for a viewport path;  not all 
components of an object should necessarily have accessors.  The "name" 
component of a viewport path is the last element in the path.  Perhaps 
an API should be supplied for extracting parts of a viewport path, but 
it should probably be something along the lines of car()/cdr() or 
head()/tail() or explode() to get different bits of the path.

Accessing the children of a viewport is subtly problematic too. 
Directly accessing the "children" slot and using the order of the 
children in that slot is "dangerous" because there is no claim made by 
the system as to how the children are internally ordered.  Again, it 
works currently, but it makes incorrect assumptions about what the 
system is doing internally so is vulnerable to future changes.

So again, the recommended approach is to use the API provided;  you 
provide the naming scheme for viewports and you control the order in 
which viewports are used.

Paul

  
    
#
On 6/7/05, Paul Murrell <p.murrell@auckland.ac.nz> wrote:
That is the point of an accessor.  If the internals change then the
accessor is modified to hide the change so that the user using the
accessor is not impacted.  

It seems that grid already partly supports this with the childNames
function.  It could be made generic and a method provided
to cover the classes discussed here too.
#
Hi
Gabor Grothendieck wrote:
I agree that a childNames() method for a viewport tree is probably 
reasonable.  The subtle problem is the fact that your code makes use of 
the *order* of the names that function would return, when in fact there 
is no claim that they will be in any particular order.

Paul

  
    
#
Here is the code once again.  This time I have supplied two 
names methods and a getChildren.viewport function to 
encapsulate the corresponding grid internals.  It would
be easiest if grid provided these itself but in the absence
of that this does encapsulate dependencies on grid
internals to a well defined set of functions.   Note that
names is only used in 'with' and 'with' will be eliminated
once Deepayan adds the use.viewport= (or whatever
its called) to print.   I am not sure from your response
whether or not you intend to add these items to the grid
API but in any case this provides an intermediate
level of safety.

library(grid)
library(lattice)

pushLayout <- function(nr, nc, name="layout") {
  pushViewport(viewport(layout=grid.layout(nr, nc), name=name))
  for (i in 1:nr) {
    for (j in 1:nc) {
      pushViewport(viewport(layout.pos.row=i, layout.pos.col=j))
      upViewport()
    }
  }
  upViewport()
}

names.vpPath <- names.viewport <- function(x) x$name

with.vpPath <- with.viewport <- function(data, expr, ...) {
      # if data is a vpPath it cannot be ROOT since  
      # NULL will never dispatch here
      depth <- if (data$name == "ROOT") 0 else downViewport(names(data))
      result <- eval.parent(substitute(expr))
      upViewport(depth)
      invisible(result)
}

getChildren.viewport <- function(x) x$children

grid.newpage()

# specify number of cells to fill and number of rows
n <- 5; nr <- 3

nc <- ceiling(n/nr)
downViewport(pushLayout(nr, nc))

vpt <- current.vpTree(all = FALSE)
for(k in 1:n) with(getChildren.viewport(vpt)[[k]],
      print( xyplot(v ~ v, list(v = 1:k)), newpage = FALSE )
)
#
Yes, I understand that although such order is convenient for
the user as the significant reduction in code size here shows.  I
wonder if there might be some performance parameter (e.g. hash)
to control it.  If hash = TRUE then no guarantee is provided.  Otherwise
order is kept.
On 6/7/05, Paul Murrell <p.murrell@auckland.ac.nz> wrote:
#
Just one additional item.  Look at:

?new.env

for an example of where this approach is used in R, noting the
hash= argument.
On 6/7/05, Gabor Grothendieck <ggrothendieck@gmail.com> wrote:
#
Hi
Gabor Grothendieck wrote:
Yep, I've made a note to look at adding these to the grid API.
Thanks.

Paul

  
    
#
Thanks.  Yet one other comment to consider when thinking
about this.  Even if its not possible
or advisable to guarantee order, even without the hash=
idea, it may be possible to guarantee that default names
are generated in some order that can be used by
getChildren to ensure that it returns the children in
the same order they are created or perhaps even some sort
of timestamp can be attached to objects to facilitate 
later traversal.
On 6/7/05, Paul Murrell <p.murrell@auckland.ac.nz> wrote:
#
Hi
Gabor Grothendieck wrote:
Thanks Gabor.  I will look at a way to ensure that childNames(vpTree) 
returns the names in the order that they were pushed.  This may be as 
simple as porting the mechanism used for grobs and gTrees, where 
(drawing) order is very important.

Paul