Skip to content

Convert guide system to ggproto #3329

Closed

Description

@clauswilke

The guide system is the last remaining major ggplot2 component that has not been converted to ggproto. As a consequence, it doesn't match well with the rest of the ggplot2 code base, and it is difficult to extend by external package maintainers. We should consider converting it over, possibly before building additional components that would use the current system, as in #3322.

Activity

  1. paleolimbot commented on May 15, 2019

    @paleolimbot
    Member

    Yes! I was surprised that guides were S3 when I opened up that code. I think it will be less work to do this after #3322, which is to say, after all the guides use the same system. I'd be happy to take this on as well, but would prefer to do #3322 first.

  2. teunbrand commented on Jun 9, 2022

    @teunbrand
    Collaborator

    The ggplot2 book softly discourages extending ggplot's guide system. However, I'm pretty keen on making some guide extensions, so I'd be willing to spend some time experimenting. What are the current thoughts on moving the guide system to ggproto? I've heard on the grapevines that the entire secondary axis mechanism might move to guides, for example. Other than simply wrapping guide code into ggproto methods and making appropriate classes, what would need to change?

  3. thomasp85 commented on Jun 10, 2022

    @thomasp85
    Member

    @teunbrand I can expand a bit on why I've yet to build up the stamina to do this. If it were a simple rewrite of the current guide methods into a ggproto system it would be quite quick and we would have done it years ago. However, I want this to be an opportunity to rethink the guide implementation in a better way. Currently there is so much code duplication between the different legend guides in terms of how to lay out the grobs, calculate ticks etc. One could even go so far as to say that the keys in a colorbar guide is actually an instance of an axis guide and should use that implementation - but can that be generalised to e.g. the legend guide? There are quite a number of questions about how to do this properly in a manner that doesn't constraint future guide development but allows maximum code reuse.

    This is not to deter you from taking on the task. I would be happy if you had the stamina for it, and I would love to set off some time to discuss and guide you through it so you end up with a PR that we agree with the direction on.

  4. teunbrand commented on Jun 10, 2022

    @teunbrand
    Collaborator

    Thanks for your thoughts Thomas, they make a lot of sense to me. From an extension POV, it would be useful too if (in particular the drawing) code was more modular in some ways, which would align with the perspective of reducing code duplication if these modules can be re-used in different guides. For example, if you'd like to make ticks for the minor breaks, you wouldn't want to touch the axis lines or labels parts. I'll have a walk through the code at some point and take stock of what is the same/very similar or very different between various guides.

  5. thomasp85 commented on Jun 10, 2022

    @thomasp85
    Member

    yes, exactly. If we are going to do it we are going to do it with thought and intent. If you want to take it on I will be happy to have one or more design meetings to discuss your ideas and give you feedback

  6. teunbrand commented on Jun 11, 2022

    @teunbrand
    Collaborator

    I'd be willing to take a stab at this, but it may take quite some time before we find appropriate solutions. Worst case scenario is we can't figure out an elegant solution, and we're back where we started with some lessons learned along the way. I'd definitely be happy to get some guidance along the way, I don't consider this a small task. Below, I've made some notes going through the guide code trying to understand what some guides share and what is specific to a guide. I think that the shared bits might be modularised in some way.

    Notes

    General ideas

    • (Thomas) tick drawing code could be universal
    • Bidirectional element_grob() might simplify code
    • guide_colourbar()/guide_legend()/guide_bins() might share the same
      layout code, if we see the colourbar or bins as a 1-key legend.
    • Some code might be cleaner if arguments like frame.colour, frame.linewidth
      etc., are captured as elements in the constructor. Likewise for ticks.*,
      axis.*, label.* and title.*. Can later be combined with parent theme
      elements using ggplot2:::combine_elements() (might be worth exporting).

    General notes

    • guide_grid() doesn't seem to follow conventions of other guides. I think it might be worth exposing, but might get hairy implementation-wise.
    • guide_none() generally does nothing and returns zeroGrob() in the end.
    • guide_coloursteps is, with the exception of training, identical to
      guide_colourbar().

    Training tasks

    Common tasks

    • Extract key from scale
      • non-axis guides: reverse key
    • Set guide name
    • Set hash

    Specific tasks

    • guide_bins()/guide_coloursteps():
      • Format breaks/labels from (n, n] notation
    • guide_colourbar()/guide_coloursteps():
      • Create key for bar

    Merging tasks

    Common tasks

    • Discard new_guide

    Specific tasks

    • guide_legend()/guide_bins()
      • Merge keys
      • Merge override.aes
    • guide_none()
      • Return new_guide instead

    Transform tasks

    Common tasks

    • Skip doing anything

    Specific tasks

    • guide_axis()
      • Do coord transformation

    guide_geom() tasks

    Common tasks

    • Skip doing anything

    Specific tasks

    • guide_legend()/guide_bins()
      • Walk through all layers
      • Decide which layers to include
      • Stage components for drawing keys

    Grob generation, i.e. drawing tasks

    Common tasks

    • Set parameters
    • Extract relevant elements from theme
    • Override specific theme settings depending on direction/position
    • Build title
    • Build labels
    • Build tickmarks

    The follow tasks are common, but implementation is specific

    • Measure components
    • Setup a layout for components
    • Assemble components

    Specific tasks

    • guide_axis()
      • Title handled by facet instead. Might be useful to provide framework for titles anyway and have title build code just return zeroGrob().
      • Build axis line.
      • Overrule axis.label.{x/y}.{position} angle
      • Dodge labels
    • guide_legend()
      • No tickmarks needed
      • Legend text are individual grobs
      • Overrule keywidth/keyheight
    • guide_colourbar()
      • Build colour bar
      • Overrule barwidth/barheight
    • guide_legend()/guide_bins()
      • Build keys
    • guide_legend()/guide_colourbar()/guide_bins()
      • Overrule title's hjust/vjust
      • Overrule legend text's hjust/vjust
  7. teunbrand commented on Jun 16, 2022

    @teunbrand
    Collaborator

    I do want to spend some time trying to get this to work. How about I just make a Guide parent + implementation of 1 guide (axis probably being the simplest), put it in a WIP PR and we could course-correct from there?

  8. thomasp85 commented on Jun 16, 2022

    @thomasp85
    Member

    That may be a good idea to get the ball rolling

  9. added a commit that references this issue on Apr 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions