-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgg.lisp
More file actions
109 lines (95 loc) · 4.48 KB
/
gg.lisp
File metadata and controls
109 lines (95 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
;;; -*- Mode: LISP; Syntax: Ansi-Common-Lisp; Base: 10; Package: GG -*-
;;; Copyright (c) 2026 Symbolics Pte. Ltd. All rights reserved.
;;; Geometry helpers for common Vega-Lite plot types.
(in-package #:gg)
(defun label (&key x y) ;'labels' is a CL keyword
"Return a plist with axis title encodings, suitable for merging.
Sets the :title property directly on each encoding channel rather than
nesting inside :axis, so that composite marks like boxplot (which
expand into multiple sub-layers) resolve a single authoritative title
instead of concatenating the field name with per-layer axis titles."
`(,@(when (or x y)
`(:encoding (,@(when x `(:x (:title ,x)))
,@(when y `(:y (:title ,y))))))))
(defun axes (&key x-type y-type
x-domain y-domain
x-range y-range
color-scheme)
"Return a plist with scale encodings, suitable for merging.
X-TYPE/Y-TYPE: :log :sqrt :time etc.
X-DOMAIN/Y-DOMAIN: explicit (min max) domain list
X-RANGE/Y-RANGE: explicit (min max) range list
COLOR-SCHEME: a Vega scheme name e.g. :category10 :viridis"
`(,@(when (or x-type x-domain x-range)
`(:encoding (:x (:scale (,@(when x-type `(:type ,x-type))
,@(when x-domain `(:domain ,x-domain))
,@(when x-range `(:range ,x-range)))))))
,@(when (or y-type y-domain y-range)
`(:encoding (:y (:scale (,@(when y-type `(:type ,y-type))
,@(when y-domain `(:domain ,y-domain))
,@(when y-range `(:range ,y-range)))))))
,@(when color-scheme
`(:encoding (:color (:scale (:scheme ,color-scheme)))))))
(defun tooltip (&rest fields)
"Return encoding plist adding tooltip channels.
Each field is one of:
:keyword — nominal field (shorthand)
(:keyword :type) — field with explicit type, e.g. (:horsepower :quantitative)
(:field f :type t) — full Vega-Lite channel plist (passed through)
The result uses a vector so the serializer emits a JSON array."
(let ((channels (mapcar (lambda (f)
(cond ((keywordp f)
`(:field ,f :type :nominal))
((and (listp f)
(= (length f) 2)
(keywordp (first f))
(keywordp (second f)))
`(:field ,(first f) :type ,(second f)))
((listp f) f)
(t `(:field ,f :type :nominal))))
fields)))
`(:encoding (:tooltip ,(coerce channels 'vector)))))
(defun coord (&key x-domain y-domain (clip t))
"Return plist clamping axis domains and optionally clipping marks.
Like ggplot2's coord_cartesian: restricts the visible area without
dropping data.
X-DOMAIN / Y-DOMAIN:
- For quantitative axes: a vector #(min max)
- For categorical axes: a vector of category names #(\"A\" \"B\" \"C\")
CLIP: if T (default), marks outside the domain are hidden.
Set to NIL for bar/boxplot where clipping cuts marks."
`(,@(when clip `(:mark (:clip t)))
,@(when x-domain
`(:encoding (:x (:scale (:domain ,x-domain)))))
,@(when y-domain
`(:encoding (:y (:scale (:domain ,y-domain)))))))
(defun theme (&key width height font background
(view-stroke :null)
padding)
"Return plist for layout and theme settings.
WIDTH, HEIGHT: pixel dimensions
FONT: base font family string — applied to the entire chart via config.font
BACKGROUND: CSS color string for the chart background
VIEW-STROKE: stroke around the view rectangle; :null (default) removes it, a string sets a CSS color, NIL omits the property entirely
PADDING: padding in pixels (number) or plist (:left :right :top :bottom)
Like ggplot2's coord_cartesian: restricts the visible area without
dropping data.
X-DOMAIN / Y-DOMAIN:
- For quantitative axes: a vector #(min max)
- For categorical axes: a vector of category names #(\"A\" \"B\" \"C\")
CLIP: if T (default), marks outside the domain are hidden.
Set to NIL for bar/boxplot where clipping cuts marks."
(let ((config-props `(,@(when font `(:font ,font))
,@(unless (null view-stroke)
`(:view (:stroke ,view-stroke))))))
`(,@(when width `(:width ,width))
,@(when height `(:height ,height))
,@(when background `(:background ,background))
,@(when padding `(:padding ,padding))
,@(when config-props `(:config ,config-props)))))
(defun layer (&rest geom-plists)
"Wrap two or more geom plists into a Vega-Lite layer spec. Each argument should be the plist returned by a geom:* function.
EXAMPLE
(gg:layer (geom:line :date :price :x-type :temporal)
(geom:error-bar :price :category :date :extent :ci))"
`(:layer ,(apply #'vector geom-plists)))