diff --git a/src/chart.typ b/src/chart.typ index d783cfb..953bf77 100644 --- a/src/chart.typ +++ b/src/chart.typ @@ -1 +1,2 @@ -#import "charts/bar/bar.typ" as bar: stacked, clustered, simple \ No newline at end of file +#import "charts/bar/bar.typ" as bar: stacked, clustered, simple +#import "charts/area/impl.typ" as area: stacked, stacked100 \ No newline at end of file diff --git a/src/charts/area/impl.typ b/src/charts/area/impl.typ new file mode 100644 index 0000000..76936ff --- /dev/null +++ b/src/charts/area/impl.typ @@ -0,0 +1 @@ +#import "stacked.typ": stacked, stacked100 \ No newline at end of file diff --git a/src/charts/area/plotter.typ b/src/charts/area/plotter.typ new file mode 100644 index 0000000..2de0135 --- /dev/null +++ b/src/charts/area/plotter.typ @@ -0,0 +1,53 @@ +#import "/src/cetz.typ": draw, styles, palette +#import "/src/plot.typ": plot +#import "/src/plot/add.typ" as add: series, xy, fill-between +#import "style.typ": areachart-default-style + +#let plotter( + series-data, + x-list: (), + area-style: palette.red, + axes: ("x", "y"), + stack: false, + ..plot-args, +) = draw.group(ctx => { + + // Setup styles + let style = styles.resolve( + ctx.style, + merge: (:), + root: "areachart", + base: areachart-default-style + ) + draw.set-style(..style) + + plot( + y-grid: true, + + plot-style: area-style, + ..plot-args, + + { + let y-offsets = (0,) * x-list.len() + for (label, data) in series-data { + add.series( + label: label, + { + add.fill-between( + data.enumerate().map(((k,v))=>(x-list.at(k), v + y-offsets.at(k))), + data.enumerate().map(((k,v))=>(x-list.at(k), y-offsets.at(k))), + ) + + if stack == true { + for (key, value) in data.enumerate() { + y-offsets.at(key) += value + } + } + + } + ) + } + } + ) + +}) diff --git a/src/charts/area/stacked.typ b/src/charts/area/stacked.typ new file mode 100644 index 0000000..55f0c76 --- /dev/null +++ b/src/charts/area/stacked.typ @@ -0,0 +1,56 @@ +#import "/src/cetz.typ": canvas, palette +#import "plotter.typ": plotter + +#let stacked( + data, + x-key: 0, + y-keys: (1,), + area-style: palette.red, + axes: ("x", "y"), + ..plot-args +) = { + + let series-data = () + for (series-index, data) in data.enumerate(){ + series-data.push( + ( + label: if label-key != none {data.at(label-key)}, + data: y-keys.map(k=>data.at(k, default: 0)) + ) + ) + } + + plotter( + series-data, + number-points: y-keys.len(), + x-list: x-list, + area-style: area-style, + axes: axes, + stack: true, + ..plot-args + ) +} + +#let stacked100( + data, + label-key: 0, + x-key: 1, + y-keys: (2,), + area-style: palette.red, + axes: ("x", "y"), + ..plot-args +) = stacked( + data.map(d=>{ + let sum = y-keys.map(k=>d.at(k, default: 0)).sum() + for key in y-keys { + d.at(key) /= sum + } + d + }), + label-key: label-key, + x-key: x-key, + y-keys: y-keys, + area-style: area-style, + axes: axes, + ..plot-args +) \ No newline at end of file diff --git a/src/charts/area/style.typ b/src/charts/area/style.typ new file mode 100644 index 0000000..f30d27d --- /dev/null +++ b/src/charts/area/style.typ @@ -0,0 +1,4 @@ +#let areachart-default-style = ( + axes: (tick: (length: 0), grid: (stroke: (dash: "dotted"))), + y-inset: 1, +) \ No newline at end of file diff --git a/tests/charts/area/stacked/test.typ b/tests/charts/area/stacked/test.typ new file mode 100644 index 0000000..c927304 --- /dev/null +++ b/tests/charts/area/stacked/test.typ @@ -0,0 +1,22 @@ +#set page(width: auto, height: auto, margin: 1cm) +#import "/tests/helper.typ": * + +#let data = ( + ([15-24], 18.0, 20.1, 23.0, 17.0), + ([25-29], 16.3, 17.6, 19.4, 15.3), + ([30-34], 14.0, 15.3, 13.9, 18.7), + ([35-44], 35.5, 26.5, 29.4, 25.8), + ([45-54], 25.0, 20.6, 22.4, 22.0), + ([55+], 19.9, 18.2, 19.2, 16.4), +) + +#let x-list = (0, 3.3, 6.6, 9.9) + +#test-case(cetz-plot.chart.area.stacked100( + size: (10,9), + label-key: 0, + y-keys: (1,2,3,4), + x-list: x-list, + labels: ([Low], [Medium], [High], [Very high]), + data, +)) diff --git a/tests/charts/area/stacked100/test.typ b/tests/charts/area/stacked100/test.typ new file mode 100644 index 0000000..821dba4 --- /dev/null +++ b/tests/charts/area/stacked100/test.typ @@ -0,0 +1,22 @@ +#set page(width: auto, height: auto, margin: 1cm) +#import "/tests/helper.typ": * + +#let data = ( + (0, 18.0, 20.1, 23.0, 17.0), + (2.5, 16.3, 17.6, 19.4, 15.3), + (5, 14.0, 15.3, 13.9, 18.7), + (7.5, 35.5, 26.5, 29.4, 25.8), + (10, 25.0, 20.6, 22.4, 22.0), + (12.5, 19.9, 18.2, 19.2, 16.4), +) + +#let x-list = (0, 3.3, 6.6, 9.9) + +#test-case(cetz-plot.chart.area.stacked( + size: (10,9), + x-key: 0, + y-keys: (1,2,3,4), + x-list: x-list, + labels: ([Low], [Medium], [High], [Very high]), + data, +))