forgegraph 0.6.1__tar.gz → 0.7.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 (339) hide show
  1. {forgegraph-0.6.1 → forgegraph-0.7.0}/CHANGELOG.md +22 -0
  2. {forgegraph-0.6.1 → forgegraph-0.7.0}/PKG-INFO +1 -1
  3. forgegraph-0.7.0/docs/examples/plate_spectra.yaml +32 -0
  4. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/graphspecs/graphspec_example.md +7 -7
  5. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/pack.md +2 -2
  6. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/themes/light_theme.md +1 -1
  7. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/gallery.md +12 -0
  8. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/layers.md +3 -1
  9. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/layouts.md +5 -3
  10. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/series.md +3 -3
  11. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/help/troubleshooting.md +2 -2
  12. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/catalogs/layer_reference.md +4 -2
  13. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/graphspec_example.md +8 -8
  14. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/graphtype_xy.md +4 -4
  15. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/layer_reference.md +5 -4
  16. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/pack.md +2 -2
  17. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/theme.md +1 -1
  18. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/README.md +1 -0
  19. forgegraph-0.7.0/examples/sample-data/plate_spectra.csv +4561 -0
  20. {forgegraph-0.6.1 → forgegraph-0.7.0}/pyproject.toml +1 -1
  21. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/build_example_data.py +28 -2
  22. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/build_sample_data.py +26 -0
  23. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/cards.py +4 -1
  24. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/axes.py +15 -4
  25. forgegraph-0.7.0/src/forgegraph/drawing/equation.py +231 -0
  26. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/key.py +8 -5
  27. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/labels.py +154 -13
  28. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/layers.py +192 -24
  29. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/marks.py +101 -1
  30. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/page.py +18 -2
  31. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/readout.py +63 -7
  32. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/ticks.py +10 -3
  33. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/xy.py +130 -33
  34. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/graphspecs/graphspec_example.yaml +7 -7
  35. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/pack.yaml +2 -2
  36. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/themes/light_theme.yaml +1 -1
  37. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/SKILL.md +1 -0
  38. forgegraph-0.7.0/src/forgegraph/skill/forgegraph/examples/heat_map.parquet +0 -0
  39. forgegraph-0.7.0/src/forgegraph/skill/forgegraph/examples/heat_map.yaml +32 -0
  40. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/catalogs/layer_reference.yaml +3 -1
  41. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/graphspec_example.yaml +8 -8
  42. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/graphtype_xy.yaml +4 -4
  43. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/layer_reference.yaml +5 -4
  44. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/pack.yaml +2 -2
  45. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/theme.yaml +1 -1
  46. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_axes.py +61 -0
  47. forgegraph-0.7.0/tests/test_key_position.py +219 -0
  48. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_layers.py +190 -0
  49. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_layout.py +15 -1
  50. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_marks.py +146 -0
  51. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_readout.py +138 -1
  52. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_render.py +24 -7
  53. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_series.py +62 -0
  54. {forgegraph-0.6.1 → forgegraph-0.7.0}/uv.lock +1 -1
  55. forgegraph-0.6.1/tests/test_key_position.py +0 -83
  56. {forgegraph-0.6.1 → forgegraph-0.7.0}/.gitignore +0 -0
  57. {forgegraph-0.6.1 → forgegraph-0.7.0}/.pre-commit-config.yaml +0 -0
  58. {forgegraph-0.6.1 → forgegraph-0.7.0}/CITATION.cff +0 -0
  59. {forgegraph-0.6.1 → forgegraph-0.7.0}/CONTRIBUTING.md +0 -0
  60. {forgegraph-0.6.1 → forgegraph-0.7.0}/LICENSE +0 -0
  61. {forgegraph-0.6.1 → forgegraph-0.7.0}/README.md +0 -0
  62. {forgegraph-0.6.1 → forgegraph-0.7.0}/SECURITY.md +0 -0
  63. {forgegraph-0.6.1 → forgegraph-0.7.0}/TRADEMARKS.md +0 -0
  64. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/design/architecture.md +0 -0
  65. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/design/initial-specification.md +0 -0
  66. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/acme_polarization.yaml +0 -0
  67. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/assay_summary.yaml +0 -0
  68. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/cells.layout.yaml +0 -0
  69. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/co2.yaml +0 -0
  70. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/dose_response.yaml +0 -0
  71. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/durability.yaml +0 -0
  72. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/lab_polarization.yaml +0 -0
  73. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/packs/lab/formats/poster.yaml +0 -0
  74. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/packs/lab/pack.yaml +0 -0
  75. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/packs/lab/themes/lab_theme.yaml +0 -0
  76. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/penguins.yaml +0 -0
  77. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/polarization.yaml +0 -0
  78. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/savings.yaml +0 -0
  79. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/tips_side_by_side.yaml +0 -0
  80. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/examples/tips_stacked.yaml +0 -0
  81. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/formats/slide.md +0 -0
  82. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/graphspecs/polarization-curve.md +0 -0
  83. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/layouts/dashboard.md +0 -0
  84. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/pack.md +0 -0
  85. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/schedules/workday.md +0 -0
  86. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/acme/themes/acme.md +0 -0
  87. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/catalogs/formats/compact_format.md +0 -0
  88. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/catalogs/formats/wide_format.md +0 -0
  89. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/catalogs/themes/light_theme.md +0 -0
  90. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/formats/compact_format.md +0 -0
  91. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/formats/wide_format.md +0 -0
  92. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/layouts/layout.md +0 -0
  93. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/packs/forgegraph_default/schedules/schedule.md +0 -0
  94. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/get-started/installation.md +0 -0
  95. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/get-started/quickstart.md +0 -0
  96. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/get-started/skill.md +0 -0
  97. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/configs.md +0 -0
  98. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/graphs.md +0 -0
  99. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/provenance.md +0 -0
  100. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/schedules.md +0 -0
  101. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/guides/themes-formats-packs.md +0 -0
  102. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/index.md +0 -0
  103. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/reference/python-api.md +0 -0
  104. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/release-notes.md +0 -0
  105. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/site/stylesheets/extra.css +0 -0
  106. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/catalogs/graphtype_xy.md +0 -0
  107. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/catalogs/layer_error_bar.md +0 -0
  108. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/catalogs/layer_note.md +0 -0
  109. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/config.md +0 -0
  110. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/format.md +0 -0
  111. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/layer_error_bar.md +0 -0
  112. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/layer_note.md +0 -0
  113. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/layout_example.md +0 -0
  114. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/provenance.md +0 -0
  115. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/schedule.md +0 -0
  116. {forgegraph-0.6.1 → forgegraph-0.7.0}/docs/specifications/styles.md +0 -0
  117. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/README.md +0 -0
  118. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/forgegraph.toml +0 -0
  119. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/lab_throughput.layout.md +0 -0
  120. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/lab_throughput.layout.yaml +0 -0
  121. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/lost_runs.parquet +0 -0
  122. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/lost_runs.yaml +0 -0
  123. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/ops/formats/dashboard.md +0 -0
  124. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/ops/formats/dashboard.yaml +0 -0
  125. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/ops/pack.yaml +0 -0
  126. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/prep_lab_dashboard.py +0 -0
  127. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/run_time.parquet +0 -0
  128. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/run_time.yaml +0 -0
  129. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/runs_gc_ms_01.yaml +0 -0
  130. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/runs_hplc_01.yaml +0 -0
  131. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/runs_hplc_02.yaml +0 -0
  132. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/runs_icp_oes_01.yaml +0 -0
  133. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/samples_weekly.yaml +0 -0
  134. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/throughput_weekly.yaml +0 -0
  135. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_gc_ms_01.parquet +0 -0
  136. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_hplc_01.parquet +0 -0
  137. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_hplc_02.parquet +0 -0
  138. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_icp_oes_01.parquet +0 -0
  139. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_runs.parquet +0 -0
  140. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/dashboard/weekly_samples.parquet +0 -0
  141. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/cell_durability.parquet +0 -0
  142. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/cell_performance.layout.md +0 -0
  143. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/cell_performance.layout.yaml +0 -0
  144. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/cell_polarization.parquet +0 -0
  145. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/forgegraph.toml +0 -0
  146. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/hfr_time.yaml +0 -0
  147. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/journal/formats/journal-double.md +0 -0
  148. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/journal/formats/journal-double.yaml +0 -0
  149. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/journal/pack.yaml +0 -0
  150. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/polarization.yaml +0 -0
  151. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/prep_cells.py +0 -0
  152. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/layouts/paper/voltage_time.yaml +0 -0
  153. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/formats/slide.yaml +0 -0
  154. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/graphspecs/polarization-curve.yaml +0 -0
  155. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/layouts/dashboard.yaml +0 -0
  156. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/pack.yaml +0 -0
  157. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/references/house.md +0 -0
  158. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/schedules/workday.yaml +0 -0
  159. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/packs/acme/themes/acme.yaml +0 -0
  160. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/air_passengers.csv +0 -0
  161. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/air_passengers_by_month.csv +0 -0
  162. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/anscombe.csv +0 -0
  163. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/assay_summary.csv +0 -0
  164. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/cell_durability.csv +0 -0
  165. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/cell_polarization.csv +0 -0
  166. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/co2_monthly.csv +0 -0
  167. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/diamonds.csv +0 -0
  168. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/dose_response.csv +0 -0
  169. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/economics.csv +0 -0
  170. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/enzyme_kinetics.csv +0 -0
  171. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/fmri.csv +0 -0
  172. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/gapminder.csv +0 -0
  173. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/geyser.csv +0 -0
  174. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/growth.csv +0 -0
  175. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/iris.csv +0 -0
  176. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/lab_export.xlsx +0 -0
  177. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/lab_runs.xlsx +0 -0
  178. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/penguins.csv +0 -0
  179. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/seaice.csv +0 -0
  180. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/sensor_log_eu.csv +0 -0
  181. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/stocks.csv +0 -0
  182. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/tips.csv +0 -0
  183. {forgegraph-0.6.1 → forgegraph-0.7.0}/examples/sample-data/weather.xlsx +0 -0
  184. {forgegraph-0.6.1 → forgegraph-0.7.0}/mkdocs.yml +0 -0
  185. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/build_recreations.py +0 -0
  186. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/check_denylist.py +0 -0
  187. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/docs_site.py +0 -0
  188. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/forgemark.py +0 -0
  189. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/mirror_specs.py +0 -0
  190. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/report_skill_evals.py +0 -0
  191. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/run_skill_evals.py +0 -0
  192. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/smoke_installed.py +0 -0
  193. {forgegraph-0.6.1 → forgegraph-0.7.0}/scripts/visual_changelog.py +0 -0
  194. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/__init__.py +0 -0
  195. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/__main__.py +0 -0
  196. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/canvas.py +0 -0
  197. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/__init__.py +0 -0
  198. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/build.py +0 -0
  199. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/examples.py +0 -0
  200. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/files.py +0 -0
  201. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/catalog_build/page.py +0 -0
  202. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/__init__.py +0 -0
  203. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/catalog.py +0 -0
  204. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/graphs.py +0 -0
  205. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/jobs.py +0 -0
  206. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/schema.py +0 -0
  207. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/cli/skill.py +0 -0
  208. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/data.py +0 -0
  209. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/__init__.py +0 -0
  210. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/colorbar.py +0 -0
  211. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/dates.py +0 -0
  212. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/errors.py +0 -0
  213. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/export.py +0 -0
  214. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/figure.py +0 -0
  215. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/layout.py +0 -0
  216. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/limits.py +0 -0
  217. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/lint.py +0 -0
  218. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/markup.py +0 -0
  219. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/spacing.py +0 -0
  220. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/style.py +0 -0
  221. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawing/text.py +0 -0
  222. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/drawn.py +0 -0
  223. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/IBMPlexSerif-Bold.ttf +0 -0
  224. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/IBMPlexSerif-Light.ttf +0 -0
  225. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/IBMPlexSerif-Medium.ttf +0 -0
  226. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/IBMPlexSerif-Regular.ttf +0 -0
  227. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
  228. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/OFL-IBMPlexSerif.txt +0 -0
  229. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/OFL-Roboto.txt +0 -0
  230. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/OFL-SourceSans3.md +0 -0
  231. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-Bold.ttf +0 -0
  232. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-Italic.ttf +0 -0
  233. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-Light.ttf +0 -0
  234. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-Medium.ttf +0 -0
  235. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-Regular.ttf +0 -0
  236. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/Roboto-SemiBold.ttf +0 -0
  237. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-Bold.ttf +0 -0
  238. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-It.ttf +0 -0
  239. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-Light.ttf +0 -0
  240. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-Medium.ttf +0 -0
  241. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-Regular.ttf +0 -0
  242. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/SourceSans3-Semibold.ttf +0 -0
  243. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/fonts/__init__.py +0 -0
  244. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/forgemark.py +0 -0
  245. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/graph.py +0 -0
  246. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/jobs/__init__.py +0 -0
  247. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/jobs/run.py +0 -0
  248. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/jobs/status.py +0 -0
  249. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/layouts/__init__.py +0 -0
  250. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/layouts/separators.py +0 -0
  251. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/__init__.py +0 -0
  252. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/catalogs/formats/compact_format.yaml +0 -0
  253. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/catalogs/formats/wide_format.yaml +0 -0
  254. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/catalogs/themes/light_theme.yaml +0 -0
  255. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/formats/compact_format.yaml +0 -0
  256. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/formats/wide_format.yaml +0 -0
  257. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/graphspecs/dated.parquet +0 -0
  258. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/graphspecs/fanned.parquet +0 -0
  259. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/graphspecs/polarization.parquet +0 -0
  260. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/graphspecs/reinforced.parquet +0 -0
  261. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/layouts/layout.yaml +0 -0
  262. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/packs/forgegraph_default/schedules/schedule.yaml +0 -0
  263. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/__init__.py +0 -0
  264. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/bars.yaml +0 -0
  265. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/figure.layout.yaml +0 -0
  266. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/figure.schedule.yaml +0 -0
  267. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/layers.yaml +0 -0
  268. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/xy.parquet +0 -0
  269. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/examples/xy.yaml +0 -0
  270. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/references/requests.md +0 -0
  271. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/skill/forgegraph/references/themes_formats_configs.md +0 -0
  272. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/__init__.py +0 -0
  273. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/access.py +0 -0
  274. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/common.py +0 -0
  275. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/explain.py +0 -0
  276. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/layers.py +0 -0
  277. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/layout.py +0 -0
  278. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/loaded.py +0 -0
  279. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/parameters.py +0 -0
  280. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/provenance.py +0 -0
  281. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/registry.py +0 -0
  282. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/resolve.py +0 -0
  283. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/schedule.py +0 -0
  284. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/series.py +0 -0
  285. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/tags.py +0 -0
  286. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/spec/validate.py +0 -0
  287. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/catalogs/graphtype_xy.yaml +0 -0
  288. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/catalogs/layer_error_bar.yaml +0 -0
  289. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/catalogs/layer_note.yaml +0 -0
  290. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/config.toml +0 -0
  291. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/format.yaml +0 -0
  292. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/layer_error_bar.yaml +0 -0
  293. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/layer_note.yaml +0 -0
  294. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/layout_example.yaml +0 -0
  295. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/provenance.yaml +0 -0
  296. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/schedule.yaml +0 -0
  297. {forgegraph-0.6.1 → forgegraph-0.7.0}/src/forgegraph/specifications/styles.yaml +0 -0
  298. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/crowded_runs.png +0 -0
  299. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/flow_sweep_wide.png +0 -0
  300. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/fuel_by_size.png +0 -0
  301. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/layout_anode_layers.png +0 -0
  302. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/layout_screening_and_rate.png +0 -0
  303. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/polarization.png +0 -0
  304. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/polarization_narrow.png +0 -0
  305. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/precipitation_loglog.png +0 -0
  306. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/replicates.png +0 -0
  307. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/screening_scatter.png +0 -0
  308. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/baselines/separator_colorbar.png +0 -0
  309. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/conftest.py +0 -0
  310. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/fixtures/catalog_comments.html +0 -0
  311. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/pack_helpers.py +0 -0
  312. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/recreations.py +0 -0
  313. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/render_helpers.py +0 -0
  314. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/spec_mirror.py +0 -0
  315. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_blank.py +0 -0
  316. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_catalog.py +0 -0
  317. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_catalog_files.py +0 -0
  318. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_catalog_inputs.py +0 -0
  319. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_catalog_page.py +0 -0
  320. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_cli.py +0 -0
  321. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_dates.py +0 -0
  322. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_denylist.py +0 -0
  323. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_docs_site.py +0 -0
  324. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_jobs.py +0 -0
  325. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_labels.py +0 -0
  326. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_lint.py +0 -0
  327. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_markup.py +0 -0
  328. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_packaging.py +0 -0
  329. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_packs.py +0 -0
  330. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_perturbation.py +0 -0
  331. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_regression.py +0 -0
  332. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_schedule.py +0 -0
  333. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_smoke.py +0 -0
  334. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_spec_comments.py +0 -0
  335. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_spec_mirrors.py +0 -0
  336. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_specs.py +0 -0
  337. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_styles.py +0 -0
  338. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_tags.py +0 -0
  339. {forgegraph-0.6.1 → forgegraph-0.7.0}/tests/test_text.py +0 -0
@@ -2,6 +2,28 @@
2
2
 
3
3
  ForgeGraph follows [semantic versioning](https://semver.org). Before 1.0, a minor version may change how graphs look or how specs are written.
4
4
 
5
+ ## 0.7.0 (2026-11-02)
6
+
7
+ ### New Features
8
+
9
+ - **Heat maps.** `marker.shape: grid_cell` makes each point fill its place in a grid, so an xy graph colored by a column of numbers becomes a heat map. The cells fit at any format, and an HTML page shows a cell's values when you point at it. There's an example in the gallery and in the skill.
10
+ - **Reference lines from an equation.** A reference line can now be an equation instead of a value, such as `equation: x` for a y = x line on a parity plot, or `2x^2 - 3x + 1` for a curve. Two equations, such as `x - 10` and `x + 10`, make a tolerance band.
11
+ - **No tick labels.** `tick.algorithm: none` draws no ticks or tick labels on an axis, for when there are too many names to fit and readers can point at a point to see its name.
12
+ - **Tab bars stay in view.** On a layout's HTML page, the tabs now stay at the top of the window as you scroll, and switching tabs keeps your place on the page.
13
+
14
+ ### Changes
15
+
16
+ - **More in the hover box.** On a graph of more than one series, the hover box now always names the series. It also shows each column a series is split by, so splitting a scatter by its sample's name shows that name on each point.
17
+ - **More room for keys.** A reference line's label at `position: auto` now goes after the keys and keeps clear of them, so keys and labels may sit in different places than before. A second key can go under or beside the first, and the y-axis can grow by up to `axis.max_extension` to make room for a key. Some graphs that couldn't fit a key now draw, with a longer y-axis.
18
+ - **Clearer key errors.** When a key doesn't fit, the error now says what's in the way at each position.
19
+
20
+ ### Bug Fixes
21
+
22
+ - Coloring by the column on a categorical axis gave every point the first color and labelled the groups 0.0, 1.0 and 2.0. Each category now gets its own color, and the same goes for marker shapes and line styles. The axis already names the groups, so with `legend.method: auto` they aren't named again.
23
+ - A categorical axis with exactly two categories had no tick labels. It now has one for each category.
24
+ - An aggregated series colored by a column of numbers crashed with a message from matplotlib. If the rows behind a point share one value, the point now takes that color. If they don't, it's an error that says which column to change.
25
+ - The hover box on an integer axis showed `2.0` where it now shows `2`.
26
+
5
27
  ## 0.6.1 (2026-10-31)
6
28
 
7
29
  ### Changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: forgegraph
3
- Version: 0.6.1
3
+ Version: 0.7.0
4
4
  Summary: Fast, consistent, publication-quality graphs from pandas DataFrames, usable by people and AI agents.
5
5
  Project-URL: Repository, https://github.com/sskaye/forgegraph
6
6
  Project-URL: Issues, https://github.com/sskaye/forgegraph/issues
@@ -0,0 +1,32 @@
1
+ name: plate_spectra
2
+ version: 0
3
+ description: A heat map of a plate's spectra, one row for each sample, colored by absorbance.
4
+ extends: None
5
+ parameters: !var
6
+ specifications:
7
+ graph_type: xy
8
+ theme: forgegraph_default/light_theme
9
+ format: forgegraph_default/compact_format
10
+ text:
11
+ title: A plate of UV-Vis spectra
12
+ data:
13
+ file: plate_spectra.parquet
14
+ preprocessing: examples/sample-data/plate_spectra.csv
15
+ arrangement:
16
+ base_axis: y
17
+ axis:
18
+ x:
19
+ title: Wavelength (nm)
20
+ y:
21
+ data:
22
+ column: Sample
23
+ type: categorical
24
+ title: Sample
25
+ series:
26
+ Wavelength (nm):
27
+ color: Normalized absorbance
28
+ color_scale: continuous
29
+ marker:
30
+ shape: grid_cell
31
+ line:
32
+ show: false
@@ -30,7 +30,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
30
30
              min: None          *# \[None, float, date\]; The axis minimum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the lowest value minus plot_area.gap × the data's range.*\
31
31
              max: None          *# \[None, float, date\]; The axis maximum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the highest value plus plot_area.gap × the data's range.*\
32
32
              tick:\
33
-                 algorithm: auto          *# \[auto, all, manual\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step.*\
33
+                 algorithm: auto          *# \[auto, all, manual, none\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step. none draws no ticks or tick labels.*\
34
34
                  step: None          *# \[None, float > 0, text\]; The distance between ticks when algorithm is manual, e.g. 5 or "2 week". On a log axis, it's a factor.*\
35
35
                  label:\
36
36
                      format: auto          *# \[auto, format\]; The tick label format. auto chooses one from the values, and for dates, from the tick unit.*\
@@ -46,7 +46,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
46
46
              min: None          *# \[None, float, date\]; The axis minimum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the lowest value minus plot_area.gap × the data's range.*\
47
47
              max: None          *# \[None, float, date\]; The axis maximum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the highest value plus plot_area.gap × the data's range.*\
48
48
              tick:\
49
-                 algorithm: auto          *# \[auto, all, manual\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step.*\
49
+                 algorithm: auto          *# \[auto, all, manual, none\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step. none draws no ticks or tick labels.*\
50
50
                  step: None          *# \[None, float > 0, text\]; The distance between ticks when algorithm is manual, e.g. 5 or "2 week". On a log axis, it's a factor.*\
51
51
                  label:\
52
52
                      format: auto          *# \[auto, format\]; The tick label format. auto chooses one from the values, and for dates, from the tick unit.*\
@@ -60,7 +60,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
60
60
          "Cell Voltage (V)":\
61
61
              column: name          *# \[name, column\]; The column of values the series draws along the axis that isn't arrangement.base_axis. name uses the column with the series' name.*\
62
62
              aggregation:          *# \[aggregation style\]; How rows at the same location are combined into one point. If its function is None, each row is drawn.*\
63
-             split: \[Cell, Run\]          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates.*\
63
+             split: \[Cell, Run\]          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates. An html page's readout shows each one.*\
64
64
              order: sorted          *# \[sorted, data\]; The order of the series' groups: sorted, or data for the order in which they first appear.*\
65
65
              color: Cell          *# \[series, hex color, column\]; The series' color. series takes the next palette color, and a column gives each of its values its own.*\
66
66
              color_scale: auto          *# \[auto, categorical, continuous\]; For a color column: categorical uses the palette, continuous a color scale and color bar, and auto continuous for numbers.*\
@@ -73,7 +73,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
73
73
                  missing_values: drop          *# \[drop, gap\]; What happens to rows missing x or y. drop joins the line across them, and gap breaks the line. Lint counts both in dropped-rows.*\
74
74
              marker:\
75
75
                  show: true          *# \[true, false\]; Whether to draw markers.*\
76
-                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own.*\
76
+                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, grid_cell, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own. grid_cell fills the grid the points lie on, for a heat map.*\
77
77
                  fill: first          *# \[first, series, filled, open, column\]; The marker fill. first is the theme's first marker fill, series takes the next one, and a column gives each of its values its own.*\
78
78
                  size: 0.5          *# \[float > 0\]; The marker size, × base size.*\
79
79
              bar:\
@@ -91,7 +91,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
91
91
      legend:\
92
92
          method: auto          *# \[auto, direct, key, none\]; How this graph names its series: one of the theme's legend.methods.*\
93
93
          key:\
94
-             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data.*\
94
+             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data. If there's none inside the plot, it goes beside a key there, or the y-axis is extended.*\
95
95
              inside: true          *# \[true, false\]; Whether the key is inside the plot area. If false, top positions are above the plot area, bottom positions are below the x-axis title, and right positions are to its right.*\
96
96
              title:\
97
97
                  text: column_name          *# \[None, column_name, text\]; The title of every key. column_name uses the name of each key's column. Other text is allowed only when there is one key. If None, there is no title.*\
@@ -106,11 +106,11 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
106
106
      layers:          *# The layers drawn on the graph, in this order, each under a name the GraphSpec chooses. A layer may set the look its theme marks !var.*\
107
107
          target:\
108
108
              type: reference          *# \[layer type\]; The layer's type: one of the graph type's layer.types.*\
109
-             axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines.*\
109
+             axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines, and an equation then gives y in terms of x.*\
110
110
              line_1:\
111
111
                  value: 1.2          *# \[None, float, date, column\]; A number, a date, or a column.*\
112
112
                  label:\
113
-                     text: Target 1.2 V          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. Other text is written as given.*\
113
+                     text: Target 1.2 V          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. With an equation, value writes e.g. y = 2x. Other text is written as given.*\
114
114
          conditions:\
115
115
              type: note          *# \[layer type\]; The layer's type: one of the graph type's layer.types.*\
116
116
              text: "60 °C, 1 atm, H₂/air"          *# \[text\]; The note's text, in label markup. A line break starts a new line.*
@@ -3,7 +3,7 @@
3
3
  This page mirrors src/forgegraph/packs/forgegraph_default/pack.yaml, which is the specification. A test fails if they ever differ.
4
4
 
5
5
  name: forgegraph_default          *# \[text\]; The pack's name, which its specs' names start with (pack/name); a pip pack's must match its entry point.*\
6
- version: 0.6.1          *# \[text\]; The pack's release version.*\
6
+ version: 0.7.0          *# \[text\]; The pack's release version.*\
7
7
  description: ForgeGraph's built-in default pack. So it’s always possible to create a graph.          *# \[text\]; One line, describing what the pack is.*\
8
8
  issues: https://github.com/sskaye/forgegraph/issues          *# \[text\]; Where to file feature requests and bug reports about the pack.*\
9
- requires: ">=0.6, <0.7"          *# \[None, version specifier\]; The ForgeGraph version required for the pack. pip format, e.g. ">=0.6, <1". If None, it works with any version.*
9
+ requires: ">=0.7, <0.8"          *# \[None, version specifier\]; The ForgeGraph version required for the pack. pip format, e.g. ">=0.6, <1". If None, it works with any version.*
@@ -46,7 +46,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
46
46
                  count: 0          *# \[int ≥ 0\]; The number of minor ticks between major ticks. If 0, there are none.*\
47
47
                  length: 0.15          *# \[float ≥ 0\]; The minor tick length, × base size.*\
48
48
                  width: 0.05          *# \[float ≥ 0\]; The width, × base size. If 0, no line is drawn.*\
49
-         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
49
+         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, keys, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
50
50
      plot_area:\
51
51
          border:          *# \[line style\]; The plot area's border.*\
52
52
              width: 0.08          *# \[float ≥ 0\]; The width, × base size. If 0, no line is drawn.*\
@@ -14,6 +14,18 @@ Polarization curves of three fuel cells, one line for each cell, named beside it
14
14
  --8<-- "docs/examples/polarization.yaml"
15
15
  ```
16
16
 
17
+ ## A heat map
18
+
19
+ A plate of 60 UV-Vis spectra, one row for each sample, colored by absorbance. It's an xy graph whose markers fill the grid its points lie on, with `marker.shape: grid_cell`. Pointing at a cell on an HTML page shows its wavelength, sample and absorbance. Data: plate_spectra.csv.
20
+
21
+ ![A grid of colored cells, wavelength across and samples up, with four families of bands.](images/plate_spectra.png){ width="480" }
22
+
23
+ ??? example "GraphSpec"
24
+
25
+ ```yaml
26
+ --8<-- "docs/examples/plate_spectra.yaml"
27
+ ```
28
+
17
29
  ## Lines over time
18
30
 
19
31
  Each cell's voltage over a 200-hour durability test, as lines without markers. Data: cell_durability.csv.
@@ -6,7 +6,9 @@ Layers are drawn over a graph: reference lines and bands, error bars, and notes.
6
6
 
7
7
  A `reference` layer draws a line across the plot at a value: horizontal with `axis: y`, vertical with `axis: x`. `line_1.value` is a number, a date, or a column, and a column's rows are combined by `line_1.aggregation`, so a line can sit at the data's mean, median, a percentile and so on. `offset` adds a distance to that, such as three standard deviations.
8
8
 
9
- A second line, `line_2`, makes the layer a band, filled between the two lines. The savings rate has a band one standard deviation either side of its mean, drawn without its lines, and a second layer with a line at the mean:
9
+ A line can also be an equation, in place of a value, such as `equation: x` for a line of y = x on a parity plot. With `axis: y`, an equation gives y in terms of x, and with `axis: x`, x in terms of y. It may use numbers, that letter, `+`, `-`, `*`, `/`, `^` and parentheses, as in `2x^2 - 3x + 1`, and it may be a curve. The line runs across the plot without widening an axis, and its label lies along it. `label.text: value` writes the equation, as in y = 2x. A label that can't find a clear place beside the data can go beyond the plot, with `label.inside: false`.
10
+
11
+ A second line, `line_2`, makes the layer a band, filled between the two lines, which can be equations: `x - 10` and `x + 10` make a tolerance band around y = x. The savings rate has a band one standard deviation either side of its mean, drawn without its lines, and a second layer with a line at the mean:
10
12
 
11
13
  ```yaml
12
14
  --8<-- "docs/examples/savings.yaml"
@@ -56,11 +56,13 @@ Tabs that hold tabs look like this, with a tab bar for each level on the HTML pa
56
56
 
57
57
  ## The HTML page
58
58
 
59
- A layout's HTML page, or a GraphSpec's, shows each figure as an image that shrinks to fit a narrow window. On a layout of several tabs, the tab names sit above the figures, in `tab_bar.label`'s look, with `tab_bar.line` under them; by default, they follow the first tile's theme. The tabs work where the page's scripts don't run. Where they run, each tab also has its own link, such as `#by-instrument`.
59
+ A layout's HTML page, or a GraphSpec's, shows each figure as an image that shrinks to fit a narrow window. On a layout of several tabs, the tab names sit above the figures, in `tab_bar.label`'s look, with `tab_bar.line` under them; by default, they follow the first tile's theme. The tabs work where the page's scripts don't run. Where they run, each tab also has its own link, such as `#by-instrument`. The tab names stay at the top of the window as the page scrolls, and switching tabs leaves the page where it is, so the same tile stays in view.
60
60
 
61
61
  **Readouts.** Pointing at a data point on the page, or tapping it, shows its values in a box above it, with a ring around the point:
62
62
 
63
63
  - each axis's title and the point's value, with its error bar and the error bar's aggregation where it has one, as in `8.0 ± 0.4 (95%CI)`;
64
- - then the series' color, marker shape and line style, each where it varies, as the key's title and the point's group.
64
+ - then the series' color, marker shape and line style, each where it varies, as the key's title and the point's group;
65
+ - then, with more than one series, the series' name;
66
+ - then each column the series is split by, with the point's value. To name each point of a scatter, split the series by the column that holds its name, as in `split: measurement_id`.
65
67
 
66
- Markers, a line's points and bars respond; empty space, reference lines and notes don't. A number with an error bar is rounded to the error's last digit; one without has a decimal more than the tick labels. The page carries each figure's points and the font the readouts are written in, so it's larger than the image alone.
68
+ Markers, a line's points and bars respond; empty space, reference lines and notes don't. A number with an error bar is rounded to the error's last digit; one without has a decimal more than the tick labels, except on an integer base axis, whose values are whole. The page carries each figure's points and the font the readouts are written in, so it's larger than the image alone.
@@ -20,7 +20,7 @@ Each series chooses its marks: `line.show`, `marker.show` and `bar.show`. By def
20
20
 
21
21
  - **Lines** join the points in order of x, or in the order of the rows with `line.connect_order: data`. `line.interpolate` draws them straight, in steps, or as a smooth curve.
22
22
  - **Missing values:** with `line.missing_values: drop`, a line joins across a row missing its x or y; with `gap`, the line breaks there. Either way, the lint counts the rows left out.
23
- - **Markers** take the theme's shapes and fills, and `marker.size` sets their size.
23
+ - **Markers** take the theme's shapes and fills, and `marker.size` sets their size. `marker.shape: grid_cell` instead fills the grid the points lie on, which makes a heat map: see the [gallery](../gallery.md#a-heat-map).
24
24
  - **Bars** stand on the x-axis, from 0, or from 1 on a log axis, and `bar.width` sets how much of the space between neighboring bars they fill.
25
25
 
26
26
  The durability test draws lines without markers:
@@ -41,7 +41,7 @@ A series' rows can be split into groups by a column. Each of `color`, `line.styl
41
41
 
42
42
  ![Bill depth against bill length, a color and a marker shape for each penguin species.](../images/penguins.png){ width="480" }
43
43
 
44
- - **split** specifies how rows should be grouped for aggregation or line paths. This can be used to, for example, prevent averaging of data from replicates or draw independent lines for multiple runs under the same conditions.
44
+ - **split** specifies how rows should be grouped for aggregation or line paths. This can be used to, for example, prevent averaging of data from replicates or draw independent lines for multiple runs under the same conditions. On an HTML page, a point's readout shows its value of each column it's split by.
45
45
  - **order** puts the groups in sorted order, or with `data`, in the order they first appear.
46
46
  - **A numeric color column** draws on a color scale, with a color bar beside the plot. `color_scale: categorical` gives each number a palette color instead.
47
47
 
@@ -85,7 +85,7 @@ A categorical axis keeps the order of its categories in the data, or sorts them
85
85
  `legend.method` chooses how a graph names its series, from the methods its theme allows:
86
86
 
87
87
  - **direct:** each name beside its own series, touching no line, marker or other name.
88
- - **key:** a key at the first of `legend.key.positions` that covers no data. With `legend.key.inside: false`, the key is outside the plot area.
88
+ - **key:** a key at the first of `legend.key.positions` that covers no data. If there's none, a second key goes under or beside the first, and otherwise the y-axis is extended by up to `axis.max_extension` to make room. With `legend.key.inside: false`, the key is outside the plot area.
89
89
  - **none:** no names, where the theme allows it. The built-in theme doesn't.
90
90
  - **auto**, the default, tries the theme's methods in order and uses the first that fits.
91
91
 
@@ -10,7 +10,7 @@ Every render lints the graph it drew and lists any issues, each under the name o
10
10
 
11
11
  Two texts' letters overlap, such as neighboring tick labels or a title and a key.
12
12
 
13
- Make room: fewer ticks (`tick.algorithm: manual` with a larger `tick.step`), a shorter label or tick label format, a wider format (`figure.width`), or rotated x tick labels, where the theme lets a GraphSpec set `axis.tick.label.x_rotation`.
13
+ Make room: fewer ticks (`tick.algorithm: manual` with a larger `tick.step`), no tick labels at all (`tick.algorithm: none`, where readers identify a point from its readout on an HTML page), a shorter label or tick label format, a wider format (`figure.width`), or rotated x tick labels, where the theme lets a GraphSpec set `axis.tick.label.x_rotation`.
14
14
 
15
15
  ### clipped-text
16
16
 
@@ -22,7 +22,7 @@ Shorten the text, place it inside the plot, or widen the format.
22
22
 
23
23
  A key covers a line or marker.
24
24
 
25
- List other positions in `legend.key.positions`, put the key outside the plot with `legend.key.inside: false`, or name the series directly with `legend.method: direct`.
25
+ The error says what's in the way at each position. List other positions in `legend.key.positions`, let the axis extend further with a larger `axis.max_extension`, move a reference line's label by leaving its `position` at `auto`, put the key outside the plot with `legend.key.inside: false`, or name the series directly with `legend.method: direct`.
26
26
 
27
27
  ### too-many-groups
28
28
 
@@ -9,7 +9,7 @@ extends: None          *# \[None, catalog\]; The catalog this one exte
9
9
  baseline: forgegraph_default/graphspec_example          *# \[graphspec\]; The GraphSpec every card changes, without its own layers.*\
10
10
  settings: None          *# The GraphSpec parameters every card sets so that the layer can be drawn. If there are none, this is None.*\
11
11
  layer:          *# The parameters of the layer every card draws, which is named after its type. If None, the layer's defaults are used.*\
12
-     axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines.*\
12
+     axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines, and an equation then gives y in terms of x.*\
13
13
      line_1:\
14
14
          value: 1.45          *# \[None, float, date, column\]; A number, a date, or a column.*\
15
15
   \
@@ -32,4 +32,6 @@ baselines:          *# Generic scenarios, each the baseline with one o
32
32
          layers.reference.line_2: {value: "Cell Voltage (V)", aggregation: {function: percentile, percentile: 75}}\
33
33
      percentile_offsets:          *# \[parameters and their values, or non-standard\]; Lines at the median cell voltage, each offset by a fraction of a percentile, so each offset's percentile acts.*\
34
34
          layers.reference.line_1: {value: "Cell Voltage (V)", aggregation: {function: median}, offset: {function: percentile, percentile: 10, multiplier: 0.05}}\
35
-         layers.reference.line_2: {value: "Cell Voltage (V)", aggregation: {function: median}, offset: {function: percentile, percentile: 90, multiplier: -0.05}}
35
+         layers.reference.line_2: {value: "Cell Voltage (V)", aggregation: {function: median}, offset: {function: percentile, percentile: 90, multiplier: -0.05}}\
36
+     from_an_equation:          *# \[parameters and their values, or non-standard\]; A line from an equation, so that its label can follow it.*\
37
+         layers.reference.line_1: {value: None, equation: "1.35 + 0.02x"}
@@ -18,7 +18,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
18
18
          file: None          *# \[None, text\]; A Parquet file holding the DataFrame to draw: an absolute path, a path starting with ~, or a path relative to the folder of the file that sets it. If None, the DataFrame is passed through the Python API.*\
19
19
          preprocessing: None          *# \[None, text\]; Where the code that made the data is: a path, a URL or a note. It's recorded in provenance and never read. If None, it's not given.*\
20
20
      axis:\
21
-         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
21
+         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, keys, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
22
22
          x:\
23
23
              data:\
24
24
                  column: "PLACEHOLDER"          *# \[None, column\]; The column on this axis when it's arrangement.base_axis, which a GraphSpec must then set. Otherwise, it must be None.*\
@@ -31,7 +31,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
31
31
              min: None          *# \[None, float, date\]; The axis minimum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the lowest value minus plot_area.gap × the data's range.*\
32
32
              max: None          *# \[None, float, date\]; The axis maximum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the highest value plus plot_area.gap × the data's range.*\
33
33
              tick:\
34
-                 algorithm: auto          *# \[auto, all, manual\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step.*\
34
+                 algorithm: auto          *# \[auto, all, manual, none\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step. none draws no ticks or tick labels.*\
35
35
                  step: None          *# \[None, float > 0, text\]; The distance between ticks when algorithm is manual, e.g. 5 or "2 week". On a log axis, it's a factor.*\
36
36
                  label:\
37
37
                      format: auto          *# \[auto, format\]; The tick label format. auto chooses one from the values, and for dates, from the tick unit.*\
@@ -47,7 +47,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
47
47
              min: None          *# \[None, float, date\]; The axis minimum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the lowest value minus plot_area.gap × the data's range.*\
48
48
              max: None          *# \[None, float, date\]; The axis maximum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the highest value plus plot_area.gap × the data's range.*\
49
49
              tick:\
50
-                 algorithm: auto          *# \[auto, all, manual\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step.*\
50
+                 algorithm: auto          *# \[auto, all, manual, none\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step. none draws no ticks or tick labels.*\
51
51
                  step: None          *# \[None, float > 0, text\]; The distance between ticks when algorithm is manual, e.g. 5 or "2 week". On a log axis, it's a factor.*\
52
52
                  label:\
53
53
                      format: auto          *# \[auto, format\]; The tick label format. auto chooses one from the values, and for dates, from the tick unit.*\
@@ -61,7 +61,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
61
61
          "SERIES_1":\
62
62
              column: name          *# \[name, column\]; The column of values the series draws along the axis that isn't arrangement.base_axis. name uses the column with the series' name.*\
63
63
              aggregation:          *# \[aggregation style\]; How rows at the same location are combined into one point. If its function is None, each row is drawn.*\
64
-             split: \[Cell, Run\]          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates.*\
64
+             split: \[Cell, Run\]          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates. An html page's readout shows each one.*\
65
65
              order: sorted          *# \[sorted, data\]; The order of the series' groups: sorted, or data for the order in which they first appear.*\
66
66
              color: Cell          *# \[series, hex color, column\]; The series' color. series takes the next palette color, and a column gives each of its values its own.*\
67
67
              color_scale: auto          *# \[auto, categorical, continuous\]; For a color column: categorical uses the palette, continuous a color scale and color bar, and auto continuous for numbers.*\
@@ -74,7 +74,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
74
74
                  missing_values: drop          *# \[drop, gap\]; What happens to rows missing x or y. drop joins the line across them, and gap breaks the line. Lint counts both in dropped-rows.*\
75
75
              marker:\
76
76
                  show: true          *# \[true, false\]; Whether to draw markers.*\
77
-                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own.*\
77
+                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, grid_cell, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own. grid_cell fills the grid the points lie on, for a heat map.*\
78
78
                  fill: first          *# \[first, series, filled, open, column\]; The marker fill. first is the theme's first marker fill, series takes the next one, and a column gives each of its values its own.*\
79
79
                  size: 0.5          *# \[float > 0\]; The marker size, × base size.*\
80
80
              bar:\
@@ -94,7 +94,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
94
94
          direct:\
95
95
              max_relative_distance: 0.8          *# \[0-1\]; The farthest a direct label may be from its series, ÷ its distance to the next nearest series.*\
96
96
          key:\
97
-             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data.*\
97
+             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data. If there's none inside the plot, it goes beside a key there, or the y-axis is extended.*\
98
98
              inside: true          *# \[true, false\]; Whether the key is inside the plot area. If false, top positions are above the plot area, bottom positions are below the x-axis title, and right positions are to its right.*\
99
99
              title:\
100
100
                  text: column_name          *# \[None, column_name, text\]; The title of every key. column_name uses the name of each key's column. Other text is allowed only when there is one key. If None, there is no title.*\
@@ -119,11 +119,11 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
119
119
      layers:          *# The layers drawn on the graph, in this order, each under a name the GraphSpec chooses. A layer may set the look its theme marks !var.*\
120
120
          target:\
121
121
              type: reference          *# \[layer type\]; The layer's type: one of the graph type's layer.types.*\
122
-             axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines.*\
122
+             axis: y          *# \[x, y\]; The axis the values are on. y draws horizontal lines, and an equation then gives y in terms of x.*\
123
123
              line_1:\
124
124
                  value: 1.2          *# \[None, float, date, column\]; A number, a date, or a column.*\
125
125
                  label:\
126
-                     text: Target 1.2 V          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. Other text is written as given.*\
126
+                     text: Target 1.2 V          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. With an equation, value writes e.g. y = 2x. Other text is written as given.*\
127
127
          conditions:\
128
128
              type: note          *# \[layer type\]; The layer's type: one of the graph type's layer.types.*\
129
129
              text: "60 °C, 1 atm, H₂/air"          *# \[text\]; The note's text, in label markup. A line break starts a new line.*
@@ -21,7 +21,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
21
21
              min: None          *# \[None, float, date\]; The axis minimum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the lowest value minus plot_area.gap × the data's range.*\
22
22
              max: None          *# \[None, float, date\]; The axis maximum. On a datetime axis, it's a date, e.g. 2025-08-25 or "2025-08-25T06:00". If None, it's the highest value plus plot_area.gap × the data's range.*\
23
23
              tick:\
24
-                 algorithm: auto          *# \[auto, all, manual\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step.*\
24
+                 algorithm: auto          *# \[auto, all, manual, none\]; How the ticks are placed. auto uses the theme's algorithm, or on dates, the boundaries of one calendar unit. all places a tick at every value. manual places one every tick.step. none draws no ticks or tick labels.*\
25
25
                  step: None          *# \[None, float > 0, text\]; The distance between ticks when algorithm is manual, e.g. 5 or "2 week". On a log axis, it's a factor.*\
26
26
                  label:\
27
27
                      format: auto          *# \[auto, format\]; The tick label format. auto chooses one from the values, and for dates, from the tick unit.*\
@@ -36,7 +36,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
36
36
          a:\
37
37
              column: name          *# \[name, column\]; The column of values the series draws along the axis that isn't arrangement.base_axis. name uses the column with the series' name.*\
38
38
              aggregation:          *# \[aggregation style\]; How rows at the same location are combined into one point. If its function is None, each row is drawn.*\
39
-             split: None          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates.*\
39
+             split: None          *# \[None, columns\]; Columns whose values split the series into separate lines, e.g. replicates. An html page's readout shows each one.*\
40
40
              order: sorted          *# \[sorted, data\]; The order of the series' groups: sorted, or data for the order in which they first appear.*\
41
41
              color: series          *# \[series, hex color, column\]; The series' color. series takes the next palette color, and a column gives each of its values its own.*\
42
42
              color_scale: auto          *# \[auto, categorical, continuous\]; For a color column: categorical uses the palette, continuous a color scale and color bar, and auto continuous for numbers.*\
@@ -48,7 +48,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
48
48
                  missing_values: drop          *# \[drop, gap\]; What happens to rows missing x or y. drop joins the line across them, and gap breaks the line. Lint counts both in dropped-rows.*\
49
49
              marker:\
50
50
                  show: true          *# \[true, false\]; Whether to draw markers.*\
51
-                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own.*\
51
+                 shape: first          *# \[first, series, circle, square, triangle_up, triangle_down, diamond, cross, plus, grid_cell, column\]; The marker shape. first is the theme's first marker shape, series takes the next one, and a column gives each of its values its own. grid_cell fills the grid the points lie on, for a heat map.*\
52
52
                  fill: first          *# \[first, series, filled, open, column\]; The marker fill. first is the theme's first marker fill, series takes the next one, and a column gives each of its values its own.*\
53
53
              bar:\
54
54
                  show: false          *# \[true, false\]; Whether to draw bars. Bars start at 0, or at 1 on a log axis, and the value axis then always includes that value.*\
@@ -63,7 +63,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
63
63
      legend:\
64
64
          method: auto          *# \[auto, direct, key, none\]; How this graph names its series: one of the theme's legend.methods.*\
65
65
          key:\
66
-             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data.*\
66
+             positions: \[top_right, top_left, bottom_right, bottom_left\]          *# \[top_left, top_center, top_right, bottom_left, bottom_center, bottom_right, right_top, right_center, right_bottom\]; The positions to try, in order. The key goes in the first one where it doesn't overlap the data. If there's none inside the plot, it goes beside a key there, or the y-axis is extended.*\
67
67
              inside: true          *# \[true, false\]; Whether the key is inside the plot area. If false, top positions are above the plot area, bottom positions are below the x-axis title, and right positions are to its right.*\
68
68
              title:\
69
69
                  text: column_name          *# \[None, column_name, text\]; The title of every key. column_name uses the name of each key's column. Other text is allowed only when there is one key. If None, there is no title.*\
@@ -4,20 +4,21 @@ This page mirrors src/forgegraph/specifications/layer_reference.yaml, which is t
4
4
 
5
5
  name: reference          *# \[text\]; The name of the layer type.*\
6
6
  version: 0          *# \[int ≥ 0\]; The version number of the specification.*\
7
- description: A horizontal or vertical line, or a shaded band between two lines, at a value or drawn from the data.          *# \[text\]; The description of the layer type.*\
7
+ description: A horizontal or vertical line, or a shaded band between two lines, at a value, drawn from the data, or from an equation.          *# \[text\]; The description of the layer type.*\
8
8
   \
9
9
  parameters:\
10
-     axis: None          *# \[x, y\]; The axis the values are on. y draws horizontal lines. A GraphSpec must set it.*\
10
+     axis: None          *# \[x, y\]; The axis the values are on. y draws horizontal lines, and an equation then gives y in terms of x. A GraphSpec must set it.*\
11
11
      per_series: false          *# \[true, false\]; If true, the layer is drawn for every series with matching color, marker shape, marker fill and line style. If false, it's drawn for all data combined.*\
12
12
      line_1:          *# The first line.*\
13
13
          value: None          *# \[None, float, date, column\]; A number, a date, or a column.*\
14
+         equation: None          *# \[None, text\]; The line as an equation in the other axis's letter, e.g. 2x or x^2, instead of a value. It may use numbers, the letter, +, -, \*, /, ^ and parentheses.*\
14
15
          aggregation:          *# \[aggregation style\]; How the column's rows are combined into the line's value. If its function is None, the values are used as-is.*\
15
16
          offset:          *# \[aggregation style\]; A distance added to the aggregate, e.g. 3 × stddev for three standard deviations above the aggregated value.*\
16
17
          scope: all          *# \[all, each\]; Which rows the aggregates are taken over: all for a straight line, or each, for a line that follows the data.*\
17
18
          line:          *# \[line style, look from layer.reference.line\]; The appearance of the line.*\
18
19
          label:          *# \[text style, look from layer.reference.label\]; The line's label.*\
19
-             text: None          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. Other text is written as given.*\
20
-             position: auto          *# \[auto, start_above, start_below, center_above, center_below, end_above, end_below\]; auto selects the first option that doesn't overlap the data and is invalid if inside is false.*\
20
+             text: None          *# \[None, aggregation, value, aggregation_value, text\]; aggregation writes e.g. Mean, value e.g. 14.9, and aggregation_value e.g. Mean: 14.9. With an equation, value writes e.g. y = 2x. Other text is written as given.*\
21
+             position: auto          *# \[auto, start_above, start_below, center_above, center_below, end_above, end_below\]; auto selects the first option that doesn't overlap the data, or with an equation, the first place along the line, and is invalid if inside is false.*\
21
22
              inside: true          *# \[true, false\]; Whether the label is inside the plot area. If false, it's beyond the plot area at the line's start or end, and a center position is invalid.*\
22
23
              format: auto          *# \[auto, format\]; The format of the value. auto uses the axis's tick label format if it's set, and otherwise one more decimal than the tick labels show. On a date axis, it uses the tick label format, which must then be set.*\
23
24
      line_2:          *# \[same as line_1\]; The second line.*\
@@ -3,7 +3,7 @@
3
3
  This page mirrors src/forgegraph/specifications/pack.yaml, which is the specification. A test fails if they ever differ.
4
4
 
5
5
  name: forgegraph_specifications          *# \[text\]; The pack's name, which its specs' names start with (pack/name); a pip pack's must match its entry point.*\
6
- version: 0.6.1          *# \[text\]; The pack's release version.*\
6
+ version: 0.7.0          *# \[text\]; The pack's release version.*\
7
7
  description: ForgeGraph's base specs, to define the specifications.          *# \[text\]; One line, describing what the pack is.*\
8
8
  issues: https://github.com/sskaye/forgegraph/issues          *# \[text\]; Where to file feature requests and bug reports about the pack's specs.*\
9
- requires: ">=0.6, <0.7"          *# \[None, version specifier\]; The ForgeGraph versions the pack works with, as pip writes them, e.g. ">=0.6, <1". If None, it works with any version.*
9
+ requires: ">=0.7, <0.8"          *# \[None, version specifier\]; The ForgeGraph versions the pack works with, as pip writes them, e.g. ">=0.6, <1". If None, it works with any version.*
@@ -45,7 +45,7 @@ parameters: !var          *# !var/!fix sets whether a GraphSpec may ch
45
45
          y:\
46
46
              tick:\
47
47
                  label:          *# \[text style, look from axis.tick.label\]; The y-axis tick labels. Each parameter not set here is axis.tick.label's.*\
48
-         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
48
+         max_extension: 0.15          *# \[0-1\]; The most an axis may be extended to fit direct labels, keys, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
49
49
      plot_area:\
50
50
          border:          *# \[line style\]; The plot area's border.*\
51
51
              sides: \[left, bottom, top, right\]          *# \[left, right, top, bottom, None\]; The sides that have a border. If None, there is no border.*\
@@ -32,6 +32,7 @@ cp -R examples/sample-data ~/forgegraph-trial
32
32
  | tips.csv | seaborn's tips | Mostly categories: day, time, sex, smoker, party size |
33
33
  | growth.csv | ChickWeight | 50 animals on 4 diets; some series end early; **diet is a number** (1–4) |
34
34
  | seaice.csv | seaborn's seaice | Daily dates; a month of missing values |
35
+ | plate_spectra.csv | UV-Vis spectra of a plate | 60 samples at 76 wavelengths, in four families, for a heat map |
35
36
  | economics.csv | ggplot2's economics | Several monthly series in different units |
36
37
  | lab_export.xlsx | an instrument export | Summary sheet first; title rows above the header; a units row; `-` and `n/a` for missing; an Average row and "End of report" under the data |
37
38
  | lab_runs.xlsx | a LIMS run log | One row per instrument run over 15 weeks; title rows above the header; statuses typed inconsistently; runs still in progress; a total under the data; an Instruments sheet |