topdrawerx 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. topdrawerx-0.5.0/LICENSE +21 -0
  2. topdrawerx-0.5.0/PKG-INFO +203 -0
  3. topdrawerx-0.5.0/README.md +169 -0
  4. topdrawerx-0.5.0/pyproject.toml +52 -0
  5. topdrawerx-0.5.0/setup.cfg +4 -0
  6. topdrawerx-0.5.0/src/topdrawerx/__init__.py +66 -0
  7. topdrawerx-0.5.0/src/topdrawerx/backends/__init__.py +9 -0
  8. topdrawerx-0.5.0/src/topdrawerx/backends/json_backend.py +19 -0
  9. topdrawerx-0.5.0/src/topdrawerx/backends/matplotlib_backend.py +370 -0
  10. topdrawerx-0.5.0/src/topdrawerx/charsets.py +242 -0
  11. topdrawerx-0.5.0/src/topdrawerx/cli.py +98 -0
  12. topdrawerx-0.5.0/src/topdrawerx/commands/__init__.py +31 -0
  13. topdrawerx-0.5.0/src/topdrawerx/commands/_util.py +39 -0
  14. topdrawerx-0.5.0/src/topdrawerx/commands/annotate.py +165 -0
  15. topdrawerx-0.5.0/src/topdrawerx/commands/axes.py +128 -0
  16. topdrawerx-0.5.0/src/topdrawerx/commands/draw.py +125 -0
  17. topdrawerx-0.5.0/src/topdrawerx/commands/frame.py +48 -0
  18. topdrawerx-0.5.0/src/topdrawerx/commands/legend.py +93 -0
  19. topdrawerx-0.5.0/src/topdrawerx/commands/meta.py +174 -0
  20. topdrawerx-0.5.0/src/topdrawerx/commands/page.py +96 -0
  21. topdrawerx-0.5.0/src/topdrawerx/commands/read.py +35 -0
  22. topdrawerx-0.5.0/src/topdrawerx/commands/setcmd.py +257 -0
  23. topdrawerx-0.5.0/src/topdrawerx/commands/title.py +139 -0
  24. topdrawerx-0.5.0/src/topdrawerx/compat.py +98 -0
  25. topdrawerx-0.5.0/src/topdrawerx/data.py +128 -0
  26. topdrawerx-0.5.0/src/topdrawerx/display.py +441 -0
  27. topdrawerx-0.5.0/src/topdrawerx/errors.py +57 -0
  28. topdrawerx-0.5.0/src/topdrawerx/lexer.py +181 -0
  29. topdrawerx-0.5.0/src/topdrawerx/palettes.py +48 -0
  30. topdrawerx-0.5.0/src/topdrawerx/registry.py +108 -0
  31. topdrawerx-0.5.0/src/topdrawerx/repl.py +175 -0
  32. topdrawerx-0.5.0/src/topdrawerx/session.py +292 -0
  33. topdrawerx-0.5.0/src/topdrawerx/state.py +208 -0
  34. topdrawerx-0.5.0/src/topdrawerx/styles/classic.tdx +13 -0
  35. topdrawerx-0.5.0/src/topdrawerx/styles/notebook.tdx +8 -0
  36. topdrawerx-0.5.0/src/topdrawerx/styles/paper.tdx +9 -0
  37. topdrawerx-0.5.0/src/topdrawerx/styles/poster.tdx +8 -0
  38. topdrawerx-0.5.0/src/topdrawerx/styles/talk.tdx +8 -0
  39. topdrawerx-0.5.0/src/topdrawerx/styles.py +140 -0
  40. topdrawerx-0.5.0/src/topdrawerx/text.py +182 -0
  41. topdrawerx-0.5.0/src/topdrawerx.egg-info/PKG-INFO +203 -0
  42. topdrawerx-0.5.0/src/topdrawerx.egg-info/SOURCES.txt +57 -0
  43. topdrawerx-0.5.0/src/topdrawerx.egg-info/dependency_links.txt +1 -0
  44. topdrawerx-0.5.0/src/topdrawerx.egg-info/entry_points.txt +2 -0
  45. topdrawerx-0.5.0/src/topdrawerx.egg-info/requires.txt +8 -0
  46. topdrawerx-0.5.0/src/topdrawerx.egg-info/top_level.txt +1 -0
  47. topdrawerx-0.5.0/tests/test_annotate.py +130 -0
  48. topdrawerx-0.5.0/tests/test_axes.py +90 -0
  49. topdrawerx-0.5.0/tests/test_case.py +135 -0
  50. topdrawerx-0.5.0/tests/test_golden.py +30 -0
  51. topdrawerx-0.5.0/tests/test_legend.py +100 -0
  52. topdrawerx-0.5.0/tests/test_lexer.py +66 -0
  53. topdrawerx-0.5.0/tests/test_panels.py +148 -0
  54. topdrawerx-0.5.0/tests/test_reference.py +32 -0
  55. topdrawerx-0.5.0/tests/test_registry.py +37 -0
  56. topdrawerx-0.5.0/tests/test_render.py +45 -0
  57. topdrawerx-0.5.0/tests/test_session.py +189 -0
  58. topdrawerx-0.5.0/tests/test_styles.py +106 -0
  59. topdrawerx-0.5.0/tests/test_text.py +27 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ken Suzuki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: topdrawerx
3
+ Version: 0.5.0
4
+ Summary: A TopDrawer-flavoured plotting language for making publication figures
5
+ Author: Ken Suzuki
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/KenSuzukiRCNP/topdrawerx
8
+ Project-URL: Source, https://github.com/KenSuzukiRCNP/topdrawerx
9
+ Project-URL: Issues, https://github.com/KenSuzukiRCNP/topdrawerx/issues
10
+ Project-URL: Changelog, https://github.com/KenSuzukiRCNP/topdrawerx/blob/trunk/CHANGELOG.md
11
+ Keywords: plotting,visualization,particle physics,topdrawer,matplotlib
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Physics
23
+ Classifier: Topic :: Scientific/Engineering :: Visualization
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: matplotlib>=3.7
28
+ Provides-Extra: repl
29
+ Requires-Dist: prompt_toolkit>=3.0; extra == "repl"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7; extra == "dev"
32
+ Requires-Dist: prompt_toolkit>=3.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # topdrawerx
36
+
37
+ [![tests](https://github.com/KenSuzukiRCNP/topdrawerx/actions/workflows/tests.yml/badge.svg)](https://github.com/KenSuzukiRCNP/topdrawerx/actions/workflows/tests.yml)
38
+ [![PyPI](https://img.shields.io/pypi/v/topdrawerx)](https://pypi.org/project/topdrawerx/)
39
+ [![python](https://img.shields.io/pypi/pyversions/topdrawerx)](https://pypi.org/project/topdrawerx/)
40
+ [![licence](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
41
+
42
+ **A small command language for making publication figures — and the REPL to
43
+ go with it.** Type a few lines, get a figure. The file stays readable, the
44
+ session *is* the script, and the defaults are the ones a physics journal wants.
45
+
46
+ The package is `topdrawerx`; the command you type is `tdx`.
47
+
48
+ ```
49
+ SET ORDER X Y DY
50
+ 2.0 1.1 0.4
51
+ 4.0 3.6 0.5
52
+ 6.0 6.2 0.6
53
+ PLOT
54
+ JOIN DASHES
55
+ TITLE LEFT 'σ (μb)'
56
+ TITLE BOTTOM '$p_{K^-}$ (GeV/c)'
57
+ ```
58
+
59
+ <img src="docs/images/panels-1.png" width="520" alt="a spectrum with a ratio panel beneath it">
60
+
61
+ The grammar is TopDrawer's — SLAC's plotting program from the late 1970s, which
62
+ a generation of particle physicists typed at daily and a few of us still miss.
63
+ topdrawerx keeps what made it pleasant, drops what only existed to serve card
64
+ readers and pen plotters, and renders through matplotlib.
65
+
66
+ ## Why you might want it
67
+
68
+ **The session is the script.** Type at the prompt, watch the figure change, then
69
+ `SAVE work.tdx` — and that file reproduces the figure exactly, next year, on
70
+ another machine. Settings apply retroactively: `SET SCALE Y LOG` after you have
71
+ already plotted does the right thing, and `UNDO` works, because every command is
72
+ replayed from a log rather than smeared onto a canvas.
73
+
74
+ **The defaults are already right for a paper.** Black on white, serif, ticks
75
+ inward on all four sides, error bars without caps, 1/2/5 tick intervals. You are
76
+ not spending your afternoon undoing someone's idea of a pretty chart.
77
+
78
+ **Physics figures are one command each.** Systematic error boxes are `BOX` over
79
+ data with `DX`/`DY` columns. Upper limits are `ARROW DOWN`. A ratio panel under a
80
+ spectrum, sharing the axis, is two `SET WINDOW`s.
81
+
82
+ **It is small.** One dependency (matplotlib), about thirty commands, a plain-text
83
+ file format your colleague can read without installing anything.
84
+
85
+ ## Install
86
+
87
+ ```sh
88
+ pip install topdrawerx # or: pip install "topdrawerx[repl]"
89
+ ```
90
+
91
+ Python ≥ 3.10, matplotlib ≥ 3.7. The optional `[repl]` extra adds line editing
92
+ and completion via `prompt_toolkit`.
93
+
94
+ ## A minute with it
95
+
96
+ ```sh
97
+ tdx # interactive, with a live plot window
98
+ tdx figure.tdx # run a file, then stay interactive
99
+ tdx figure.tdx -o fig.pdf # batch: run it and write the figure
100
+ tdx --check old/*.top # what would these legacy files need?
101
+ ```
102
+
103
+ At the prompt:
104
+
105
+ ```
106
+ tdx> SET ORDER X Y DY
107
+ tdx> 2.0 1.1 0.4
108
+ tdx> 4.0 3.6 0.5
109
+ tdx> 6.0 6.2 0.6
110
+ tdx> PLOT
111
+ tdx> JOIN
112
+ tdx> SET SCALE Y LOG ← applies to what you already drew
113
+ tdx> LEGEND 'K⁻ beam'
114
+ tdx> SAVE 'fig.pdf' ← or SAVE 'work.tdx' for the script
115
+ ```
116
+
117
+ The output format comes from the file name — `.pdf`, `.png`, `.svg`. There is no
118
+ device to configure. Several pages going to a PDF become one multi-page file.
119
+
120
+ From Python, the same engine:
121
+
122
+ ```python
123
+ import topdrawerx as tdx
124
+
125
+ s = tdx.Session()
126
+ s.run(open("figure.tdx").read())
127
+ fig = tdx.figure(s.frame) # a real matplotlib Figure, yours to adjust
128
+ tdx.save(s.frames, "out.pdf")
129
+ ```
130
+
131
+ ## Gallery
132
+
133
+ Every one of these is a file in [`examples/`](examples), rendered by
134
+ `tools/render_examples.py`.
135
+
136
+ | | |
137
+ | --- | --- |
138
+ | [`demo.tdx`](examples/demo.tdx)<br>points, errors, a curve, a histogram | <img src="docs/images/demo-1.png" width="330"> |
139
+ | [`annotate.tdx`](examples/annotate.tdx)<br>systematic boxes, upper limits, placed text | <img src="docs/images/annotate.png" width="330"> |
140
+ | [`styles.tdx`](examples/styles.tdx)<br>`SET STYLE TALK`, a palette, a legend | <img src="docs/images/styles.png" width="330"> |
141
+ | [`panels.tdx`](examples/panels.tdx)<br>`ZONE 2 2` | <img src="docs/images/panels-2.png" width="330"> |
142
+ | [`legacy_case.top`](examples/legacy_case.top)<br>a 1980s file, unchanged | <img src="docs/images/legacy_case.png" width="330"> |
143
+
144
+ `styles.tdx` with `PAPER` instead of `TALK` is the same figure in monochrome
145
+ serif at journal size — one word.
146
+
147
+ ## How it compares
148
+
149
+ | | |
150
+ | --- | --- |
151
+ | **gnuplot** | The closest relative: scriptable, has a REPL. topdrawerx has a smaller, more regular grammar (verb, qualifiers, no punctuation), physics defaults, and retroactive settings with `UNDO`. |
152
+ | **Grace / xmgrace** | GUI-first and largely unmaintained. topdrawerx is text-first, so figures live in version control. |
153
+ | **ROOT** | An analysis framework that also draws. topdrawerx is a plotting tool with one dependency. |
154
+ | **matplotlib** | The engine underneath. If you want a library, use it directly; topdrawerx is for when you want a *language* and a prompt. |
155
+
156
+ ## Old TopDrawer files
157
+
158
+ A `.top` file from 1985 usually just plots. `CASE` lines are converted into
159
+ Unicode and maths, the plotter settings are accepted and ignored, and the legacy
160
+ symbol codes work:
161
+
162
+ ```
163
+ TITLE TOP 'K2-3P R D2-3X0C12+3' → K⁻p → D⁻Ξ_c⁺
164
+ CASE ' X XL W X XFXLXX X'
165
+ ```
166
+
167
+ Commands topdrawerx does not know are skipped with a note, and the file still
168
+ plots; `--strict` turns that off. `tdx --check <files>` reports which commands a
169
+ directory of old files actually uses — which is how the implementation order
170
+ gets decided.
171
+
172
+ Being faithful to the original is a nice-to-have, not the goal: where the 1978
173
+ constraint and the 2026 user disagree, the user wins.
174
+
175
+ ## Documentation
176
+
177
+ - [Command reference](docs/reference.md) — generated from the source, so it
178
+ cannot drift.
179
+ - [Design notes](docs/design.md) — how the replay model, the display list and
180
+ the command registry fit together, and how to add a command.
181
+ - [Changelog](CHANGELOG.md).
182
+
183
+ ## Status and scope
184
+
185
+ Version 0.x: working, tested (159 tests), and used for real figures, but the
186
+ grammar may still move. Planned next: `FIT` and `SPLINE`, `CONTOUR` with simple
187
+ 3-D data, then a numpy API and a Jupyter cell magic. Deliberately not planned:
188
+ control flow (`IF`, `REPEAT`) — Python is the better scripting language and the
189
+ Python API is the seam — HBOOK, and most of the data arithmetic.
190
+
191
+ This is a personal project, shared because others may find it useful. Bug
192
+ reports and patches are welcome; replies may be slow, and there is no support
193
+ promise. If you use it in a paper, there is a DOI in [CITATION.cff](CITATION.cff).
194
+
195
+ ## Licence and attribution
196
+
197
+ MIT — see [LICENSE](LICENSE).
198
+
199
+ TopDrawer was written at the Stanford Linear Accelerator Center. topdrawerx is
200
+ an independent reimplementation of its command language and is not affiliated
201
+ with, endorsed by, or derived from SLAC's code. The character-set tables in
202
+ `src/topdrawerx/charsets.py` were transcribed from the published TopDrawer
203
+ reference manual, and each cites the section it came from.
@@ -0,0 +1,169 @@
1
+ # topdrawerx
2
+
3
+ [![tests](https://github.com/KenSuzukiRCNP/topdrawerx/actions/workflows/tests.yml/badge.svg)](https://github.com/KenSuzukiRCNP/topdrawerx/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/topdrawerx)](https://pypi.org/project/topdrawerx/)
5
+ [![python](https://img.shields.io/pypi/pyversions/topdrawerx)](https://pypi.org/project/topdrawerx/)
6
+ [![licence](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
7
+
8
+ **A small command language for making publication figures — and the REPL to
9
+ go with it.** Type a few lines, get a figure. The file stays readable, the
10
+ session *is* the script, and the defaults are the ones a physics journal wants.
11
+
12
+ The package is `topdrawerx`; the command you type is `tdx`.
13
+
14
+ ```
15
+ SET ORDER X Y DY
16
+ 2.0 1.1 0.4
17
+ 4.0 3.6 0.5
18
+ 6.0 6.2 0.6
19
+ PLOT
20
+ JOIN DASHES
21
+ TITLE LEFT 'σ (μb)'
22
+ TITLE BOTTOM '$p_{K^-}$ (GeV/c)'
23
+ ```
24
+
25
+ <img src="docs/images/panels-1.png" width="520" alt="a spectrum with a ratio panel beneath it">
26
+
27
+ The grammar is TopDrawer's — SLAC's plotting program from the late 1970s, which
28
+ a generation of particle physicists typed at daily and a few of us still miss.
29
+ topdrawerx keeps what made it pleasant, drops what only existed to serve card
30
+ readers and pen plotters, and renders through matplotlib.
31
+
32
+ ## Why you might want it
33
+
34
+ **The session is the script.** Type at the prompt, watch the figure change, then
35
+ `SAVE work.tdx` — and that file reproduces the figure exactly, next year, on
36
+ another machine. Settings apply retroactively: `SET SCALE Y LOG` after you have
37
+ already plotted does the right thing, and `UNDO` works, because every command is
38
+ replayed from a log rather than smeared onto a canvas.
39
+
40
+ **The defaults are already right for a paper.** Black on white, serif, ticks
41
+ inward on all four sides, error bars without caps, 1/2/5 tick intervals. You are
42
+ not spending your afternoon undoing someone's idea of a pretty chart.
43
+
44
+ **Physics figures are one command each.** Systematic error boxes are `BOX` over
45
+ data with `DX`/`DY` columns. Upper limits are `ARROW DOWN`. A ratio panel under a
46
+ spectrum, sharing the axis, is two `SET WINDOW`s.
47
+
48
+ **It is small.** One dependency (matplotlib), about thirty commands, a plain-text
49
+ file format your colleague can read without installing anything.
50
+
51
+ ## Install
52
+
53
+ ```sh
54
+ pip install topdrawerx # or: pip install "topdrawerx[repl]"
55
+ ```
56
+
57
+ Python ≥ 3.10, matplotlib ≥ 3.7. The optional `[repl]` extra adds line editing
58
+ and completion via `prompt_toolkit`.
59
+
60
+ ## A minute with it
61
+
62
+ ```sh
63
+ tdx # interactive, with a live plot window
64
+ tdx figure.tdx # run a file, then stay interactive
65
+ tdx figure.tdx -o fig.pdf # batch: run it and write the figure
66
+ tdx --check old/*.top # what would these legacy files need?
67
+ ```
68
+
69
+ At the prompt:
70
+
71
+ ```
72
+ tdx> SET ORDER X Y DY
73
+ tdx> 2.0 1.1 0.4
74
+ tdx> 4.0 3.6 0.5
75
+ tdx> 6.0 6.2 0.6
76
+ tdx> PLOT
77
+ tdx> JOIN
78
+ tdx> SET SCALE Y LOG ← applies to what you already drew
79
+ tdx> LEGEND 'K⁻ beam'
80
+ tdx> SAVE 'fig.pdf' ← or SAVE 'work.tdx' for the script
81
+ ```
82
+
83
+ The output format comes from the file name — `.pdf`, `.png`, `.svg`. There is no
84
+ device to configure. Several pages going to a PDF become one multi-page file.
85
+
86
+ From Python, the same engine:
87
+
88
+ ```python
89
+ import topdrawerx as tdx
90
+
91
+ s = tdx.Session()
92
+ s.run(open("figure.tdx").read())
93
+ fig = tdx.figure(s.frame) # a real matplotlib Figure, yours to adjust
94
+ tdx.save(s.frames, "out.pdf")
95
+ ```
96
+
97
+ ## Gallery
98
+
99
+ Every one of these is a file in [`examples/`](examples), rendered by
100
+ `tools/render_examples.py`.
101
+
102
+ | | |
103
+ | --- | --- |
104
+ | [`demo.tdx`](examples/demo.tdx)<br>points, errors, a curve, a histogram | <img src="docs/images/demo-1.png" width="330"> |
105
+ | [`annotate.tdx`](examples/annotate.tdx)<br>systematic boxes, upper limits, placed text | <img src="docs/images/annotate.png" width="330"> |
106
+ | [`styles.tdx`](examples/styles.tdx)<br>`SET STYLE TALK`, a palette, a legend | <img src="docs/images/styles.png" width="330"> |
107
+ | [`panels.tdx`](examples/panels.tdx)<br>`ZONE 2 2` | <img src="docs/images/panels-2.png" width="330"> |
108
+ | [`legacy_case.top`](examples/legacy_case.top)<br>a 1980s file, unchanged | <img src="docs/images/legacy_case.png" width="330"> |
109
+
110
+ `styles.tdx` with `PAPER` instead of `TALK` is the same figure in monochrome
111
+ serif at journal size — one word.
112
+
113
+ ## How it compares
114
+
115
+ | | |
116
+ | --- | --- |
117
+ | **gnuplot** | The closest relative: scriptable, has a REPL. topdrawerx has a smaller, more regular grammar (verb, qualifiers, no punctuation), physics defaults, and retroactive settings with `UNDO`. |
118
+ | **Grace / xmgrace** | GUI-first and largely unmaintained. topdrawerx is text-first, so figures live in version control. |
119
+ | **ROOT** | An analysis framework that also draws. topdrawerx is a plotting tool with one dependency. |
120
+ | **matplotlib** | The engine underneath. If you want a library, use it directly; topdrawerx is for when you want a *language* and a prompt. |
121
+
122
+ ## Old TopDrawer files
123
+
124
+ A `.top` file from 1985 usually just plots. `CASE` lines are converted into
125
+ Unicode and maths, the plotter settings are accepted and ignored, and the legacy
126
+ symbol codes work:
127
+
128
+ ```
129
+ TITLE TOP 'K2-3P R D2-3X0C12+3' → K⁻p → D⁻Ξ_c⁺
130
+ CASE ' X XL W X XFXLXX X'
131
+ ```
132
+
133
+ Commands topdrawerx does not know are skipped with a note, and the file still
134
+ plots; `--strict` turns that off. `tdx --check <files>` reports which commands a
135
+ directory of old files actually uses — which is how the implementation order
136
+ gets decided.
137
+
138
+ Being faithful to the original is a nice-to-have, not the goal: where the 1978
139
+ constraint and the 2026 user disagree, the user wins.
140
+
141
+ ## Documentation
142
+
143
+ - [Command reference](docs/reference.md) — generated from the source, so it
144
+ cannot drift.
145
+ - [Design notes](docs/design.md) — how the replay model, the display list and
146
+ the command registry fit together, and how to add a command.
147
+ - [Changelog](CHANGELOG.md).
148
+
149
+ ## Status and scope
150
+
151
+ Version 0.x: working, tested (159 tests), and used for real figures, but the
152
+ grammar may still move. Planned next: `FIT` and `SPLINE`, `CONTOUR` with simple
153
+ 3-D data, then a numpy API and a Jupyter cell magic. Deliberately not planned:
154
+ control flow (`IF`, `REPEAT`) — Python is the better scripting language and the
155
+ Python API is the seam — HBOOK, and most of the data arithmetic.
156
+
157
+ This is a personal project, shared because others may find it useful. Bug
158
+ reports and patches are welcome; replies may be slow, and there is no support
159
+ promise. If you use it in a paper, there is a DOI in [CITATION.cff](CITATION.cff).
160
+
161
+ ## Licence and attribution
162
+
163
+ MIT — see [LICENSE](LICENSE).
164
+
165
+ TopDrawer was written at the Stanford Linear Accelerator Center. topdrawerx is
166
+ an independent reimplementation of its command language and is not affiliated
167
+ with, endorsed by, or derived from SLAC's code. The character-set tables in
168
+ `src/topdrawerx/charsets.py` were transcribed from the published TopDrawer
169
+ reference manual, and each cites the section it came from.
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "topdrawerx"
7
+ version = "0.5.0"
8
+ description = "A TopDrawer-flavoured plotting language for making publication figures"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Ken Suzuki" }]
13
+ keywords = ["plotting", "visualization", "particle physics", "topdrawer", "matplotlib"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Science/Research",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: Physics",
26
+ "Topic :: Scientific/Engineering :: Visualization",
27
+ ]
28
+ dependencies = [
29
+ "matplotlib>=3.7",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/KenSuzukiRCNP/topdrawerx"
34
+ Source = "https://github.com/KenSuzukiRCNP/topdrawerx"
35
+ Issues = "https://github.com/KenSuzukiRCNP/topdrawerx/issues"
36
+ Changelog = "https://github.com/KenSuzukiRCNP/topdrawerx/blob/trunk/CHANGELOG.md"
37
+
38
+ [project.optional-dependencies]
39
+ repl = ["prompt_toolkit>=3.0"]
40
+ dev = ["pytest>=7", "prompt_toolkit>=3.0"]
41
+
42
+ [project.scripts]
43
+ tdx = "topdrawerx.cli:main"
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["src"]
47
+
48
+ [tool.setuptools.package-data]
49
+ topdrawerx = ["styles/*.tdx"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,66 @@
1
+ """tdx -- a TopDrawer-flavoured plotting language for modern Python.
2
+
3
+ Quick use::
4
+
5
+ import topdrawerx
6
+
7
+ session = tdx.Session()
8
+ session.run('''
9
+ SET ORDER X Y DY
10
+ 1.0 2.0 0.1
11
+ 2.0 3.9 0.2
12
+ PLOT
13
+ JOIN
14
+ TITLE LEFT '$d\\sigma/d\\Omega$ (μb/sr)'
15
+ ''')
16
+ tdx.save(session.frames, "figure.pdf")
17
+
18
+ The command language, the display list and the backends are separate on
19
+ purpose: see :mod:`tdx.session` for why the whole log is replayed on every
20
+ command, and :mod:`tdx.display` for the boundary the backends sit behind.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from .errors import (
26
+ AmbiguousCommand,
27
+ ArgumentError,
28
+ DataError,
29
+ LexError,
30
+ TdxError,
31
+ UnknownCommand,
32
+ )
33
+ from .session import Context, Session, render_script, replay
34
+ from . import commands # noqa: F401 (importing this fills the registries)
35
+
36
+ __version__ = "0.5.0"
37
+
38
+ __all__ = [
39
+ "Session",
40
+ "Context",
41
+ "render_script",
42
+ "replay",
43
+ "save",
44
+ "figure",
45
+ "TdxError",
46
+ "LexError",
47
+ "UnknownCommand",
48
+ "AmbiguousCommand",
49
+ "ArgumentError",
50
+ "DataError",
51
+ "__version__",
52
+ ]
53
+
54
+
55
+ def save(frames, path: str) -> list[str]:
56
+ """Write display-list frames to a file (format from the suffix)."""
57
+ from .backends import matplotlib_backend
58
+
59
+ return matplotlib_backend.save(frames, path)
60
+
61
+
62
+ def figure(frame):
63
+ """Render one frame into a matplotlib ``Figure`` you can keep working on."""
64
+ from .backends import matplotlib_backend
65
+
66
+ return matplotlib_backend.make_figure(frame)
@@ -0,0 +1,9 @@
1
+ """Backends turn a display list into something you can look at.
2
+
3
+ ``matplotlib`` is the default; ``json`` is used by the tests. Nothing above
4
+ this package knows which one is in use.
5
+ """
6
+
7
+ from . import json_backend, matplotlib_backend # noqa: F401
8
+
9
+ __all__ = ["json_backend", "matplotlib_backend"]
@@ -0,0 +1,19 @@
1
+ """JSON backend -- the test oracle.
2
+
3
+ Golden-file tests compare display lists, not images: stable across matplotlib
4
+ versions, readable in a diff, and they fail with a message you can act on.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+
11
+ from ..display import Frame
12
+
13
+
14
+ def to_dict(frames: list[Frame]) -> dict:
15
+ return {"frames": [f.to_dict() for f in frames]}
16
+
17
+
18
+ def dumps(frames: list[Frame], indent: int = 2) -> str:
19
+ return json.dumps(to_dict(frames), indent=indent, sort_keys=True, ensure_ascii=False)