79310772

Date: 2024-12-27 02:26:57
Score: 1.5
Natty:
Report link

I have fixed this, here is the code I used. I've built a new layout option in my package ggalign that arranges multiple plots in circular tracks. Each plot fits into its designated circle track while sharing the same coordinate origin (0, 0) based on this idea.


draw_ggcircle_list <- function(plot_list, start = 0L, end = 2 * pi,
                               inner_radius = 0.1) {
    sizes <- rep_len(1, length(plot_list))

    # for every plot track, all relative to the total radius `1`
    plot_track <- sizes / sum(sizes) * (1 - inner_radius)
    plot_sizes <- 1 - cumsum(c(0, plot_track[-length(plot_track)]))
    plot_inner <- plot_sizes - plot_track
    plot_table <- origin <- NULL
    for (i in rev(seq_along(plot_list))) { # from inner-most to the out-most
        plot_size <- plot_sizes[[i]]
        plot <- .subset2(plot_list, i) +
            ggplot2::coord_radial(
                start = start, end = end,
                inner.radius = plot_inner[[i]] / plot_size,
                r.axis.inside = TRUE
            ) +
            ggplot2::labs(x = NULL, y = NULL)

        # copied from `ggplot2:::ggplot_gtable`
        data <- ggplot2::ggplot_build(plot)
        plot <- data$plot
        layout <- data$layout
        data <- data$data
        theme <- ggplot2:::plot_theme(plot$theme)

        geom_grobs <- ggplot2:::by_layer(
            function(l, d) l$draw_geom(d, layout),
            plot$layers, data,
            "converting geom to grob"
        )
        gt <- layout$render(geom_grobs, data, theme, plot$labels)

        # for each inner gtable, we insert it to the panel area of the
        # outter gtable
        #
        # how to get the coordinate origin from the `coord_radial()` ?
        # origin <- layout$coord$transform(
        #     data.frame(x = 0.5, y = 0.5),
        #     panel_params = layout$panel_params[[1L]]
        # )
        # For bbox, `ggplot2::polar_bbox` always take (0.5, 0.5) as origin
        bbox <- layout$panel_params[[1L]]$bbox
        just <- c(
            scales::rescale(0.5, from = bbox$x),
            scales::rescale(0.5, from = bbox$y)
        )

        if (is.null(plot_table)) {
            plot_table <- gt
        } else {
            # define the panel size of the inner track
            rescale_factor <- last_plot_size / plot_size

            # just using panel spacing as the spacer between two plots
            spacing <- ggplot2::calc_element("panel.spacing.y", theme)

            plot_table <- grid::editGrob(plot_table, vp = grid::viewport(
                width = grid::unit(rescale_factor, "npc") - spacing,
                height = grid::unit(rescale_factor, "npc") - spacing,
                x = origin[1L], y = origin[2L],
                just = just,
                default.units = "native",
                clip = "off"
            ))

            # add the inner track to the panel area of the outter track
            out_panel <- ggplot2::find_panel(gt)
            plot_table <- gtable::gtable_add_grob(
                gt, plot_table,
                t = .subset2(out_panel, "t"),
                l = .subset2(out_panel, "l"),
                b = .subset2(out_panel, "b"),
                r = .subset2(out_panel, "r"),
                name = "inner-track"
            )
        }
        origin <- just
        last_plot_size <- plot_size # the last plot panel size
    }
    plot_table
}

library(ggplot2)

# draw_circle_list will convert all plot into polar coordinate
p1 <- ggplot(mpg, aes(class, displ)) +
    geom_boxplot() +
    theme(plot.background = element_rect(fill = "red")) +
    ggplot2::guides(
        theta = ggplot2::guide_axis_theta(angle = 0),
        r     = ggplot2::guide_axis(angle = 0)
    ) +
    ggplot2::theme(axis.line.theta = ggplot2::element_line())

grid::grid.draw(draw_ggcircle_list(list(p1, p1)))

enter image description here

Reasons:
  • Probably link only (1):
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: Yun