fluxplot 0.1.0__tar.gz → 0.4.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 (211) hide show
  1. {fluxplot-0.1.0 → fluxplot-0.4.0}/.gitignore +1 -0
  2. {fluxplot-0.1.0 → fluxplot-0.4.0}/PKG-INFO +112 -62
  3. {fluxplot-0.1.0 → fluxplot-0.4.0}/README.md +111 -61
  4. {fluxplot-0.1.0 → fluxplot-0.4.0}/examples/fluxbox_example.py +1 -1
  5. fluxplot-0.4.0/examples/meanmark_example.py +74 -0
  6. {fluxplot-0.1.0 → fluxplot-0.4.0}/pyproject.toml +1 -1
  7. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/__init__.py +2 -2
  8. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/api.py +41 -5
  9. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/brackets.py +7 -4
  10. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/data.py +9 -4
  11. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/descriptors.py +5 -0
  12. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/fields.py +5 -3
  13. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/integrity.py +13 -0
  14. fluxplot-0.4.0/src/fluxplot/layout.py +418 -0
  15. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/manifest.py +12 -4
  16. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/raster.py +2 -1
  17. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/render.py +2 -2
  18. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/roles.py +6 -0
  19. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/schemas/manifest.schema.json +1773 -6
  20. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/schemas/recipe.schema.json +1 -1
  21. fluxplot-0.4.0/src/fluxplot/signature_fluxplots/__init__.py +7 -0
  22. fluxplot-0.4.0/src/fluxplot/signature_fluxplots/_categorical.py +379 -0
  23. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/signature_fluxplots/_colour.py +3 -3
  24. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/signature_fluxplots/fluxbox.py +123 -21
  25. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/signature_fluxplots/hexmatrix.py +1 -1
  26. fluxplot-0.4.0/src/fluxplot/signature_fluxplots/meanmark.py +349 -0
  27. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/tagger.py +22 -0
  28. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/version.py +3 -2
  29. fluxplot-0.4.0/tests/fixtures/scene3d-library/SHA256SUMS.json +35 -0
  30. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/box-axes.fluxplot.json +2 -2
  31. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/box-axes.glb +0 -0
  32. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/box-axes.recipe.json +1 -1
  33. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/categorical-missing.fluxplot.json +2 -2
  34. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/categorical-missing.glb +0 -0
  35. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/categorical-missing.recipe.json +1 -1
  36. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/continuous.fluxplot.json +2 -2
  37. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/continuous.glb +0 -0
  38. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/continuous.recipe.json +1 -1
  39. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-a.fluxplot.json +2 -2
  40. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-a.glb +0 -0
  41. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-a.recipe.json +1 -1
  42. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-b.fluxplot.json +2 -2
  43. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-b.glb +0 -0
  44. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-b.recipe.json +1 -1
  45. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-incompatible.fluxplot.json +2 -2
  46. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-incompatible.glb +0 -0
  47. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/morph-incompatible.recipe.json +1 -1
  48. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/named-parts.fluxplot.json +2 -2
  49. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/named-parts.glb +0 -0
  50. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/named-parts.recipe.json +1 -1
  51. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/plain.fluxplot.json +2 -2
  52. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/plain.glb +0 -0
  53. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/plain.recipe.json +1 -1
  54. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/scalebar.fluxplot.json +2 -2
  55. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/scalebar.glb +0 -0
  56. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/scalebar.recipe.json +1 -1
  57. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/sequence.fluxplot.json +2 -2
  58. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/sequence.glb +0 -0
  59. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/sequence.recipe.json +1 -1
  60. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/states.fluxplot.json +2 -2
  61. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/states.glb +0 -0
  62. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d-library/states.recipe.json +1 -1
  63. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_brackets.py +3 -3
  64. fluxplot-0.1.0/tests/test_glowbar.py → fluxplot-0.4.0/tests/test_categorical.py +21 -124
  65. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_dark_ground.py +14 -15
  66. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_fluxbox.py +89 -11
  67. fluxplot-0.4.0/tests/test_layout_capture.py +112 -0
  68. fluxplot-0.4.0/tests/test_layout_escapees.py +20 -0
  69. fluxplot-0.4.0/tests/test_meanmark.py +318 -0
  70. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_schema_complete.py +4 -4
  71. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_series_colors.py +5 -5
  72. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_tick_group_completeness.py +2 -1
  73. fluxplot-0.1.0/examples/glowbar_example.py +0 -54
  74. fluxplot-0.1.0/src/fluxplot/signature_fluxplots/__init__.py +0 -18
  75. fluxplot-0.1.0/src/fluxplot/signature_fluxplots/glowbar.py +0 -769
  76. fluxplot-0.1.0/tests/fixtures/scene3d-library/SHA256SUMS.json +0 -35
  77. {fluxplot-0.1.0 → fluxplot-0.4.0}/LICENSE +0 -0
  78. {fluxplot-0.1.0 → fluxplot-0.4.0}/THIRD_PARTY_NOTICES.md +0 -0
  79. {fluxplot-0.1.0 → fluxplot-0.4.0}/examples/growth_plot.py +0 -0
  80. {fluxplot-0.1.0 → fluxplot-0.4.0}/examples/hexmatrix_example.py +0 -0
  81. {fluxplot-0.1.0 → fluxplot-0.4.0}/examples/image_example.py +0 -0
  82. {fluxplot-0.1.0 → fluxplot-0.4.0}/examples/scene3d_demo.py +0 -0
  83. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_fieldmap.py +0 -0
  84. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_mesh_reduce.py +0 -0
  85. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_scene3d_size.py +0 -0
  86. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_viewer/THIRD-PARTY.txt +0 -0
  87. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_viewer/flux-model3d-viewer.min.js +0 -0
  88. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/_viewer/stamp.json +0 -0
  89. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/autotag.py +0 -0
  90. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/base.mplstyle +0 -0
  91. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/canonical_json.py +0 -0
  92. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/capture.py +0 -0
  93. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/colorcheck.py +0 -0
  94. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/colors.py +0 -0
  95. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/colorscale.py +0 -0
  96. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/definitions/colormaps.json +0 -0
  97. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/definitions/flexoki.tokens.json +0 -0
  98. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/definitions/palettes.json +0 -0
  99. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/fits.py +0 -0
  100. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/glb.py +0 -0
  101. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/ids.py +0 -0
  102. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/images.py +0 -0
  103. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/mesh3d.py +0 -0
  104. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/panels.py +0 -0
  105. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/postprocess.py +0 -0
  106. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/presets.py +0 -0
  107. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/provenance.py +0 -0
  108. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/recipe.py +0 -0
  109. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/scene3d.py +0 -0
  110. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/scene3d_manifest.py +0 -0
  111. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/scene3d_viewer.py +0 -0
  112. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/schemas/.gitkeep +0 -0
  113. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/schemas/scene3d.schema.json +0 -0
  114. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/seaborn_adapters.py +0 -0
  115. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/stats/__init__.py +0 -0
  116. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/stats/_common.py +0 -0
  117. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/stats/multi_group.py +0 -0
  118. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/stats/paired.py +0 -0
  119. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/stats/two_group.py +0 -0
  120. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/style.py +0 -0
  121. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/surface.py +0 -0
  122. {fluxplot-0.1.0 → fluxplot-0.4.0}/src/fluxplot/surface3d.py +0 -0
  123. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/_helpers.py +0 -0
  124. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/conftest.py +0 -0
  125. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/colorscale_vectors.json +0 -0
  126. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/fields.fluxplot.json +0 -0
  127. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/fields.png +0 -0
  128. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/fields.recipe.json +0 -0
  129. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/fields.svg +0 -0
  130. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-a.fluxplot.json +0 -0
  131. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-a.recipe.json +0 -0
  132. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-a.svg +0 -0
  133. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-b.fluxplot.json +0 -0
  134. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-b.recipe.json +0 -0
  135. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/panels-b.svg +0 -0
  136. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/presets.fluxplot.json +0 -0
  137. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/presets.recipe.json +0 -0
  138. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/polish/presets.svg +0 -0
  139. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/SHA256SUMS.json +0 -0
  140. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/box-axes.fluxplot.json +0 -0
  141. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/box-axes.glb +0 -0
  142. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/categorical-missing.fluxplot.json +0 -0
  143. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/categorical-missing.glb +0 -0
  144. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/continuous.fluxplot.json +0 -0
  145. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/continuous.glb +0 -0
  146. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-a.fluxplot.json +0 -0
  147. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-a.glb +0 -0
  148. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-b.fluxplot.json +0 -0
  149. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-b.glb +0 -0
  150. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-incompatible.fluxplot.json +0 -0
  151. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/morph-incompatible.glb +0 -0
  152. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/named-parts.fluxplot.json +0 -0
  153. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/named-parts.glb +0 -0
  154. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/plain.fluxplot.json +0 -0
  155. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/plain.glb +0 -0
  156. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/scalebar.fluxplot.json +0 -0
  157. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/scalebar.glb +0 -0
  158. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/sequence.fluxplot.json +0 -0
  159. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/sequence.glb +0 -0
  160. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/states.fluxplot.json +0 -0
  161. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/fixtures/scene3d/states.glb +0 -0
  162. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/generate_colorscale_vectors.py +0 -0
  163. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/generate_polish_fixtures.py +0 -0
  164. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/generate_scene3d_fixtures.py +0 -0
  165. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/generate_scene3d_library_fixtures.py +0 -0
  166. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_alpha_by.py +0 -0
  167. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_autotag_promotion.py +0 -0
  168. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_band.py +0 -0
  169. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_capture.py +0 -0
  170. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_color_definitions.py +0 -0
  171. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_colorcheck.py +0 -0
  172. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_colors.py +0 -0
  173. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_colorscale.py +0 -0
  174. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_core_fixes.py +0 -0
  175. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_data_kind.py +0 -0
  176. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_determinism.py +0 -0
  177. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_errorbar_legend.py +0 -0
  178. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_examples_fresh.py +0 -0
  179. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_extra_sweep.py +0 -0
  180. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_fits.py +0 -0
  181. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_hexmatrix.py +0 -0
  182. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_id_stability.py +0 -0
  183. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_ids.py +0 -0
  184. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_images.py +0 -0
  185. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_m1_correctness.py +0 -0
  186. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_marker_dom.py +0 -0
  187. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_parts_contract.py +0 -0
  188. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_polish.py +0 -0
  189. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_provenance.py +0 -0
  190. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_rasterize.py +0 -0
  191. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_recipe_rerun.py +0 -0
  192. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_scaffold.py +0 -0
  193. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_scene3d.py +0 -0
  194. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_scene3d_api.py +0 -0
  195. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_scene3d_contract.py +0 -0
  196. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_scene3d_static.py +0 -0
  197. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_schema.py +0 -0
  198. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_seaborn_coverage.py +0 -0
  199. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_semantic_integrity.py +0 -0
  200. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_staged_write.py +0 -0
  201. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_stats.py +0 -0
  202. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_stats_multi.py +0 -0
  203. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_step_stem_axes.py +0 -0
  204. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_style.py +0 -0
  205. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_surface.py +0 -0
  206. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_tag_seaborn.py +0 -0
  207. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_theme.py +0 -0
  208. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_tick_deref.py +0 -0
  209. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_twins_figure.py +0 -0
  210. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_view_geometry.py +0 -0
  211. {fluxplot-0.1.0 → fluxplot-0.4.0}/tests/test_wrappers.py +0 -0
@@ -17,3 +17,4 @@ test-results/
17
17
 
18
18
  # Personal planning/spec docs — kept local, never published
19
19
  notes/
20
+ _plot_lab/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fluxplot
3
- Version: 0.1.0
3
+ Version: 0.4.0
4
4
  Summary: matplotlib, but every meaningful thing has a name — semantic SVG plots for the Flux ecosystem
5
5
  Project-URL: Homepage, https://github.com/fluxsci/fluxplot
6
6
  Project-URL: Repository, https://github.com/fluxsci/fluxplot
@@ -297,6 +297,24 @@ anchors + a scale type is the complete, portable contract. (FluxPlot computes th
297
297
  dpi-invariant fraction method that round-trips *exactly* against the positions matplotlib actually
298
298
  emits — verified in `NOTES_matplotlib_svg.md`.)
299
299
 
300
+ ### Layout capture — resizing without rerunning Python
301
+
302
+ Schema 0.4 adds a `layout` block, captured after the final SVG-renderer draw while the layout
303
+ is frozen. It records the original engine and GridSpec inputs, axes domains and aspect rules,
304
+ text anchors/extents, font advances, numeric locator/formatter parameters, legends, colorbars
305
+ and coordinate frames. Initial layout inputs are preserved across repeated saves, so the same
306
+ figure remains deterministic even when a draw adjusts its limits or subplot parameters.
307
+
308
+ Flux uses these facts to reflow a placed plot at a new physical size without measuring browser
309
+ text or executing Python. Data follows axes affines; glyphs, typography and stroke widths keep
310
+ their point sizes. Tick values identify existing labels across reticks. The matplotlib oracle in
311
+ `tools/relayout_oracle.py` generates layout, tick and solver truth for the Flux verification gates.
312
+ Date/category/custom (including locale-specific) ticks reposition without reticking. Point-sized glyphs anchored to data keep their point geometry: fluxbox boxes (`box` frames) and meanmark circles, arms and caps (`mark` frames, rebuilt exactly as `_MeanPart.get_path` draws them). Secondary
313
+ axes with affine conversions reflow; non-affine conversions record a named transform refusal. A dynamic major locator that emits no
314
+ native ticks records `ticks:no-template`, since there is no faithful SVG tick template to clone.
315
+ Polar, 3D, subfigures, compressed
316
+ layout, usetex and unsupported transforms record named refusals; their SVG still saves normally.
317
+
300
318
  ---
301
319
 
302
320
  ## Semantic IDs: the universal join key
@@ -475,58 +493,89 @@ label rather than being dropped.
475
493
 
476
494
  **Signature fluxplots** — complete, opinionated plot types that are unique to Flux. They take a
477
495
  DataFrame (pandas, polars or a dict of columns) plus column names, seaborn-style, and name every
478
- part for you. The first is the **glowbar**:
496
+ part for you. **Meanmark** draws an arithmetic mean as an outlined circle with a tinted
497
+ interior and two uncertainty arms, matching the compact mean-and-error marks used in scientific
498
+ figures:
479
499
 
480
500
  ```python
481
- gb = fp.glowbar(df, x="condition", y="APP/GAPDH", units="subject", ax=ax)
482
- gb = fp.glowbar(df, x="condition", y="signal", units="mouse", # paired / repeated measures
483
- connect_identical_points_across_x_values=True, ax=ax)
501
+ mm = fp.meanmark(df, x="condition", y="signal", units="subject", ax=ax)
502
+ # Summary alone, solid dots, 95% Student-t confidence intervals and endpoint caps:
503
+ mm = fp.meanmark(df, x="condition", y="signal", ax=ax,
504
+ show_individual_points=False, interval="ci", confidence=.95,
505
+ mark_fill_color="stroke", show_caps=True)
484
506
  ```
485
507
 
486
- Every observation is a dot; beside each group sits a slim bar that *glows* — its ink densest at
487
- the centre of the distribution and fading to hard caps at the interval ends — with the **mean** as
488
- a heavy, haloed line across the bar and the **median** as a V-notch cut into both of its edges.
489
- The interval is mean ± SEM by default, glowing around the mean; `interval="iqr"` spans the box of
490
- a box plot and glows around the median, `"sd"` gives mean ± SD, and a callable
491
- `values -> (low, high)` is accepted. Default groups use ColorBrewer's `YlGnBu`, then `YlOrRd`.
492
-
493
- With `units=` every unit (animal, subject, cell) keeps a **fixed lane and colour derived from the
494
- table, never from the values**, so plots of different measures made from one table agree dot for
495
- dot — even when a unit is missing from one of them. Unit colours are equal *perceptual* steps
496
- (CAM02-UCS) of each group's ColorBrewer map between `shade_range=(88, 22)` lightness, dealt across
497
- lanes so neighbours always contrast (`interleave_shades=True`), and rimmed in a deeper shade of
498
- themselves so the palest dots stay crisp (`point_edge="rim"`). Connectors are a quiet neutral grey
499
- and break at a unit's missing category rather than bridging it.
500
-
501
- | part | default id | role |
508
+ The default is a **95% Student-t confidence interval** around the mean (`interval="ci"`,
509
+ `confidence=.95`), using sample SD (`ddof=1`). `"sem"` gives mean ± SEM, `"sd"` gives mean ± SD,
510
+ `"range"` spans the observations, and a
511
+ callable `values -> (low, high)` supplies absolute bounds (finite and containing the mean).
512
+ Statistics count finite observations, without collapsing repeated rows per unit. A singleton has
513
+ an observable mean but no estimable SD/SEM/CI: its circle is drawn with null uncertainty bounds.
514
+ Empty categories retain a tick and have no summary. `orientation="horizontal"` rotates the layout;
515
+ `x` still supplies categories and `y` values. `value_scale="log"` changes the value axis while
516
+ preserving the arithmetic mean in original units; nonpositive data or bounds raise an error.
517
+
518
+ The circle diameter (`mark_size=4.5`) and outline (`mark_edge_width=1.5`) are in points. Its stroke
519
+ uses the group colour unless `mark_color` overrides it. `mark_fill_color="auto"` mixes 25% of the
520
+ stroke with the axes ground into an opaque tint (`mark_fill_mix=.25`). Use `"stroke"` for a solid
521
+ dot, `"none"` for a hollow circle, or any explicit colour. `mark_fill_alpha` affects only the fill.
522
+ The arms stop at the circle rim, so a transparent interior never reveals a line through its centre;
523
+ intervals shorter than the radius fit inside the circle and have no visible arm beyond it.
524
+ `error_width=1.7` / `error_color` control the arms; `show_error=False` hides them.
525
+ Caps are on by default (`cap_size=3.5`, `cap_width=1.2`); `cap_color` overrides their colour.
526
+ Use `show_caps=False` to remove them. Each endpoint remains individually editable.
527
+ `show_notch=True` cuts two V-shaped notches into the circle's actual outline, at the exact mean.
528
+ `notch_depth` and `notch_height` are in points; the cutouts are transparent on any background.
529
+
530
+ With `units=` every unit keeps a lane and colour derived from the table, never the measured values,
531
+ so different measures from the same table agree even with missing values. Points default to the
532
+ category colour (`point_colors="group"`), with area 13 pt², .06 jitter, .2 pt rims and .45 fill alpha.
533
+ Use `point_colors="shades"` for equal perceptual steps through the palette, interleaved across lanes.
534
+ Paired connectors default on when `units` is supplied (`connect_identical_points_across_x_values="auto"`);
535
+ use `False` to disable them or `True` to require units. They break at missing categories and default to
536
+ .5 pt width, `#5f5f5f`, and .2 alpha. Set `connect_color=None` for the theme's neutral colour.
537
+ These controls are shared with `fluxbox`, whose defaults differ:
538
+
539
+ - `show_individual_points`, `point_size` (points²), `jitter`, `point_edge`, `point_edge_width`,
540
+ `point_fill_alpha` (the rim stays opaque).
541
+ - `palette`, `group_color`, `group_color_position`, `point_colors`, `shade_range`, `interleave_shades`.
542
+ Palettes accept FluxPlot and matplotlib colormaps, discrete palettes, or individual colours;
543
+ mappings choose a palette per category. Defaults cycle through `YlGnBu`, `YlOrRd`, `RdPu`,
544
+ `BuGn`, `Purples`, and `YlOrBr`.
545
+ - `connect_identical_points_across_x_values`, `connect_line_width`, `connect_color`, `connect_alpha`.
546
+ - `order`, `unit_order`, `series`, `unit_series`, `label_axes`, `zorder`.
547
+
548
+ `mark_offset=None` resolves to .25 category units when points or connectors are shown, otherwise zero.
549
+ `mark_side="right"` puts summaries to the right; `"left"` places them to the left,
550
+ and `"outer"` puts the first left and the others right. For horizontal plots those sides are below/above.
551
+ Set `mark_offset=0` to centre all summaries on their category ticks.
552
+
553
+ | part | default SVG id | role / editing kind |
502
554
  |---|---|---|
503
- | interval glow | `<category>.glow` | `box` |
504
- | interval caps | `<category>.caps` | `cap` |
505
- | mean line | `<category>.mean` | `mean` |
506
- | median notch | `<category>.median` | `median` |
507
- | a unit's point(s) / connector | `<unit>.points`, `<unit>.point.<k>` / `<unit>.line` | `point` / `line` |
508
- | points without `units` | `<category>.points`, `<category>.point.<k>` | `point` |
509
-
510
- Each category series carries a `glowbar` manifest payload with the exact statistics drawn (`n`,
511
- `mean`, `median`, `sd`, `sem`, `q1`, `q3`, `interval`, `low`, `high`, `center`, `x`, `groupColor`),
512
- and each unit series its identity (`units`, `unit`, `categories`, `colors`); the manifest's
513
- `plotType` is `"glowbar"`. Series names default to the category / unit values — `series=` and
514
- `unit_series=` (a mapping or a callable) rename them. Every visual choice is a keyword:
515
- `bar_width`, `bar_offset`, `bar_side` (`"outer"` — the default: the first category's bar to
516
- the left of its points, every other to the right — or `"left"` / `"right"` for all), `glow_steps`, `glow_alpha`, `mean_line_width`, `mean_color`,
517
- `mean_halo_width`, `median_notch_depth`, `median_notch_height`, `cap_width`, `cap_color`,
518
- `show_mean`/`show_median`/`show_caps`/`show_individual_points`, `point_size`, `jitter`,
519
- `point_edge`, `point_fill_alpha` (fill only — the rim stays opaque), `palette` (per category: any
520
- `fp.colors.maps` colormap such as `"cmasher.emerald"`, any `fp.colors.palettes` palette such as
521
- `"brewer.Set2"` / `"tol.bright"`, a matplotlib map, a list of colours or one colour — glowbar picks
522
- as many distinct point colours as it needs plus a solid group colour), `group_color`, `group_color_position`, `point_colors`, `shade_range`,
523
- `interleave_shades`, `connect_line_width`, `connect_color`, `connect_alpha` (see
524
- `help(fp.glowbar)`). It returns a `GlowbarResult` (`.ax`, `.categories`, `.stats`, `.group_colors`,
525
- `.point_colors`, `.series`, `.unit_series`, `.artists`). `examples/glowbar_example.py` draws both
526
- designs.
527
-
528
- The **fluxbox** is the glowbar with a box plot for its summary — the same call, the same lanes,
529
- colours, connectors and names, so the two can be swapped for one another dot for dot:
555
+ | mean circle, including its cutout outline | `<category>.mean` | `mean` / shape |
556
+ | lower uncertainty arm | `<category>.error-low` | `errorbar` / line |
557
+ | upper uncertainty arm | `<category>.error-high` | `errorbar` / line |
558
+ | optional lower / upper caps | `<category>.cap-low`, `<category>.cap-high` | `cap` / line |
559
+ | unit points / connector | `<unit>.points`, `<unit>.point.<k>` / `<unit>.line` | `point` / `line` |
560
+ | points without units | `<category>.points`, `<category>.point.<k>` | `point` |
561
+
562
+ `fp.save` writes the SVG, manifest and regeneration recipe together. The manifest lists the circle,
563
+ each arm and each cap as separate components, with `meanmark` statistics (`n`, `mean`, `meanKind`,
564
+ `sd`, `sem`, `interval`, `confidence`, `low`, `high`, `x`, `y`, `categoryPosition`, `orientation`,
565
+ `scale`, `groupColor`, `palette`) and unit identity. IDs persist when caps, points or notches are
566
+ toggled; lower/upper always refer to data values, including on inverted axes. The recipe records
567
+ the producing script; use `fp.params` and `recipe={"params": ...}` to expose adjustable controls
568
+ for Flux regeneration (see `examples/meanmark_example.py`). Geometry lives in the SVG, statistics
569
+ in the manifest, and regeneration settings in the recipe.
570
+
571
+ The return value is `MeanmarkResult` with `.ax`, `.categories`, `.stats`, `.group_colors`,
572
+ `.point_colors`, `.series`, `.unit_series`, `.orientation`, and `.artists`. Artists are grouped under
573
+ `mean`, `error_low`, `error_high`, `cap_low`, `cap_high`, `points`, and `lines`; each summary part
574
+ is an independent matplotlib artist. Vertical plots also support `.positions` and `.brackets(...)`.
575
+
576
+ The **fluxbox** uses a box plot for its summary — the same call, the same lanes,
577
+ colours, connectors and names. Set their shared controls explicitly to preserve the same
578
+ point styling when swapping between them:
530
579
 
531
580
  ```python
532
581
  fb = fp.fluxbox(df, x="condition", y="APP/GAPDH", units="subject", ax=ax)
@@ -537,7 +586,7 @@ Beside each group sits a slim box (Q1–Q3), a half-strength wash of the group c
537
586
  a dark background lifted, only as far as it takes to differ from the box by `median_contrast=30`
538
587
  units of perceived lightness, so it reads for any palette and theme; the whiskers (and fliers) share
539
588
  that colour, so the box is the only wash. The **mean** is a V-notch cut
540
- into both edges of the box — the glowbar's median notch; a mean outside the box (a strongly skewed
589
+ into both edges of the box; a mean outside the box (a strongly skewed
541
590
  group) keeps its mark as the same two V's drawn solid, pointing in at the whisker. The capless
542
591
  whiskers reach the most extreme observations within `whis` × IQR of the box (`1.5` — Tukey's rule,
543
592
  exactly `plt.boxplot`'s whiskers), `"range"`, or a `(low, high)` pair of percentiles. Observations
@@ -552,7 +601,7 @@ points are hidden (`show_fliers="auto"`).
552
601
  | median line | `<category>.median` | `median` |
553
602
  | mean notch | `<category>.mean` | `mean` |
554
603
  | fliers (points hidden) | `<category>.fliers` | `flier` |
555
- | points / connectors | as for the glowbar | `point` / `line` |
604
+ | points / connectors | as for the meanmark | `point` / `line` |
556
605
 
557
606
  Each category series carries a `fluxbox` manifest payload with the exact statistics drawn (`n`,
558
607
  `mean`, `median`, `sd`, `sem`, `q1`, `q3`, `iqr`, `whis`, `whiskerLow`, `whiskerHigh`, `outliers`,
@@ -561,8 +610,8 @@ Each category series carries a `fluxbox` manifest payload with the exact statist
561
610
  median outright), `median_contrast`, `mean_notch_depth`, `mean_notch_height`, `whisker_width`, `whisker_color`,
562
611
  `cap_size`, `cap_width`, `cap_color`, `flier_size`, `cut_color` and
563
612
  `show_mean`/`show_median`/`show_whiskers`/`show_caps`/`show_fliers`; every point, colour, connector
564
- and naming keyword is the glowbar's. It returns a `FluxboxResult` with the same fields as a
565
- `GlowbarResult`. `examples/fluxbox_example.py` draws both designs.
613
+ and naming keyword is the meanmark's. It returns a `FluxboxResult` with the same core fields as a
614
+ `MeanmarkResult` (without `orientation`). `examples/fluxbox_example.py` draws both designs.
566
615
 
567
616
  The **hexmatrix** tiles the plane with regular hexagons, each one a named part. One call covers a
568
617
  point cloud's density, a 2D gradient of a third variable, and a matrix drawn on a hex lattice:
@@ -674,12 +723,12 @@ test above over the pairs of a `{name: sample}` family and corrects across them:
674
723
  *From rows to brackets* — `fp.brackets(ax, rows, positions=…)` draws one significance bracket per
675
724
  post-hoc row and stacks them automatically: shortest span first, each bracket one `step` above the
676
725
  data it spans and above every bracket it overlaps in x (multiplicative steps on a log axis), so
677
- nothing crosses. `positions` maps group name → x; a glowbar / fluxbox result provides it, and
726
+ nothing crosses. `positions` maps group name → x; a meanmark / fluxbox result provides it, and
678
727
  `gb.brackets(rows)` is the one-liner:
679
728
 
680
729
  ```python
681
730
  rows = fp.stats.pairwise(fp.stats.welch_hedges, {"ctl": ctl, "drug": drug, "sham": sham})
682
- gb = fp.glowbar(data=df, x="group", y="value", ax=ax)
731
+ gb = fp.meanmark(data=df, x="group", y="value", ax=ax)
683
732
  gb.brackets(rows) # *** / ** / * / ns from p_corrected_holm
684
733
  gb.brackets(rows, label="p", ns=False, p_column="p_corrected_bh") # "p = 0.003", drop ns pairs
685
734
  ```
@@ -837,12 +886,12 @@ fluxplot/
837
886
  brackets.py # fp.brackets: stats rows → stacked significance brackets
838
887
  fits.py # fp.regression, fp.kde
839
888
  seaborn_adapters.py # exact identity for seaborn's categorical plots and hue splits
840
- signature_fluxplots/ # preset plot types unique to Flux (fp.glowbar, fp.fluxbox, fp.hexmatrix)
889
+ signature_fluxplots/ # preset plot types unique to Flux (fp.meanmark, fp.fluxbox, fp.hexmatrix)
841
890
  stats/ # tests behind the plots, returning reporting rows (two-group, paired, k-group, post hoc)
842
891
  schemas/ # JSON Schemas for the manifest and recipe (validated on every save)
843
892
  definitions/ # the shipped colormap / palette / token tables
844
893
  examples/growth_plot.py # the worked example above
845
- examples/glowbar_example.py # the glowbar signature plot, unpaired + paired
894
+ examples/meanmark_example.py # the meanmark signature plot, paired + summary variants
846
895
  examples/fluxbox_example.py # the fluxbox signature plot, unpaired + paired
847
896
  examples/hexmatrix_example.py # the hexmatrix: joint, log-log, spatial, gradient, lattice map
848
897
  examples/image_example.py # a two-channel micrograph with a scale bar and per-channel keys
@@ -1054,12 +1103,12 @@ Every series records its primary paint in the manifest — `series[].color = {"h
1054
1103
  "alpha": 1.0, "token": "flexoki.green-400", "palette": {"name": "flexoki.light", "index": 2}}`:
1055
1104
  the exact palette token that names it when one does, its slot in the active cycle when it came
1056
1105
  from there (`"varies"` for a colour-mapped collection, whose `scale` names the colour scale).
1057
- The glowbar and fluxbox payloads record the `palette` spec each category's shades came from.
1106
+ The meanmark and fluxbox payloads record the `palette` spec each category's shades came from.
1058
1107
 
1059
1108
  `fp.colors.categories` keeps categories consistent across figures: `get("SD")` returns the colour
1060
1109
  pinned to a category, else assigns the next unused slot of the palette (default: the theme's
1061
1110
  cycle) in first-request order and remembers it, so `"b"` is the same colour whether or not `"a"`
1062
- is on the plot — `categorical_colors` (surface maps), glowbar and fluxbox group colours and
1111
+ is on the plot — `categorical_colors` (surface maps), meanmark and fluxbox group colours and
1063
1112
  `tag_seaborn`'s hue levels all consult it. `assign({...})` pins colours, `save()` / `load()`
1064
1113
  write and read a project file, `fluxplot.colors.json`
1065
1114
  (`{"spec": "fluxplot/colors", "version": 1, "categories": {"SD": "#bc5215"}, "palette": "flexoki"}`),
@@ -1067,16 +1116,17 @@ which the first use auto-loads from `$FLUXPLOT_COLORS` or the nearest one betwee
1067
1116
  directory and the Git root; nothing is ever written implicitly. `categories.auto_series = True`
1068
1117
  colours `fp.line` / `fp.scatter` series by their name when the call gives no colour (off by
1069
1118
  default). Two more recipe controls follow: `__fluxplot__.palette = "tol.bright"` makes every
1070
- `fx.use_*` install that palette as the prop cycle (and glowbar categories draw from it), and
1119
+ `fx.use_*` install that palette as the prop cycle (and meanmark categories draw from it), and
1071
1120
  `__fluxplot__.series = {"<series id>": {"color": "#…"}}` recolours a series on a rerun — the
1072
1121
  helpers and the signature plots check it before drawing.
1073
1122
 
1074
1123
  ### Signature plots on dark grounds
1075
1124
 
1076
- The glowbar, fluxbox and hexmatrix judge every ink against the ground they are drawn on. On a
1125
+ The meanmark, fluxbox and hexmatrix judge every ink against the ground they are drawn on. On a
1077
1126
  dark theme the point rims lighten instead of darkening, the shades stay at least 20 lightness
1078
- units above the ground, the mean and median inks lift off their marks instead of deepening
1079
- (`_colour.median_ink`, shared by both plots), connectors take the theme's grid neutral, a
1127
+ units above the ground, and fluxbox's median ink lifts off its box instead of deepening
1128
+ (`_colour.median_ink`). Meanmark derives its circle tint from its stroke and the ground.
1129
+ Connectors take the theme's grid neutral, a
1080
1130
  qualitative palette's group ink is the light neutral, and a hexmatrix's single-colour ramp turns
1081
1131
  (deep near the ground → pale at the top, named `hexmatrix.mono-dark:#…` so a rerun rebuilds it).
1082
1132
  `tests/test_dark_ground.py` pins WCAG contrast ≥ 1.5 for every mark and ≥ 3 for bracket and
@@ -267,6 +267,24 @@ anchors + a scale type is the complete, portable contract. (FluxPlot computes th
267
267
  dpi-invariant fraction method that round-trips *exactly* against the positions matplotlib actually
268
268
  emits — verified in `NOTES_matplotlib_svg.md`.)
269
269
 
270
+ ### Layout capture — resizing without rerunning Python
271
+
272
+ Schema 0.4 adds a `layout` block, captured after the final SVG-renderer draw while the layout
273
+ is frozen. It records the original engine and GridSpec inputs, axes domains and aspect rules,
274
+ text anchors/extents, font advances, numeric locator/formatter parameters, legends, colorbars
275
+ and coordinate frames. Initial layout inputs are preserved across repeated saves, so the same
276
+ figure remains deterministic even when a draw adjusts its limits or subplot parameters.
277
+
278
+ Flux uses these facts to reflow a placed plot at a new physical size without measuring browser
279
+ text or executing Python. Data follows axes affines; glyphs, typography and stroke widths keep
280
+ their point sizes. Tick values identify existing labels across reticks. The matplotlib oracle in
281
+ `tools/relayout_oracle.py` generates layout, tick and solver truth for the Flux verification gates.
282
+ Date/category/custom (including locale-specific) ticks reposition without reticking. Point-sized glyphs anchored to data keep their point geometry: fluxbox boxes (`box` frames) and meanmark circles, arms and caps (`mark` frames, rebuilt exactly as `_MeanPart.get_path` draws them). Secondary
283
+ axes with affine conversions reflow; non-affine conversions record a named transform refusal. A dynamic major locator that emits no
284
+ native ticks records `ticks:no-template`, since there is no faithful SVG tick template to clone.
285
+ Polar, 3D, subfigures, compressed
286
+ layout, usetex and unsupported transforms record named refusals; their SVG still saves normally.
287
+
270
288
  ---
271
289
 
272
290
  ## Semantic IDs: the universal join key
@@ -445,58 +463,89 @@ label rather than being dropped.
445
463
 
446
464
  **Signature fluxplots** — complete, opinionated plot types that are unique to Flux. They take a
447
465
  DataFrame (pandas, polars or a dict of columns) plus column names, seaborn-style, and name every
448
- part for you. The first is the **glowbar**:
466
+ part for you. **Meanmark** draws an arithmetic mean as an outlined circle with a tinted
467
+ interior and two uncertainty arms, matching the compact mean-and-error marks used in scientific
468
+ figures:
449
469
 
450
470
  ```python
451
- gb = fp.glowbar(df, x="condition", y="APP/GAPDH", units="subject", ax=ax)
452
- gb = fp.glowbar(df, x="condition", y="signal", units="mouse", # paired / repeated measures
453
- connect_identical_points_across_x_values=True, ax=ax)
471
+ mm = fp.meanmark(df, x="condition", y="signal", units="subject", ax=ax)
472
+ # Summary alone, solid dots, 95% Student-t confidence intervals and endpoint caps:
473
+ mm = fp.meanmark(df, x="condition", y="signal", ax=ax,
474
+ show_individual_points=False, interval="ci", confidence=.95,
475
+ mark_fill_color="stroke", show_caps=True)
454
476
  ```
455
477
 
456
- Every observation is a dot; beside each group sits a slim bar that *glows* — its ink densest at
457
- the centre of the distribution and fading to hard caps at the interval ends — with the **mean** as
458
- a heavy, haloed line across the bar and the **median** as a V-notch cut into both of its edges.
459
- The interval is mean ± SEM by default, glowing around the mean; `interval="iqr"` spans the box of
460
- a box plot and glows around the median, `"sd"` gives mean ± SD, and a callable
461
- `values -> (low, high)` is accepted. Default groups use ColorBrewer's `YlGnBu`, then `YlOrRd`.
462
-
463
- With `units=` every unit (animal, subject, cell) keeps a **fixed lane and colour derived from the
464
- table, never from the values**, so plots of different measures made from one table agree dot for
465
- dot — even when a unit is missing from one of them. Unit colours are equal *perceptual* steps
466
- (CAM02-UCS) of each group's ColorBrewer map between `shade_range=(88, 22)` lightness, dealt across
467
- lanes so neighbours always contrast (`interleave_shades=True`), and rimmed in a deeper shade of
468
- themselves so the palest dots stay crisp (`point_edge="rim"`). Connectors are a quiet neutral grey
469
- and break at a unit's missing category rather than bridging it.
470
-
471
- | part | default id | role |
478
+ The default is a **95% Student-t confidence interval** around the mean (`interval="ci"`,
479
+ `confidence=.95`), using sample SD (`ddof=1`). `"sem"` gives mean ± SEM, `"sd"` gives mean ± SD,
480
+ `"range"` spans the observations, and a
481
+ callable `values -> (low, high)` supplies absolute bounds (finite and containing the mean).
482
+ Statistics count finite observations, without collapsing repeated rows per unit. A singleton has
483
+ an observable mean but no estimable SD/SEM/CI: its circle is drawn with null uncertainty bounds.
484
+ Empty categories retain a tick and have no summary. `orientation="horizontal"` rotates the layout;
485
+ `x` still supplies categories and `y` values. `value_scale="log"` changes the value axis while
486
+ preserving the arithmetic mean in original units; nonpositive data or bounds raise an error.
487
+
488
+ The circle diameter (`mark_size=4.5`) and outline (`mark_edge_width=1.5`) are in points. Its stroke
489
+ uses the group colour unless `mark_color` overrides it. `mark_fill_color="auto"` mixes 25% of the
490
+ stroke with the axes ground into an opaque tint (`mark_fill_mix=.25`). Use `"stroke"` for a solid
491
+ dot, `"none"` for a hollow circle, or any explicit colour. `mark_fill_alpha` affects only the fill.
492
+ The arms stop at the circle rim, so a transparent interior never reveals a line through its centre;
493
+ intervals shorter than the radius fit inside the circle and have no visible arm beyond it.
494
+ `error_width=1.7` / `error_color` control the arms; `show_error=False` hides them.
495
+ Caps are on by default (`cap_size=3.5`, `cap_width=1.2`); `cap_color` overrides their colour.
496
+ Use `show_caps=False` to remove them. Each endpoint remains individually editable.
497
+ `show_notch=True` cuts two V-shaped notches into the circle's actual outline, at the exact mean.
498
+ `notch_depth` and `notch_height` are in points; the cutouts are transparent on any background.
499
+
500
+ With `units=` every unit keeps a lane and colour derived from the table, never the measured values,
501
+ so different measures from the same table agree even with missing values. Points default to the
502
+ category colour (`point_colors="group"`), with area 13 pt², .06 jitter, .2 pt rims and .45 fill alpha.
503
+ Use `point_colors="shades"` for equal perceptual steps through the palette, interleaved across lanes.
504
+ Paired connectors default on when `units` is supplied (`connect_identical_points_across_x_values="auto"`);
505
+ use `False` to disable them or `True` to require units. They break at missing categories and default to
506
+ .5 pt width, `#5f5f5f`, and .2 alpha. Set `connect_color=None` for the theme's neutral colour.
507
+ These controls are shared with `fluxbox`, whose defaults differ:
508
+
509
+ - `show_individual_points`, `point_size` (points²), `jitter`, `point_edge`, `point_edge_width`,
510
+ `point_fill_alpha` (the rim stays opaque).
511
+ - `palette`, `group_color`, `group_color_position`, `point_colors`, `shade_range`, `interleave_shades`.
512
+ Palettes accept FluxPlot and matplotlib colormaps, discrete palettes, or individual colours;
513
+ mappings choose a palette per category. Defaults cycle through `YlGnBu`, `YlOrRd`, `RdPu`,
514
+ `BuGn`, `Purples`, and `YlOrBr`.
515
+ - `connect_identical_points_across_x_values`, `connect_line_width`, `connect_color`, `connect_alpha`.
516
+ - `order`, `unit_order`, `series`, `unit_series`, `label_axes`, `zorder`.
517
+
518
+ `mark_offset=None` resolves to .25 category units when points or connectors are shown, otherwise zero.
519
+ `mark_side="right"` puts summaries to the right; `"left"` places them to the left,
520
+ and `"outer"` puts the first left and the others right. For horizontal plots those sides are below/above.
521
+ Set `mark_offset=0` to centre all summaries on their category ticks.
522
+
523
+ | part | default SVG id | role / editing kind |
472
524
  |---|---|---|
473
- | interval glow | `<category>.glow` | `box` |
474
- | interval caps | `<category>.caps` | `cap` |
475
- | mean line | `<category>.mean` | `mean` |
476
- | median notch | `<category>.median` | `median` |
477
- | a unit's point(s) / connector | `<unit>.points`, `<unit>.point.<k>` / `<unit>.line` | `point` / `line` |
478
- | points without `units` | `<category>.points`, `<category>.point.<k>` | `point` |
479
-
480
- Each category series carries a `glowbar` manifest payload with the exact statistics drawn (`n`,
481
- `mean`, `median`, `sd`, `sem`, `q1`, `q3`, `interval`, `low`, `high`, `center`, `x`, `groupColor`),
482
- and each unit series its identity (`units`, `unit`, `categories`, `colors`); the manifest's
483
- `plotType` is `"glowbar"`. Series names default to the category / unit values — `series=` and
484
- `unit_series=` (a mapping or a callable) rename them. Every visual choice is a keyword:
485
- `bar_width`, `bar_offset`, `bar_side` (`"outer"` — the default: the first category's bar to
486
- the left of its points, every other to the right — or `"left"` / `"right"` for all), `glow_steps`, `glow_alpha`, `mean_line_width`, `mean_color`,
487
- `mean_halo_width`, `median_notch_depth`, `median_notch_height`, `cap_width`, `cap_color`,
488
- `show_mean`/`show_median`/`show_caps`/`show_individual_points`, `point_size`, `jitter`,
489
- `point_edge`, `point_fill_alpha` (fill only — the rim stays opaque), `palette` (per category: any
490
- `fp.colors.maps` colormap such as `"cmasher.emerald"`, any `fp.colors.palettes` palette such as
491
- `"brewer.Set2"` / `"tol.bright"`, a matplotlib map, a list of colours or one colour — glowbar picks
492
- as many distinct point colours as it needs plus a solid group colour), `group_color`, `group_color_position`, `point_colors`, `shade_range`,
493
- `interleave_shades`, `connect_line_width`, `connect_color`, `connect_alpha` (see
494
- `help(fp.glowbar)`). It returns a `GlowbarResult` (`.ax`, `.categories`, `.stats`, `.group_colors`,
495
- `.point_colors`, `.series`, `.unit_series`, `.artists`). `examples/glowbar_example.py` draws both
496
- designs.
497
-
498
- The **fluxbox** is the glowbar with a box plot for its summary — the same call, the same lanes,
499
- colours, connectors and names, so the two can be swapped for one another dot for dot:
525
+ | mean circle, including its cutout outline | `<category>.mean` | `mean` / shape |
526
+ | lower uncertainty arm | `<category>.error-low` | `errorbar` / line |
527
+ | upper uncertainty arm | `<category>.error-high` | `errorbar` / line |
528
+ | optional lower / upper caps | `<category>.cap-low`, `<category>.cap-high` | `cap` / line |
529
+ | unit points / connector | `<unit>.points`, `<unit>.point.<k>` / `<unit>.line` | `point` / `line` |
530
+ | points without units | `<category>.points`, `<category>.point.<k>` | `point` |
531
+
532
+ `fp.save` writes the SVG, manifest and regeneration recipe together. The manifest lists the circle,
533
+ each arm and each cap as separate components, with `meanmark` statistics (`n`, `mean`, `meanKind`,
534
+ `sd`, `sem`, `interval`, `confidence`, `low`, `high`, `x`, `y`, `categoryPosition`, `orientation`,
535
+ `scale`, `groupColor`, `palette`) and unit identity. IDs persist when caps, points or notches are
536
+ toggled; lower/upper always refer to data values, including on inverted axes. The recipe records
537
+ the producing script; use `fp.params` and `recipe={"params": ...}` to expose adjustable controls
538
+ for Flux regeneration (see `examples/meanmark_example.py`). Geometry lives in the SVG, statistics
539
+ in the manifest, and regeneration settings in the recipe.
540
+
541
+ The return value is `MeanmarkResult` with `.ax`, `.categories`, `.stats`, `.group_colors`,
542
+ `.point_colors`, `.series`, `.unit_series`, `.orientation`, and `.artists`. Artists are grouped under
543
+ `mean`, `error_low`, `error_high`, `cap_low`, `cap_high`, `points`, and `lines`; each summary part
544
+ is an independent matplotlib artist. Vertical plots also support `.positions` and `.brackets(...)`.
545
+
546
+ The **fluxbox** uses a box plot for its summary — the same call, the same lanes,
547
+ colours, connectors and names. Set their shared controls explicitly to preserve the same
548
+ point styling when swapping between them:
500
549
 
501
550
  ```python
502
551
  fb = fp.fluxbox(df, x="condition", y="APP/GAPDH", units="subject", ax=ax)
@@ -507,7 +556,7 @@ Beside each group sits a slim box (Q1–Q3), a half-strength wash of the group c
507
556
  a dark background lifted, only as far as it takes to differ from the box by `median_contrast=30`
508
557
  units of perceived lightness, so it reads for any palette and theme; the whiskers (and fliers) share
509
558
  that colour, so the box is the only wash. The **mean** is a V-notch cut
510
- into both edges of the box — the glowbar's median notch; a mean outside the box (a strongly skewed
559
+ into both edges of the box; a mean outside the box (a strongly skewed
511
560
  group) keeps its mark as the same two V's drawn solid, pointing in at the whisker. The capless
512
561
  whiskers reach the most extreme observations within `whis` × IQR of the box (`1.5` — Tukey's rule,
513
562
  exactly `plt.boxplot`'s whiskers), `"range"`, or a `(low, high)` pair of percentiles. Observations
@@ -522,7 +571,7 @@ points are hidden (`show_fliers="auto"`).
522
571
  | median line | `<category>.median` | `median` |
523
572
  | mean notch | `<category>.mean` | `mean` |
524
573
  | fliers (points hidden) | `<category>.fliers` | `flier` |
525
- | points / connectors | as for the glowbar | `point` / `line` |
574
+ | points / connectors | as for the meanmark | `point` / `line` |
526
575
 
527
576
  Each category series carries a `fluxbox` manifest payload with the exact statistics drawn (`n`,
528
577
  `mean`, `median`, `sd`, `sem`, `q1`, `q3`, `iqr`, `whis`, `whiskerLow`, `whiskerHigh`, `outliers`,
@@ -531,8 +580,8 @@ Each category series carries a `fluxbox` manifest payload with the exact statist
531
580
  median outright), `median_contrast`, `mean_notch_depth`, `mean_notch_height`, `whisker_width`, `whisker_color`,
532
581
  `cap_size`, `cap_width`, `cap_color`, `flier_size`, `cut_color` and
533
582
  `show_mean`/`show_median`/`show_whiskers`/`show_caps`/`show_fliers`; every point, colour, connector
534
- and naming keyword is the glowbar's. It returns a `FluxboxResult` with the same fields as a
535
- `GlowbarResult`. `examples/fluxbox_example.py` draws both designs.
583
+ and naming keyword is the meanmark's. It returns a `FluxboxResult` with the same core fields as a
584
+ `MeanmarkResult` (without `orientation`). `examples/fluxbox_example.py` draws both designs.
536
585
 
537
586
  The **hexmatrix** tiles the plane with regular hexagons, each one a named part. One call covers a
538
587
  point cloud's density, a 2D gradient of a third variable, and a matrix drawn on a hex lattice:
@@ -644,12 +693,12 @@ test above over the pairs of a `{name: sample}` family and corrects across them:
644
693
  *From rows to brackets* — `fp.brackets(ax, rows, positions=…)` draws one significance bracket per
645
694
  post-hoc row and stacks them automatically: shortest span first, each bracket one `step` above the
646
695
  data it spans and above every bracket it overlaps in x (multiplicative steps on a log axis), so
647
- nothing crosses. `positions` maps group name → x; a glowbar / fluxbox result provides it, and
696
+ nothing crosses. `positions` maps group name → x; a meanmark / fluxbox result provides it, and
648
697
  `gb.brackets(rows)` is the one-liner:
649
698
 
650
699
  ```python
651
700
  rows = fp.stats.pairwise(fp.stats.welch_hedges, {"ctl": ctl, "drug": drug, "sham": sham})
652
- gb = fp.glowbar(data=df, x="group", y="value", ax=ax)
701
+ gb = fp.meanmark(data=df, x="group", y="value", ax=ax)
653
702
  gb.brackets(rows) # *** / ** / * / ns from p_corrected_holm
654
703
  gb.brackets(rows, label="p", ns=False, p_column="p_corrected_bh") # "p = 0.003", drop ns pairs
655
704
  ```
@@ -807,12 +856,12 @@ fluxplot/
807
856
  brackets.py # fp.brackets: stats rows → stacked significance brackets
808
857
  fits.py # fp.regression, fp.kde
809
858
  seaborn_adapters.py # exact identity for seaborn's categorical plots and hue splits
810
- signature_fluxplots/ # preset plot types unique to Flux (fp.glowbar, fp.fluxbox, fp.hexmatrix)
859
+ signature_fluxplots/ # preset plot types unique to Flux (fp.meanmark, fp.fluxbox, fp.hexmatrix)
811
860
  stats/ # tests behind the plots, returning reporting rows (two-group, paired, k-group, post hoc)
812
861
  schemas/ # JSON Schemas for the manifest and recipe (validated on every save)
813
862
  definitions/ # the shipped colormap / palette / token tables
814
863
  examples/growth_plot.py # the worked example above
815
- examples/glowbar_example.py # the glowbar signature plot, unpaired + paired
864
+ examples/meanmark_example.py # the meanmark signature plot, paired + summary variants
816
865
  examples/fluxbox_example.py # the fluxbox signature plot, unpaired + paired
817
866
  examples/hexmatrix_example.py # the hexmatrix: joint, log-log, spatial, gradient, lattice map
818
867
  examples/image_example.py # a two-channel micrograph with a scale bar and per-channel keys
@@ -1024,12 +1073,12 @@ Every series records its primary paint in the manifest — `series[].color = {"h
1024
1073
  "alpha": 1.0, "token": "flexoki.green-400", "palette": {"name": "flexoki.light", "index": 2}}`:
1025
1074
  the exact palette token that names it when one does, its slot in the active cycle when it came
1026
1075
  from there (`"varies"` for a colour-mapped collection, whose `scale` names the colour scale).
1027
- The glowbar and fluxbox payloads record the `palette` spec each category's shades came from.
1076
+ The meanmark and fluxbox payloads record the `palette` spec each category's shades came from.
1028
1077
 
1029
1078
  `fp.colors.categories` keeps categories consistent across figures: `get("SD")` returns the colour
1030
1079
  pinned to a category, else assigns the next unused slot of the palette (default: the theme's
1031
1080
  cycle) in first-request order and remembers it, so `"b"` is the same colour whether or not `"a"`
1032
- is on the plot — `categorical_colors` (surface maps), glowbar and fluxbox group colours and
1081
+ is on the plot — `categorical_colors` (surface maps), meanmark and fluxbox group colours and
1033
1082
  `tag_seaborn`'s hue levels all consult it. `assign({...})` pins colours, `save()` / `load()`
1034
1083
  write and read a project file, `fluxplot.colors.json`
1035
1084
  (`{"spec": "fluxplot/colors", "version": 1, "categories": {"SD": "#bc5215"}, "palette": "flexoki"}`),
@@ -1037,16 +1086,17 @@ which the first use auto-loads from `$FLUXPLOT_COLORS` or the nearest one betwee
1037
1086
  directory and the Git root; nothing is ever written implicitly. `categories.auto_series = True`
1038
1087
  colours `fp.line` / `fp.scatter` series by their name when the call gives no colour (off by
1039
1088
  default). Two more recipe controls follow: `__fluxplot__.palette = "tol.bright"` makes every
1040
- `fx.use_*` install that palette as the prop cycle (and glowbar categories draw from it), and
1089
+ `fx.use_*` install that palette as the prop cycle (and meanmark categories draw from it), and
1041
1090
  `__fluxplot__.series = {"<series id>": {"color": "#…"}}` recolours a series on a rerun — the
1042
1091
  helpers and the signature plots check it before drawing.
1043
1092
 
1044
1093
  ### Signature plots on dark grounds
1045
1094
 
1046
- The glowbar, fluxbox and hexmatrix judge every ink against the ground they are drawn on. On a
1095
+ The meanmark, fluxbox and hexmatrix judge every ink against the ground they are drawn on. On a
1047
1096
  dark theme the point rims lighten instead of darkening, the shades stay at least 20 lightness
1048
- units above the ground, the mean and median inks lift off their marks instead of deepening
1049
- (`_colour.median_ink`, shared by both plots), connectors take the theme's grid neutral, a
1097
+ units above the ground, and fluxbox's median ink lifts off its box instead of deepening
1098
+ (`_colour.median_ink`). Meanmark derives its circle tint from its stroke and the ground.
1099
+ Connectors take the theme's grid neutral, a
1050
1100
  qualitative palette's group ink is the light neutral, and a hexmatrix's single-colour ramp turns
1051
1101
  (deep near the ground → pale at the top, named `hexmatrix.mono-dark:#…` so a rerun rebuilds it).
1052
1102
  `tests/test_dark_ground.py` pins WCAG contrast ≥ 1.5 for every mark and ≥ 3 for bracket and
@@ -1,4 +1,4 @@
1
- """The fluxbox — the glowbar with a box plot for its summary — on the glowbar's two designs.
1
+ """The fluxbox — a box plot with individual observations, in two designs.
2
2
 
3
3
  * ``out/fluxbox.svg`` two independent groups, one dot per animal (fixed lane + colour per animal)
4
4
  * ``out/fluxbox_paired.svg`` the same animals measured twice, joined across conditions