Skip to content

Commit b459d65

Browse files
committed
Merge branch 'dev'
2 parents 08389ab + a445a86 commit b459d65

18 files changed

Lines changed: 1282 additions & 17 deletions

AGENTS.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,28 @@
103103
对标 Vega-Lite / AntV-G2 的视觉通道丰富度,不限于 ggplot2 原生几何。
104104
新 mark 按需引入,遵循统一 S7 泛型+方法模式(`mark_<type>` + `geom_<底层>`),支持 `rasterize`
105105

106-
**已实现**10):
106+
**已实现**18):
107107

108108
| 函数 | 对应 | 用途 |
109109
|---|---|---|
110-
| `mark_point` | `geom_point` | 散点 |
111-
| `mark_line` | `geom_line` | 折线/趋势 |
112-
| `mark_bar` | `geom_bar`/`geom_col` | 柱状图(有 y 映射→`geom_col`,无 y→`geom_bar`|
113-
| `mark_boxplot` | `geom_boxplot` | 箱线图 |
114-
| `mark_histogram` | `geom_histogram` | 直方图 |
115-
| `mark_density` | `geom_density` | 密度曲线 |
116-
| `mark_area` | `geom_area` | 面积图/堆叠面积 |
117-
| `mark_text` | `geom_text`/`geom_text_repel` | 文本标签/数据标注 |
118-
| `mark_violin` | `geom_violin` | 小提琴图 |
119-
| `mark_map` | `geom_sf` | 地图/地理空间 |
110+
| `mark_point` | `geom_point` | 散点 ✅ |
111+
| `mark_line` | `geom_line` | 折线/趋势 ✅ |
112+
| `mark_bar` | `geom_bar`/`geom_col` | 柱状图 ✅ |
113+
| `mark_boxplot` | `geom_boxplot` | 箱线图 ✅ |
114+
| `mark_histogram` | `geom_histogram` | 直方图 ✅ |
115+
| `mark_density` | `geom_density` | 密度曲线 ✅ |
116+
| `mark_area` | `geom_area` | 面积图 ✅ |
117+
| `mark_text` | `geom_text`/`geom_text_repel` | 文本标签 ✅ |
118+
| `mark_violin` | `geom_violin` | 小提琴图 ✅ |
119+
| `mark_map` | `geom_sf` | 地图 ✅ |
120+
| `mark_rect` | `geom_tile` | 矩形/热力图 ✅ |
121+
| `mark_rule` | `geom_hline/vline/abline/segment` | 参考线/参考区域 ✅ |
122+
| `mark_path` | `geom_path` | 路径/轨迹 ✅ |
123+
| `mark_polygon` | `geom_polygon` | 多边形 ✅ |
124+
| `mark_smooth` | `geom_smooth` | 回归平滑+置信带 ✅ |
125+
| `mark_hex` | `geom_hex` | 2D 六边形热力图 ✅ |
126+
| `mark_density_2d` | `geom_density_2d`/`geom_density_2d_filled` | 2D 密度等高线 ✅ |
127+
| `mark_corr` | `geom_tile` + corr 预处理 | 相关性矩阵热力图 ✅ |
120128

121129
#### Vega-Lite vs AntV G2 复合 Mark 全量对比
122130

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Imports:
2626
S7 (>= 0.1.0),
2727
cli,
2828
patchwork
29-
Collate:
29+
Collate:
3030
'class.R'
3131
'utils.R'
3232
'style.R'
@@ -35,6 +35,7 @@ Collate:
3535
'compose.R'
3636
'encode.R'
3737
'mark.R'
38+
'factory.R'
3839
'plot.R'
3940
'project.R'
4041
'scale.R'

NAMESPACE

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,24 @@ export(label_caption)
1515
export(label_legend)
1616
export(label_subtitle)
1717
export(label_title)
18+
export(make_mark)
19+
export(make_theme)
1820
export(mark_area)
1921
export(mark_bar)
2022
export(mark_boxplot)
23+
export(mark_corr)
2124
export(mark_density)
25+
export(mark_density_2d)
26+
export(mark_hex)
2227
export(mark_histogram)
2328
export(mark_line)
2429
export(mark_map)
30+
export(mark_path)
2531
export(mark_point)
32+
export(mark_polygon)
33+
export(mark_rect)
34+
export(mark_rule)
35+
export(mark_smooth)
2636
export(mark_text)
2737
export(mark_violin)
2838
export(plotit)

R/factory.R

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#' @include class.R mark.R
2+
NULL
3+
4+
# ---- make_mark ----
5+
#' Create a custom mark
6+
#'
7+
#' Registers a new S7 generic + method from any ggplot2 geom function,
8+
#' making it available in the plotit pipeline. The new mark behaves
9+
#' identically to built-in marks: it supports `mapping`, `data`,
10+
#' `position`, auto-dodge, and rasterization.
11+
#'
12+
#' @param name Mark name as a string (e.g. `"mark_spoke"`).
13+
#' Should start with `"mark_"`.
14+
#' @param geom_fun A ggplot2 geom function
15+
#' (e.g. `ggplot2::geom_spoke`).
16+
#' @return Invisibly returns the registered S7 generic.
17+
#' @examples
18+
#' make_mark("mark_spoke", ggplot2::geom_spoke)
19+
#' # Now usable in pipeline:
20+
#' \dontrun{
21+
#' df |> plotit(encode(x = x, y = y, radius = r, angle = a)) |> mark_spoke()
22+
#' }
23+
#' @export
24+
make_mark <- function(name, geom_fun) {
25+
if (!is.character(name) || length(name) != 1) {
26+
cli::cli_abort("{.arg name} must be a single string.")
27+
}
28+
if (!grepl("^mark_", name)) {
29+
cli::cli_warn(
30+
"{.arg name} should start with 'mark_', got {.val {name}}."
31+
)
32+
}
33+
34+
generic <- S7::new_generic(
35+
name, "plot",
36+
function(plot, mapping = NULL, data = NULL, position = NULL, ...,
37+
rasterize = FALSE, rasterize_dpi = 300, rasterize_dev = "cairo") {
38+
S7::S7_dispatch()
39+
}
40+
)
41+
._register_mark_method(generic, geom_fun)
42+
invisible(generic)
43+
}
44+
45+
# ---- make_theme ----
46+
#' Create a reusable theme preset
47+
#'
48+
#' Builds a theme function from `ggplot2::theme()` elements and
49+
#' an optional base theme. The returned function applies the theme
50+
#' to a plotit object and can be used anywhere `style()` is used.
51+
#'
52+
#' @param name Name for the theme function as a string
53+
#' (e.g. `"style_dark"`).
54+
#' @param ... Theme elements passed to [ggplot2::theme()].
55+
#' @param base_theme A base ggplot2 theme function
56+
#' (default: [ggplot2::theme_minimal]).
57+
#' @return Invisibly returns the created function.
58+
#' @examples
59+
#' style_dark <- make_theme("style_dark",
60+
#' plot.background = ggplot2::element_rect(fill = "#1a1a1a"),
61+
#' text = ggplot2::element_text(colour = "white"))
62+
#' \dontrun{
63+
#' plotit(iris, encode(x = Sepal.Width, y = Sepal.Length)) |>
64+
#' mark_point() |> style_dark()
65+
#' }
66+
#' @export
67+
make_theme <- function(name, ..., base_theme = ggplot2::theme_minimal) {
68+
force(name)
69+
force(base_theme)
70+
dots <- rlang::list2(...)
71+
72+
fun <- function(plot, base_size = NULL, base_family = NULL) {
73+
thm <- base_theme(
74+
base_size = base_size %||% 11,
75+
base_family = base_family %||% ""
76+
) + do.call(ggplot2::theme, dots)
77+
plot@gg <- plot@gg + thm
78+
plot
79+
}
80+
assign(name, fun, envir = parent.frame())
81+
invisible(fun)
82+
}

0 commit comments

Comments
 (0)