aphalo / ggpp

Grammar of graphics extensions to 'ggplot2'
118 stars 10 forks source link

Enhance action `"spread"` in `position_nudge_to()` #6

Open aphalo opened 1 year ago

aphalo commented 1 year ago

This is a note about something to consider in the future. In 'ggrepel' repulsion is applied during rendering, so it tracks changes in plot size. This is advantageous in some respects, but means that repulsion will result in different plot layouts depending on the graphical device and width and height parameters passed to it. A different compromise solution is to apply the repulsion with a position function.

While position_repulse() would depend on a random shift component, position_spread() would work deterministically.

aphalo commented 1 year ago

No new position functions are needed for this, so I changed the title. Action "spread" is now implemented. I think repulsion is overkill in this case. Adding support for spreading to a minimum distance would be deterministic and easier to implement.

aphalo commented 7 months ago

Action "spread" in position_nudge_to() within a range arranges the new positions to be equally spaced. An alternative approach to spreading would be to deterministically displace the positions just to ensure a minimum distance, but not more than this. Finding the optimal displacement will probably require iteration using a penalty. Possibly, the penalty could be a sum of squares to ensure that the larger displacements are penalized more than the sum of smaller ones. Someone must have developed an algorithm for this. Maybe asking in Mastodon would be a good idea.

aphalo commented 1 month ago

Some investigation suggests that compute_panel instead of compute_layer should be used, as it takes scales as argument. Currently action "spread" distributes the labels within the range of the data. An action "expand" could be implemented to use the scale range instead. To add even more flexibility, user-supplied functions could be accepted as arguments for action although the interface would need some thought to make it possible to use user-passed functions with both data and scale ranges. So, instead of implementing "expand", we could add x.range and y.range formal parameters, thus making it possible to also pass a numeric range, "data" or "scale" as arguments.

This needs quite a bit of thought as most likely user passed functions will need additional arguments. A different approach using more formal arguments would to have x.action.fun and y.action.fun and the matching x.action.fun.args and y.action.fun.args and x.range and y.range in a position function called position_nudge_fun() and implement position_nudge_to() and other similar position functions as wrappers on position_nudge_fun().

help(ggproto) and https://ggplot2-book.org/internals#sec-ggproto, and https://ggplot2-book.org/extensions#new-positions

aphalo commented 1 month ago

Using compute_panel works only if all layers share the same data, at least without implementing a work-around. I ended implementing the ability to use an expanded or contracted range based on the data in the layer. Does not necesarily make use of all the available room for spreading the positions but gives an option that does not require arbitrary values entered mannually. Action "expand" is not needed. Parameter distance is ready to be used when a different strategy for spreading is implemented in the future. Current strategy is distance = "equal". In the future a different strategy that only ensures a minimum distance would be useful.

At this point, I set this issue back to on hold for the implementation of a different strategy. Alternatively, functions could be accepted as arguments to distance.