Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ca8dc61
sometimes the agent needs to run roxygen
yihui Mar 30, 2026
5bb5c03
Fix broken coordinate/mark examples: parallel, radar, density, partition
Copilot Mar 30, 2026
e6e8286
Merge remote-tracking branch 'origin/copilot/fix-broken-coordinate-ma…
Copilot Mar 30, 2026
c46688f
Merge branch 'main' into copilot/fix-broken-coordinate-mark-examples
yihui-bot Mar 30, 2026
8ffb4db
Merge branch 'main' into copilot/fix-broken-coordinate-mark-examples
yihui-bot Mar 30, 2026
869a92d
no need to add news [ci skip]
yihui Mar 30, 2026
bffd9f1
Fix dark theme backgrounds, improve coordinate examples, add all miss…
Copilot Mar 30, 2026
da9b6f4
Align coordinate example data between docs and Rmd, add dark theme co…
Copilot Mar 30, 2026
7f0c72a
Fix all chart examples: radar, parallel, marks, gauge, sankey, chord,…
Copilot Mar 31, 2026
4587114
Mark shape example as eval=FALSE due to G2 legend/plot shape mismatch
Copilot Mar 31, 2026
9bbf6c1
Fix all chart examples per reviewer feedback: radar, parallel, transp…
Copilot Mar 31, 2026
ca0f631
Merge branch 'main' into copilot/fix-broken-coordinate-mark-examples
yihui-bot Mar 31, 2026
2e90ee3
Fix reviewer feedback: padding_of(), graph mark labels, lineX/lineY s…
Copilot Mar 31, 2026
d1ba36c
larger points so we can see colors more clearly
yihui Mar 31, 2026
069a0b6
blue stroke, yellow fill
yihui Mar 31, 2026
0bd2a54
try to transpose parallel coord
yihui Mar 31, 2026
1259189
delete garbage [ci skip]
yihui Mar 31, 2026
fd78572
instructions on filing new issues [ci skip]
yihui Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,23 @@ no errors in the browser console. The workflow is:
- The G2 chart renders without JavaScript errors.
- No warnings or errors appear in the browser console.

### Submitting Plot Changes in PRs

When fixing or changing plot examples, **always submit screenshots** of the
plots as PR comments so reviewers can see the visual results. Take screenshots
in headless browsers (Playwright or Puppeteer) and attach them to the PR.

### G2 Reference

When in doubt about G2 features, marks, or coordinate systems, always refer to:
- **G2 source repository**: https://github.com/antvis/G2
- **G2 documentation site**: https://g2.antv.antgroup.com

If you have consulted the source repository and documentation but still cannot
solve a problem raised in a PR, **file a GitHub issue for that problem to keep
track of it**. The issue should clearly describe what the problem is, and why
you cannot solve it.

## Project Structure

### Key Files and Directories
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export(mark_tree)
export(mark_treemap)
export(mark_vector)
export(mark_word_cloud)
export(padding_of)
export(preview)
export(renderG2)
export(scale_of)
Expand Down
22 changes: 22 additions & 0 deletions R/component.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
#' Set Chart Padding
#'
#' Set padding around the chart plotting area. This is useful when axis
#' labels or titles are cut off.
#'
#' @param chart A `g2` object.
#' @param top,right,bottom,left Padding in pixels for each side.
#' @return The modified `g2` object.
#' @export
#' @examples
#' g2(mtcars, x = 'mpg', y = 'hp') |>
#' mark_point() |>
#' padding_of(top = 30)
padding_of = function(chart, top = NULL, right = NULL, bottom = NULL,
left = NULL) {
if (!is.null(top)) chart$padding$paddingTop = top
if (!is.null(right)) chart$padding$paddingRight = right
if (!is.null(bottom)) chart$padding$paddingBottom = bottom
if (!is.null(left)) chart$padding$paddingLeft = left
chart
}

#' Configure an Axis
#'
#' Customise the axis for a positional channel (`'x'` or `'y'`). Set to
Expand Down
26 changes: 23 additions & 3 deletions R/coordinate.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#' transforms such as `'transpose'` (equivalent to ggplot2's `coord_flip()`) or
#' `'fisheye'`.
#'
#' The `'parallel'` coordinate requires a `position` encoding (a character
#' vector of column names) instead of separate `x`/`y` encodings. For radar
#' charts, use the `'polar'` coordinate with long-format data (x/y/color).
#'
#' @param chart A `g2` object.
#' @param type Coordinate type string.
#' @param ... Additional options such as `innerRadius`, `outerRadius`,
Expand All @@ -30,11 +34,27 @@
#' mark_interval() |>
#' coordinate('radial')
#'
#' # Parallel coordinate
#' # Parallel coordinate (uses position encoding)
#' g2(iris, position = c('Sepal.Length', 'Sepal.Width',
#' 'Petal.Length', 'Petal.Width'), color = 'Species') |>
#' mark_line(transform = list(list(type = 'normalizeY'))) |>
#' coordinate('parallel')
#' mark_line() |>
#' coordinate('parallel') |>
#' legend_of('color', position = 'bottom') |>
#' padding_of(top = 30)
#'
#' # Radar coordinate (polar with long-format data)
#' df2 = data.frame(
#' item = rep(c('Design', 'Dev', 'Marketing', 'Sales', 'Support'), 2),
#' score = c(80, 90, 65, 75, 85, 60, 70, 85, 80, 70),
#' team = rep(c('A', 'B'), each = 5)
#' )
#' g2(df2, x = 'item', y = 'score', color = 'team') |>
#' mark_area(style = list(fillOpacity = 0.5)) |>
#' mark_line(style = list(lineWidth = 2)) |>
#' coordinate('polar') |>
#' scale_of('x', padding = 0.5, align = 0) |>
#' scale_of('y', domainMin = 0, domainMax = 100) |>
#' axis_of('x', grid = TRUE)
coordinate = function(chart, type, ...) {
chart$coords = c(list(type = type), list(...))
chart
Expand Down
7 changes: 6 additions & 1 deletion R/gglite.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ g2 = function(data = NULL, ..., width = 640, height = 480) {
axes = list(),
legends = list(),
chart_title = NULL,
facet = NULL
facet = NULL,
padding = list()
), class = 'g2')
dots = list(...)
if (length(dots)) chart$aesthetics = modifyList(chart$aesthetics, dots)
Expand Down Expand Up @@ -94,3 +95,7 @@ annotate_df = function(x) {
}
x
}

#' Remove NULL elements from a list
#' @keywords internal
dropNulls = function(x) x[!vapply(x, is.null, logical(1))]
28 changes: 6 additions & 22 deletions R/mark.R
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,12 @@ mark_boxplot = function(chart, ...) mark(chart, 'boxplot', ...)

#' Add a Density Mark
#'
#' A composite mark for kernel density estimation visualization.
#' A composite mark for kernel density estimation visualization. Note that
#' G2 v5 does not support the `kde` transform; use [mark_heatmap()] as an
#' alternative for 2-D density visualizations.
#'
#' @inheritParams mark
#' @export
#' @examples
#' g2(iris, x = 'Sepal.Width', y = 'Sepal.Length') |>
#' mark_density(
#' transform = list(list(type = 'kde')),
#' style = list(fill = 'steelblue', fillOpacity = 0.5)
#' )
mark_density = function(chart, ...) mark(chart, 'density', ...)

#' Add a Heatmap Mark
Expand Down Expand Up @@ -435,21 +431,9 @@ mark_shape = function(chart, ...) mark(chart, 'shape', ...)

#' Add a Partition (Sunburst) Mark
#'
#' Note: the `partition` mark may not work correctly in G2 v5. Consider using
#' [mark_treemap()] as an alternative for hierarchical data visualization.
#'
#' @inheritParams mark
#' @export
#' @examples
#' tree_data = list(
#' name = 'root', children = list(
#' list(name = 'A', value = 10, children = list(
#' list(name = 'A1', value = 5), list(name = 'A2', value = 5)
#' )),
#' list(name = 'B', value = 20)
#' )
#' )
#' g2() |>
#' mark_partition(
#' data = list(value = tree_data),
#' encode = list(value = 'value'),
#' coordinate = list(type = 'theta', innerRadius = 0.3)
#' )
mark_partition = function(chart, ...) mark(chart, 'partition', ...)
14 changes: 12 additions & 2 deletions R/render.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build_config = function(chart) {
if (!is.null(chart$tooltip_config)) config$tooltip = chart$tooltip_config
if (!is.null(chart$sliders)) config$slider = chart$sliders
if (!is.null(chart$scrollbars)) config$scrollbar = chart$scrollbars
if (length(chart$padding)) config = modifyList(config, chart$padding)

# Faceting wraps the spec as a facet view
if (!is.null(chart$facet)) {
Expand Down Expand Up @@ -84,12 +85,18 @@ chart_html = function(chart, id = NULL, width = NULL, height = NULL) {
defer_opt = getOption('gglite.defer_render')
threshold = if (isTRUE(defer_opt)) 0.5 else if (is.numeric(defer_opt)) defer_opt

# G2 dark themes render content with light colors on a transparent canvas;
# set a dark background so the chart is visible on light pages
dark = isTRUE(chart$theme$type %in% c('dark', 'classicDark'))
w = if (!is.null(width)) paste0('width:', width, 'px;') else ''
h = if (!is.null(height)) paste0('height:', height, 'px;') else ''
style = paste0(w, h)
if (nzchar(style)) style = paste0(' style="', style, '"')
bg = if (dark) 'background-color:#141414;' else ''

if (!is.null(threshold)) {
# Ensure container has min-height so IntersectionObserver can trigger
ch = if (is.null(ctor$height)) 480 else ctor$height
mh = paste0('min-height:', ch, 'px;')
style = paste0(w, h, bg, mh)
spec_js = paste0('const spec = ', xfun::tojson(spec), ';\n')
options_js = 'chart.options(spec);\n'
render_js = paste0(
Expand All @@ -102,11 +109,14 @@ chart_html = function(chart, id = NULL, width = NULL, height = NULL) {
' }).observe(document.getElementById("', id, '"));\n'
)
} else {
style = paste0(w, h, bg)
spec_js = ''
options_js = paste0('chart.options(', xfun::tojson(spec), ');\n')
render_js = 'chart.render();\n'
}

if (nzchar(style)) style = paste0(' style="', style, '"')

paste0(
'<div id="', id, '"', style, '></div>\n',
'<script type="module">\n',
Expand Down
56 changes: 52 additions & 4 deletions examples/coordinates.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,54 @@ g2(df, x = 'x', y = 'y', color = 'x') |>
coordinate('radial', innerRadius = 0.3)
```

## Parallel

Maps multiple numeric variables to parallel axes. Use a `position`
encoding (a character vector of column names) instead of `x`/`y`.

```{r}
g2(iris, position = c('Sepal.Length', 'Sepal.Width',
'Petal.Length', 'Petal.Width'), color = 'Species') |>
mark_line() |>
coordinate('parallel') |>
legend_of('color', position = 'bottom') |>
padding_of(top = 30)
```

## Radar

Displays data on radial axes emanating from a center point. A radar chart
is a line or area chart in polar coordinates. Use long-format data with
`x` (category), `y` (value), and `color` (series) encodings. All values
should be on the same scale (e.g., 0--100).

```{r}
df_radar = data.frame(
item = rep(c('Design', 'Dev', 'Marketing', 'Sales', 'Support'), 2),
score = c(80, 90, 65, 75, 85, 60, 70, 85, 80, 70),
team = rep(c('A', 'B'), each = 5)
)
g2(df_radar, x = 'item', y = 'score', color = 'team') |>
mark_area(style = list(fillOpacity = 0.5)) |>
mark_line(style = list(lineWidth = 2)) |>
mark_point() |>
coordinate('polar') |>
scale_of('x', padding = 0.5, align = 0) |>
scale_of('y', domainMin = 0, domainMax = 100) |>
axis_of('y', grid = TRUE, title = FALSE)
```

## Helix

Arranges data along a helix spiral. Works best with `mark_interval()`.

```{r}
df_helix = data.frame(x = paste0('D', 1:50), y = abs(sin(1:50 / 5)))
g2(df_helix, x = 'x', y = 'y', color = 'y') |>
mark_interval() |>
coordinate('helix')
```

## Transpose (coord_flip)

Swap x and y axes, equivalent to ggplot2's `coord_flip()`. This is a
Expand All @@ -98,11 +146,11 @@ g2(df, x = 'x', y = 'y') |>
coord_transpose()
```

### Transpose with polar
### Transpose with parallel

```{r}
g2(df, x = 'x', y = 'y', color = 'x') |>
mark_interval() |>
coordinate('polar') |>
g2(iris, position = names(iris)[-5], color = 'Species') |>
mark_line() |>
coordinate('parallel') |>
coord_transpose()
```
Loading