|
3 | 3 | #import "line.typ"
|
4 | 4 | #import "annotation.typ"
|
5 | 5 |
|
| 6 | +// Internal: This function takes the line-data (a sanitized input) and calculates |
| 7 | +// which points should be visible, and if they are partially clipped, recalcuates |
| 8 | +// positions |
6 | 9 | #let _prepare(self, ctx) = {
|
7 | 10 | self.stroke-paths = self.line-data
|
8 |
| - .map(((x, y, s, ..)) => { |
9 |
| - ( |
10 |
| - lines: util.compute-stroke-paths( |
11 |
| - ((x, 0), (x,y)), |
12 |
| - ctx.axes |
13 |
| - ), |
14 |
| - style: s, |
15 |
| - ) |
16 |
| - |
17 |
| - }) |
| 11 | + .map( |
| 12 | + ((x, y, style, ..)) => {( |
| 13 | + lines: util.compute-stroke-paths( ((x, 0), (x,y)), ctx.axes), |
| 14 | + style: style, |
| 15 | + )}) |
18 | 16 | self
|
19 | 17 | }
|
20 | 18 |
|
21 |
| -#let _fill(self, ctx) = {} |
22 |
| - |
| 19 | +// Visible: Draw the lines using the pre-calculated stroke paths from earlier. |
| 20 | +// The overall style is first applied, and then overriden |
23 | 21 | #let _stroke(self, ctx) = {
|
24 | 22 | for (lines, style) in self.stroke-paths {
|
25 | 23 | for p in lines {
|
|
28 | 26 | }
|
29 | 27 | }
|
30 | 28 |
|
31 |
| -#let _legend-preview(self) = { |
32 |
| - draw.line((0,.5), (1,.5), ..self.style) |
33 |
| -} |
34 |
| - |
| 29 | +/// Add a comb plot to a plot environment. |
| 30 | +/// |
| 31 | +/// Must be called from the body of a `plot(..)` command. |
| 32 | +/// |
| 33 | +/// #example(``` |
| 34 | +/// let points = ( |
| 35 | +/// (0,4), |
| 36 | +/// (1,2), |
| 37 | +/// (2,5, (stroke: red)), |
| 38 | +/// (3,1), |
| 39 | +/// (4,3) |
| 40 | +/// ) |
| 41 | +/// plot.plot(size: (12, 3), y-min: 0, x-inset: 0.5, y-inset: (0,0.5), { |
| 42 | +/// plot.add-comb( |
| 43 | +/// points, |
| 44 | +/// style-key: 2 // Indicate which key sfor tyle overrides (optional) |
| 45 | +/// ) |
| 46 | +/// }) |
| 47 | +/// ```, vertical: true) |
| 48 | +/// |
| 49 | +/// - data (array,dictionary): Array of 2D data points (and optionally a style |
| 50 | +/// override) |
| 51 | +/// - x-key (int,string): Key to use for retrieving an x-value from |
| 52 | +/// a single data entry. This value gets passed to the `.at(...)` |
| 53 | +/// function of a data item. Resulting value must be a number. |
| 54 | +/// - y-key (int,string): Key to use for retrieving a |
| 55 | +/// y-value. Resulting value must be a number. |
| 56 | +/// - style (style): Style to use, can be used with a `palette` function |
| 57 | +/// - style-key (int,string,none): Key to use for retrieving a `style` |
| 58 | +/// with which to override the current style. Resulting value must |
| 59 | +/// be either a `style` or `none` |
| 60 | +/// - mark (string): Mark symbol to place at each distinct value of the |
| 61 | +/// graph. Uses the `mark` style key of `style` for drawing. |
| 62 | +/// - mark-size (float): Mark size in cavas units |
| 63 | +/// - mark-style (style): Style override for marks. |
| 64 | +/// - axes (axes): Name of the axes to use for plotting. Reversing the axes |
| 65 | +/// means rotating the plot by 90 degrees |
| 66 | +/// - label (none, content): The name of the category to be shown in the legend. |
35 | 67 | #let add-comb(
|
36 |
| - domain: auto, |
37 |
| - mz-key: 0, |
38 |
| - intensity-key: 1, |
39 |
| - label-key: none, |
| 68 | + x-key: 0, |
| 69 | + y-key: 1, |
40 | 70 | style-key: none,
|
41 | 71 | style: (:),
|
42 | 72 | mark: none,
|
43 | 73 | mark-size: 0.05,
|
44 | 74 | mark-style: (:),
|
45 | 75 | axes: ("x", "y"),
|
46 | 76 | label: none,
|
47 |
| - label-padding: none, |
48 |
| - annotations: auto, |
49 | 77 | data
|
50 | 78 | ) = {
|
51 | 79 |
|
52 | 80 | let line-data = data.map(d=>(
|
53 |
| - d.at(mz-key), |
54 |
| - d.at(intensity-key), |
55 |
| - if style-key != none {d.at(style-key, default: none)} else {style} |
| 81 | + x: d.at(x-key), |
| 82 | + y: d.at(y-key), |
| 83 | + style: if style-key != none {d.at(style-key, default: none)} else {style}, |
56 | 84 | ))
|
57 | 85 |
|
58 | 86 | let x-domain = (
|
59 |
| - calc.min(..line-data.map(t => t.at(0))), |
60 |
| - calc.max(..line-data.map(t => t.at(0))) |
| 87 | + calc.min(..line-data.map(t => t.x)), |
| 88 | + calc.max(..line-data.map(t => t.x)) |
61 | 89 | )
|
62 | 90 |
|
63 | 91 | let y-domain = if line-data != none {(
|
64 |
| - calc.min(..line-data.map(t => t.at(1))), |
65 |
| - calc.max(..line-data.map(t => t.at(1))) |
| 92 | + calc.min(..line-data.map(t => t.y)), |
| 93 | + calc.max(..line-data.map(t => t.y)) |
66 | 94 | )}
|
67 | 95 |
|
68 |
| - let annotations = if annotations == auto { |
69 |
| - if (label-key == none) { |
70 |
| - () |
71 |
| - } else { |
72 |
| - data.filter(it=>it.at(label-key, default: none) != none) |
73 |
| - } |
74 |
| - } else if annotations == none { |
75 |
| - () |
76 |
| - } else { |
77 |
| - annotations |
78 |
| - } |
79 |
| - |
80 | 96 | ((:
|
81 | 97 | type: "comb",
|
82 | 98 | label: label,
|
83 |
| - data: data, /* Raw data */ |
84 |
| - line-data: line-data, /* Transformed data */ |
| 99 | + data: line-data.map(((x, y,..))=>(x,y)), /* X-Y data */ |
| 100 | + line-data: line-data, /* formatted data */ |
85 | 101 | axes: axes,
|
86 | 102 | x-domain: x-domain,
|
87 | 103 | y-domain: y-domain,
|
|
90 | 106 | mark-size: mark-size,
|
91 | 107 | mark-style: mark-style,
|
92 | 108 | plot-prepare: _prepare,
|
93 |
| - plot-fill: _fill, |
94 | 109 | plot-stroke: _stroke,
|
95 |
| - // plot-legend-preview: _legend-preview, |
96 |
| - mz-key: mz-key, |
97 |
| - intensity-key: intensity-key, |
98 |
| - label-key: label-key, |
99 | 110 | width: 0.5,
|
100 | 111 | ),)
|
101 | 112 |
|
102 |
| - for (x, y, a) in annotations { |
103 |
| - annotation.annotate( |
104 |
| - draw.content((x,y), [#a], anchor: "south"), |
105 |
| - axes: ("x", "y"), |
106 |
| - resize: true, |
107 |
| - padding: none, |
108 |
| - background: false |
109 |
| - ) |
110 |
| - } |
111 |
| - |
112 | 113 | }
|
0 commit comments