Skip to content

Commit 9155ee8

Browse files
Merge pull request #31 from JamesxX/bar-element
Bar element and barchart
2 parents 6be438e + 443806b commit 9155ee8

File tree

25 files changed

+771
-4
lines changed

25 files changed

+771
-4
lines changed

gallery/barchart.typ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
chart.barchart(mode: "clustered",
1818
size: (9, auto),
1919
label-key: 0,
20-
value-key: (..range(1, 5)),
20+
value-key: (..range(1, 4)),
2121
bar-width: .8,
2222
x-tick-step: 2.5,
2323
data2,

manual.pdf

61.1 KB
Binary file not shown.

manual.typ

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,19 @@ You can use the following options to customize each axis of the plot. You must p
226226
to its end.
227227
]
228228

229-
// = Chart
229+
#pagebreak()
230+
== Add
231+
232+
#doc-style.parse-show-module("/src/plot/elements/bar.typ", first-heading-level: 2)
233+
234+
235+
#pagebreak()
236+
= Chart
237+
238+
== Bar
239+
240+
#doc-style.parse-show-module("/src/charts/bar/simple.typ", first-heading-level: 2)
241+
#pagebreak()
242+
#doc-style.parse-show-module("/src/charts/bar/clustered.typ", first-heading-level: 2)
243+
#pagebreak()
244+
#doc-style.parse-show-module("/src/charts/bar/stacked.typ", first-heading-level: 2)

src/chart.typ

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import "charts/bar/bar.typ" as bar: stacked, clustered, simple

src/charts/bar/bar.typ

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#import "clustered.typ": clustered
2+
#import "stacked.typ": stacked, stacked100
3+
#import "simple.typ": simple

src/charts/bar/clustered.typ

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#import "/src/cetz.typ": canvas, palette
2+
#import "plotter.typ": plotter
3+
4+
/// Render a clustered bar chart
5+
///
6+
/// ```example
7+
/// cetz-plot.chart.bar.clustered(
8+
/// size: (4,4),
9+
/// (
10+
/// ([One], 1, 1, 2, 3),
11+
/// ([Two], 3, 1, 1 ,1),
12+
/// ([Three], 3, 2, 1, 3),
13+
/// ),
14+
/// label-key: 0,
15+
/// y-keys: (1,2,3,4),
16+
/// labels: (
17+
/// $0 -> 24$,
18+
/// $25 -> 49$,
19+
/// $50 -> 74$,
20+
/// $75 -> 100$
21+
/// ),
22+
/// )
23+
/// ```
24+
/// - data (array): An array of clusers to plot. Each entry can include a label
25+
/// for the cluster, shown on the `x` axis, a number of `y` coordinates that
26+
/// represent the magnitude of a bar that starts at 0, and optionally a
27+
/// corresponding number of `y-error` magnitudes for each bar.
28+
/// - labels (array): An array of either content or none, to be shown in the legend
29+
/// for its corresponding series. The n'th y-keys series is labelled by the
30+
/// n'th label (or none).
31+
/// - label-key (string, int): The key at which the x-axis label is described in
32+
/// each data entry.
33+
/// - y-keys (array): The n'th entry in `y-keys` corresponds to the key at which
34+
/// the `y` coordinate can be found in each data entry, for the n'th series.
35+
/// - y-error-keys (any): The n'th entry in `y-error-keys` corresponds to the key at
36+
/// which the `y-error` magnitude (as a float or as a tuple) can be found in
37+
/// each data entry, for the n'th series.
38+
/// - bar-width (float): The width of the bar along the `x` axis, in data-viewport
39+
/// space. The bar is drawn centered about its `x` coordinate, therefore, the bar
40+
/// extends by $#raw("bar-width")\/2$ either side.
41+
/// - bar-spacing (float): The spacing between bars within a cluster, in data-viewprot
42+
/// space.
43+
/// - bar-style (style): Style to use, can be used with a `palette` function
44+
/// - axes (axes): Name of the axes to use for plotting. Reversing the axes
45+
/// means rotating the plot by 90 degrees.
46+
/// - ..plot-args (variadic): Additional plotting parameters and axis options to be
47+
/// passed to @@plot
48+
#let clustered(
49+
data,
50+
labels: (),
51+
label-key: 0,
52+
y-keys: (1,),
53+
y-error-keys: none,
54+
bar-width: 0.7,
55+
bar-spacing: 0,
56+
bar-style: palette.red,
57+
axes: ("x", "y"),
58+
..plot-args
59+
) = {
60+
let series-count = y-keys.len()
61+
bar-width /= series-count
62+
let cluster-width = series-count * bar-width + (series-count - 1) * bar-spacing
63+
64+
let series-data = ()
65+
66+
for (series-index, y-key) in y-keys.enumerate() {
67+
68+
series-data.push(
69+
(
70+
label: if label-key != none {labels.at(series-index)},
71+
data: for (observation-index, observation) in data.enumerate() {
72+
let x = observation-index - cluster-width/2 + series-index * (bar-width + bar-spacing) + bar-width/2
73+
let y = observation.at(y-key, default: 0)
74+
((
75+
x: x,
76+
y: y,
77+
y-error: if y-error-keys != none {
78+
let err-key = y-error-keys.at(series-index, default: none)
79+
if err-key != none {observation.at(err-key, default: 0)}
80+
}
81+
),)
82+
}
83+
)
84+
)
85+
}
86+
87+
plotter(
88+
data,
89+
series-data,
90+
x-key: "x",
91+
y-key: "y",
92+
y-error-key: if y-error-keys != none {"y-error"},
93+
label-key: label-key,
94+
bar-width: bar-width,
95+
bar-style: bar-style,
96+
axes: axes,
97+
..plot-args,
98+
)
99+
}

src/charts/bar/plotter.typ

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#import "/src/cetz.typ": draw, styles, palette
2+
#import "/src/plot.typ": plot
3+
#import "/src/plot/add.typ" as add: series, bar, errorbar
4+
#import "style.typ": barchart-default-style
5+
6+
#let plotter(
7+
data,
8+
series-data,
9+
x-key: "x",
10+
y-key: "y",
11+
y-error-key: none,
12+
y-offset-key: none,
13+
label-key: none,
14+
bar-width: 0.7,
15+
bar-style: palette.red,
16+
axes: ("x", "y"),
17+
..plot-args,
18+
) = {
19+
20+
draw.group(ctx => {
21+
22+
// Setup styles
23+
let style = styles.resolve(
24+
ctx.style,
25+
merge: (:),
26+
root: "barchart",
27+
base: barchart-default-style
28+
)
29+
draw.set-style(..style)
30+
31+
plot(
32+
33+
// To do: Is there a better way to setup the x-axis using custom axis-style
34+
x-min: -0.75, x-max: data.len() - 0.25,
35+
x-tick-step: if label-key == none {1},
36+
x-ticks: if label-key != none {
37+
data.map((d)=>d.at(label-key, default: none)).enumerate()
38+
} else {()},
39+
40+
y-grid: true,
41+
42+
plot-style: bar-style,
43+
..plot-args,
44+
45+
// Body argument: An array of series
46+
for (label, data) in series-data {
47+
add.series(
48+
label: label,
49+
{
50+
add.bar(
51+
data,
52+
x-key: x-key,
53+
y-key: y-key,
54+
y-offset-key: y-offset-key,
55+
bar-width: bar-width,
56+
axes: axes,
57+
)
58+
59+
if y-error-key != none {
60+
add.errorbar(
61+
data,
62+
x-key: x-key,y-key: y-key, y-error-key: y-error-key,
63+
axes: axes,
64+
)
65+
}
66+
}
67+
)
68+
}
69+
)
70+
})
71+
}

src/charts/bar/simple.typ

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#import "/src/cetz.typ": canvas, palette, draw, styles
2+
#import "/src/plot.typ": plot
3+
#import "/src/plot/add.typ" as add: series, bar, errorbar
4+
#import "style.typ": barchart-default-style
5+
6+
/// Render a single series as a barchart
7+
///
8+
/// ```example
9+
/// cetz-plot.chart.bar.simple(
10+
/// size: (4,4),
11+
/// label-key: 0,
12+
/// y-key: 1,
13+
/// y-error-key: 2,
14+
/// label: [label],
15+
/// (
16+
/// ([One], 1, 0.5),
17+
/// ([Two], 3, 0.75),
18+
/// ([Three], 2, 1),
19+
/// ),
20+
/// )
21+
/// ```
22+
/// - data (array): An array of bars to plot. Each entry can include a label
23+
/// for the bar, shown on the `x` axis, a `y` coordinates that
24+
/// represents the magnitude of a bar that starts at 0, and optionally a
25+
/// `y-error` magnitude.
26+
/// - label (content, none): Optional label to be shown in legend
27+
/// - label-key (string, int): The key at which the x-axis label is described in
28+
/// each data entry.
29+
/// - y-key (string, int): The key at which the `y` coordinate is described in each
30+
/// data entry.
31+
/// - y-error-key (string, int, none): Optionally where `y-error` coordinate is
32+
/// described in each data entry.
33+
/// - bar-width (float): The width of the bar along the `x` axis, in data-viewport
34+
/// space. The bar is drawn centered about its `x` coordinate, therefore, the bar
35+
/// extends by $#raw("bar-width")\/2$ either side.
36+
/// - bar-style (style): Style to use, can be used with a `palette` function
37+
/// - axes (axes): Name of the axes to use for plotting. Reversing the axes
38+
/// means rotating the plot by 90 degrees.
39+
/// - ..plot-args (variadic): Additional plotting parameters and axis options to be
40+
/// passed to @@plot
41+
#let simple(
42+
data,
43+
label: none,
44+
label-key: 0,
45+
y-key: 1,
46+
y-error-key: none,
47+
bar-width: 0.7,
48+
bar-style: palette.red,
49+
axes: ("x", "y"),
50+
..plot-args
51+
) = {
52+
53+
let data = data.enumerate().map(((index, entry))=> (
54+
index,
55+
entry.at(y-key, default: 0),
56+
if y-error-key != none {entry.at(y-error-key, default: none)},
57+
entry.at(label-key, default: none)
58+
))
59+
60+
draw.group(ctx => {
61+
62+
// Setup styles
63+
let style = styles.resolve(
64+
ctx.style,
65+
merge: (:),
66+
root: "barchart",
67+
base: barchart-default-style
68+
)
69+
draw.set-style(..style)
70+
71+
plot(
72+
73+
// To do: Is there a better way to setup the x-axis using custom axis-style
74+
x-min: -0.75, x-max: data.len() - 0.25,
75+
x-tick-step: if label-key == none {1},
76+
x-ticks: if label-key != none {
77+
data.map((d)=>d.at(3, default: none)).enumerate()
78+
} else {()},
79+
80+
y-grid: true,
81+
82+
plot-style: bar-style,
83+
..plot-args,
84+
85+
{
86+
add.series(
87+
label: label,
88+
{
89+
add.bar(
90+
data,
91+
x-key: 0,
92+
y-key: 1,
93+
bar-width: bar-width,
94+
axes: axes,
95+
)
96+
97+
if y-error-key != none {
98+
add.errorbar(
99+
data,
100+
x-key: 0,
101+
y-key: 1,
102+
y-error-key: 2,
103+
axes: axes,
104+
)
105+
}
106+
}
107+
)
108+
}
109+
)
110+
})
111+
112+
}

0 commit comments

Comments
 (0)