jarvisplot 2.1.0__tar.gz → 2.1.2__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 (243) hide show
  1. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/PKG-INFO +1 -1
  2. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/SOURCES.txt +2 -0
  3. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/PKG-INFO +1 -1
  4. jarvisplot-2.1.2/jarvisplot/Figure/corr_layout_diamond.py +491 -0
  5. jarvisplot-2.1.2/jarvisplot/Figure/corrplot_runtime.py +1178 -0
  6. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/figure.py +18 -0
  7. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/capabilities.py +75 -4
  8. jarvisplot-2.1.2/jarvisplot/cards/corrplot/diamond.json +555 -0
  9. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/corrplot/matrix.json +15 -2
  10. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/style_preference.json +2 -1
  11. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/core_runtime.py +243 -45
  12. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/man_methods.py +153 -19
  13. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/man_render_agent.py +2 -0
  14. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/method_notes.yaml +3 -1
  15. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/type-correlation-matrix.yaml +82 -6
  16. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/core/figure.json +1 -1
  17. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/corrplot.json +2 -2
  18. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/templates_catalog.py +40 -2
  19. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/validation.py +173 -0
  20. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/template.py +1 -0
  21. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/pyproject.toml +1 -1
  22. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_capabilities.py +39 -0
  23. jarvisplot-2.1.2/tests/test_corrplot.py +1330 -0
  24. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_man_cli.py +49 -0
  25. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_template_catalog.py +29 -0
  26. jarvisplot-2.1.0/jarvisplot/Figure/corrplot_runtime.py +0 -556
  27. jarvisplot-2.1.0/tests/test_corrplot.py +0 -596
  28. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  29. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  30. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/requires.txt +0 -0
  31. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/JarvisPLOT.egg-info/top_level.txt +0 -0
  32. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/README.md +0 -0
  33. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/__init__.py +0 -0
  34. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/adapters_rect.py +0 -0
  35. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/adapters_ternary.py +0 -0
  36. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
  37. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  38. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/colorbar_runtime.py +0 -0
  39. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/config_runtime.py +0 -0
  40. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/corr_layout.py +0 -0
  41. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/corr_order.py +0 -0
  42. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/correlation_runtime.py +0 -0
  43. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/data_pipelines.py +0 -0
  44. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/debug_config.py +0 -0
  45. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/density_cell_runtime.py +0 -0
  46. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/design_runtime.py +0 -0
  47. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/dynesty_runtime.py +0 -0
  48. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/figure_types.py +0 -0
  49. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/helper.py +0 -0
  50. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
  51. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
  52. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
  53. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/layer_runtime.py +0 -0
  54. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/layout_runtime.py +0 -0
  55. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/method_registry.py +0 -0
  56. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
  57. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/posterior_hpd.py +0 -0
  58. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/posterior_mesh.py +0 -0
  59. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/preprocessor.py +0 -0
  60. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/preprocessor_runtime.py +0 -0
  61. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/profile_runtime.py +0 -0
  62. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/significance_runtime.py +0 -0
  63. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/Figure/style_runtime.py +0 -0
  64. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/__init__.py +0 -0
  65. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/agent_digest.py +0 -0
  66. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/agent_io.py +0 -0
  67. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cache_store.py +0 -0
  68. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  69. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -0
  70. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
  71. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
  72. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
  73. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +0 -0
  74. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/rectratio.json +0 -0
  75. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
  76. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
  77. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  78. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
  79. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  80. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  81. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/args.json +0 -0
  82. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/colors/colormaps.json +0 -0
  83. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/default/output.json +0 -0
  84. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/flowchart/default.json +0 -0
  85. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  86. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
  87. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
  88. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
  89. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  90. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
  91. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
  92. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
  93. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
  94. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
  95. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
  96. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/gambit.png +0 -0
  97. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cards/icons/gambit_small.png +0 -0
  98. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cli.py +0 -0
  99. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/cli_help.py +0 -0
  100. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/client.py +0 -0
  101. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/column_demand.py +0 -0
  102. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/column_probe.py +0 -0
  103. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/config.py +0 -0
  104. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/config_address.py +0 -0
  105. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/core.py +0 -0
  106. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/core_assets.py +0 -0
  107. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/data_access.py +0 -0
  108. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/data_loader.py +0 -0
  109. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/data_loader_hdf5.py +0 -0
  110. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/data_loader_runtime.py +0 -0
  111. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/data_loader_summary.py +0 -0
  112. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/dataset_types.py +0 -0
  113. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/diagnostic_guidance.py +0 -0
  114. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/diagnostics.py +0 -0
  115. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/dryrun_runtime.py +0 -0
  116. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/expr_names.py +0 -0
  117. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/fix_apply.py +0 -0
  118. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/flowchart.py +0 -0
  119. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/inner_func.py +0 -0
  120. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/man_catalog.py +0 -0
  121. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/man_render_human.py +0 -0
  122. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/man_transforms.py +0 -0
  123. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/agent-output.yaml +0 -0
  124. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/cli-map.yaml +0 -0
  125. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/data-columns.yaml +0 -0
  126. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
  127. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
  128. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
  129. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/layer-method.yaml +0 -0
  130. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/manifest.yaml +0 -0
  131. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/style-axes.yaml +0 -0
  132. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
  133. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
  134. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/validate.yaml +0 -0
  135. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/workflow.yaml +0 -0
  136. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
  137. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/memtrace.py +0 -0
  138. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/method_contracts.py +0 -0
  139. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/render_health.py +0 -0
  140. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/core/common.json +0 -0
  141. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/core/dataset.json +0 -0
  142. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/core/layer.json +0 -0
  143. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/core/transform.json +0 -0
  144. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/manifest.json +0 -0
  145. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/bar.json +0 -0
  146. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/barh.json +0 -0
  147. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/contour.json +0 -0
  148. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/contourf.json +0 -0
  149. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
  150. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/errorbar.json +0 -0
  151. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/fill.json +0 -0
  152. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/fill_between.json +0 -0
  153. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
  154. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/hist.json +0 -0
  155. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/hist2d.json +0 -0
  156. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/imshow.json +0 -0
  157. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/jpcontour.json +0 -0
  158. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/jpcontourf.json +0 -0
  159. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/jpfield.json +0 -0
  160. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/pcolor.json +0 -0
  161. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/pcolormesh.json +0 -0
  162. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/plot.json +0 -0
  163. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/quiver.json +0 -0
  164. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/scatter.json +0 -0
  165. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/stairs.json +0 -0
  166. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/step.json +0 -0
  167. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/tricontour.json +0 -0
  168. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/tricontourf.json +0 -0
  169. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/tripcolor.json +0 -0
  170. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
  171. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/triplot.json +0 -0
  172. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/voronoi.json +0 -0
  173. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/methods/voronoif.json +0 -0
  174. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema/plot-config.json +0 -0
  175. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema_catalog.py +0 -0
  176. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/schema_diagnostics.py +0 -0
  177. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/transform_contracts.py +0 -0
  178. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/__init__.py +0 -0
  179. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/cmaps.py +0 -0
  180. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/dataframes.py +0 -0
  181. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/expression.py +0 -0
  182. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/interpolator.py +0 -0
  183. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/utils/pathing.py +0 -0
  184. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/__init__.py +0 -0
  185. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/cap.py +0 -0
  186. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/config_cmd.py +0 -0
  187. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/data.py +0 -0
  188. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/doctor.py +0 -0
  189. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/dryrun.py +0 -0
  190. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/explain.py +0 -0
  191. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/man.py +0 -0
  192. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/suggest.py +0 -0
  193. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/verbs/validate.py +0 -0
  194. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/jarvisplot/yaml_io.py +0 -0
  195. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/setup.cfg +0 -0
  196. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_agent_digest.py +0 -0
  197. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_agent_envelope.py +0 -0
  198. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_axis_title.py +0 -0
  199. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_card_debug_shape.py +0 -0
  200. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_cli_help.py +0 -0
  201. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_cli_verbs.py +0 -0
  202. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_cmaps.py +0 -0
  203. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_column_existence.py +0 -0
  204. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_config_addressing.py +0 -0
  205. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_config_edit.py +0 -0
  206. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_config_expand.py +0 -0
  207. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_correlation_runtime.py +0 -0
  208. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_data_access.py +0 -0
  209. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_data_describe.py +0 -0
  210. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_data_head_eval.py +0 -0
  211. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_data_loader_contracts.py +0 -0
  212. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_data_pipelines.py +0 -0
  213. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_debug_config.py +0 -0
  214. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_debug_switches.py +0 -0
  215. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_debug_yaml_override.py +0 -0
  216. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_density_cell_transform.py +0 -0
  217. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_design_runtime.py +0 -0
  218. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_diagnostic_guidance.py +0 -0
  219. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_docs_status_labels.py +0 -0
  220. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_doctor_partial.py +0 -0
  221. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_explain.py +0 -0
  222. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_figure_contracts.py +0 -0
  223. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_figure_type_expander.py +0 -0
  224. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_fix_application.py +0 -0
  225. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_flowchart_renderer.py +0 -0
  226. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_imshow_adapter.py +0 -0
  227. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_interp_2d_transform.py +0 -0
  228. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_interp_natural_neighbor.py +0 -0
  229. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_interpolator_contracts.py +0 -0
  230. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_jpcontour_methods.py +0 -0
  231. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_method_contracts.py +0 -0
  232. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_parse_data_metadata.py +0 -0
  233. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_pathing_contracts.py +0 -0
  234. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_posterior_hpd.py +0 -0
  235. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_render_health.py +0 -0
  236. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_row_ledger.py +0 -0
  237. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_schema_catalog.py +0 -0
  238. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_suggest.py +0 -0
  239. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_suggest_axes.py +0 -0
  240. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_template_json.py +0 -0
  241. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_ternary_debug.py +0 -0
  242. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_transform_contracts.py +0 -0
  243. {jarvisplot-2.1.0 → jarvisplot-2.1.2}/tests/test_validate_no_matplotlib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -55,6 +55,7 @@ jarvisplot/Figure/bin_stat_runtime.py
55
55
  jarvisplot/Figure/colorbar_runtime.py
56
56
  jarvisplot/Figure/config_runtime.py
57
57
  jarvisplot/Figure/corr_layout.py
58
+ jarvisplot/Figure/corr_layout_diamond.py
58
59
  jarvisplot/Figure/corr_order.py
59
60
  jarvisplot/Figure/correlation_runtime.py
60
61
  jarvisplot/Figure/corrplot_runtime.py
@@ -97,6 +98,7 @@ jarvisplot/cards/a4paper/4x1/rect_cmap.json
97
98
  jarvisplot/cards/a4paper/4x1/ternary.json
98
99
  jarvisplot/cards/a4paper/4x1/ternary_cmap.json
99
100
  jarvisplot/cards/colors/colormaps.json
101
+ jarvisplot/cards/corrplot/diamond.json
100
102
  jarvisplot/cards/corrplot/matrix.json
101
103
  jarvisplot/cards/default/output.json
102
104
  jarvisplot/cards/flowchart/default.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,491 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Figure geometry for the rotated (diamond) correlation matrix.
4
+
5
+ ``corr_layout`` solves the square card: one half of the matrix is redundant,
6
+ the names are rotated tick labels crammed under a 4.2 mm cell, and the figure
7
+ is bounded by the page *width*. This module solves the other arrangement --
8
+ keep one triangle, turn it 45 degrees -- from the map::
9
+
10
+ u = (j - i) / 2 how far apart the two variables are -> horizontal
11
+ v = (i + j) / 2 the midpoint of the two -> vertical
12
+
13
+ Everything about this card is a consequence of that map:
14
+
15
+ * A cell's four neighbours land at ``(±1/2, ±1/2)``, so a cell is a square
16
+ turned 45 degrees whose two diagonals are one unit each.
17
+ * Variable ``k`` appears in ``(k, j)`` for ``j > k``, running down-right from
18
+ ``(0, k)``, and in ``(i, k)`` for ``i < k``, running up-right to the same
19
+ point. Its pairs are one connected **V** rooted at ``u = 0, v = k`` -- which
20
+ is exactly where its name goes. That is the whole reason for the card: on
21
+ the square one a variable's pairs are a row and a column meeting at a corner.
22
+ * Because item ``k`` sits at ``v = k``, the names stay ordinary y tick labels.
23
+ The ordering, the tick machinery and ``tl.*`` learn nothing new.
24
+
25
+ So the anchor moves. The square card is anchored on the **cell** and bounded
26
+ by the page width; this one is anchored on the **row pitch** -- the label line
27
+ height -- and bounded by the page *height*, since the panel comes out about
28
+ twice as tall as it is wide. Its floor is a different quantity too: below
29
+ ``pitch_min`` it is the names that collide, not the marks that stop being
30
+ readable. Two different anchors, two different bounds, two different floors,
31
+ which is why this is its own solver rather than a branch in the other one.
32
+
33
+ Nothing here imports the rest of the Figure runtime, and the measuring
34
+ primitives are shared with ``corr_layout`` rather than copied.
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ from dataclasses import dataclass, field
40
+ from typing import Any, Mapping, Sequence
41
+
42
+ from .corr_layout import MM_PER_INCH, _geom, max_label_mm, measure_text_mm
43
+
44
+ __all__ = [
45
+ "DiamondGeometry",
46
+ "diamond_extent",
47
+ "solve_diamond_geometry",
48
+ ]
49
+
50
+ #: The two sides the label column may take. ``right`` is the same map with
51
+ #: ``u`` negated; it must never become a second code path.
52
+ SIDES = ("left", "right")
53
+
54
+
55
+ def diamond_extent(n: int, *, diag: bool = False) -> tuple[float, float, float, float]:
56
+ """``(u_lo, u_hi, v_lo, v_hi)`` of the panel, in cell units.
57
+
58
+ Cell centres run ``u ∈ [1/2, (n-1)/2]`` and ``v ∈ [1/2, n-3/2]`` off the
59
+ diagonal; the panel is those plus the half cell that hangs off each end.
60
+ The v range is then exactly ``[0, n-1]``, which is where labels ``0`` and
61
+ ``n-1`` sit -- the first and last name line up with the top and bottom of
62
+ the diamond, as they must.
63
+
64
+ With ``diag`` the self-pairs come in at ``u = 0`` and push both ranges out
65
+ by half a cell.
66
+
67
+ A half, not the quarter the pentagon of
68
+ :func:`corrplot_runtime._self_cell_vertices` actually reaches: that shape
69
+ is how a *fill* keeps a whole cell's area against the edge, and the round
70
+ glyphs do not take it. A circle on the diagonal is a circle, and at the
71
+ quarter it overhung the panel by 0.07 cells -- which is only 0.3 mm, but it
72
+ is 0.3 mm off the one gap the eye checks, the channel between the matrix
73
+ and the names, and it narrowed it on the diagonal rows alone. The panel
74
+ buys the half so that every glyph fits whatever shape it is.
75
+ """
76
+ n = max(int(n), 2)
77
+ if diag:
78
+ return -0.5, n / 2.0, -0.5, n - 0.5
79
+ return 0.0, n / 2.0, 0.0, float(n - 1)
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class DiamondGeometry:
84
+ """A solved diamond layout, in the units each consumer wants.
85
+
86
+ ``pitch_mm`` is the distance from one variable to the next down the label
87
+ column, and it is also the cell's diagonal -- the two are the same number
88
+ by construction, which is what keeps the lattice square.
89
+
90
+ ``label_block_mm`` is the band the column was given; ``label_text_mm`` is
91
+ how much of it the names actually occupy. The difference is page margin,
92
+ and the stripes stop at the text rather than bleeding into it.
93
+
94
+ ``name_pad_mm`` and ``number_pad_mm`` are how far from the panel's edge the
95
+ two columns of the label band are anchored -- the names' outer edge and the
96
+ numbers' outer edge. Both are anchored on their *outer* edge, which is the
97
+ whole point of the band: a number column is only a column if every number
98
+ starts at the same place, and a ragged inner edge is the price. See the
99
+ ``edge_numbers`` branch below.
100
+
101
+ The two ``colorbar_*`` numbers are carried out because the bar's axis label
102
+ is placed in axes fractions of a bar this solve sized: the caller cannot
103
+ turn ``colorbar.label_gap`` into a fraction without knowing the width it is
104
+ a fraction of.
105
+ """
106
+
107
+ figsize: tuple[float, float]
108
+ panel_rect: tuple[float, float, float, float]
109
+ colorbar_rect: tuple[float, float, float, float]
110
+ logo_rect: tuple[float, float, float, float]
111
+ xlim: tuple[float, float]
112
+ ylim: tuple[float, float]
113
+ pitch_mm: float
114
+ width_mm: float
115
+ height_mm: float
116
+ label_block_mm: float
117
+ label_text_mm: float
118
+ name_pad_mm: float
119
+ number_pad_mm: float
120
+ number_size_pt: float
121
+ colorbar_w_mm: float
122
+ colorbar_label_gap_mm: float
123
+ side: str
124
+ clamped: bool
125
+ n_cells: int
126
+ notes: list[str] = field(default_factory=list)
127
+
128
+ @property
129
+ def panel_w_mm(self) -> float:
130
+ return self.panel_rect[2] * self.figsize[0] * MM_PER_INCH
131
+
132
+ @property
133
+ def panel_h_mm(self) -> float:
134
+ return self.panel_rect[3] * self.figsize[1] * MM_PER_INCH
135
+
136
+ def as_frame(self) -> dict:
137
+ """The ``Frame`` fragment this geometry implies.
138
+
139
+ The same three keys the square solver writes, so the caller merges it
140
+ the same way and neither card is a special case downstream.
141
+ """
142
+ return {
143
+ "figure": {"figsize": list(self.figsize)},
144
+ "axes": {
145
+ "axcorr": {"rect": list(self.panel_rect)},
146
+ "axccorr": {"rect": list(self.colorbar_rect), "xticks": []},
147
+ "axlogo": {"rect": list(self.logo_rect)},
148
+ },
149
+ }
150
+
151
+
152
+ def solve_diamond_geometry(
153
+ n: int,
154
+ *,
155
+ geometry: Mapping[str, Any],
156
+ labels: Sequence[Any] = (),
157
+ colorbar_labels: Sequence[Any] = (),
158
+ colorbar_title: Any = "",
159
+ side: str = "right",
160
+ diag: bool = False,
161
+ edge_numbers: bool = False,
162
+ label_size_pt: float = 6.0,
163
+ label_pad_mm: float = 0.0,
164
+ number_size_pt: float = 4.2,
165
+ colorbar_label_size_pt: float = 5.5,
166
+ colorbar_tick_pad_mm: float = 0.6,
167
+ colorbar_title_size_pt: float = 7.0,
168
+ family: str = "sans",
169
+ ) -> DiamondGeometry:
170
+ """Solve the figure size and axes rects for an ``n`` variable diamond.
171
+
172
+ ``labels`` are printed horizontally in a column of their own, so what they
173
+ cost is their width -- once, in a band that has to exist anyway -- rather
174
+ than a rotated tick label under every cell.
175
+ """
176
+ n = max(int(n), 2)
177
+ side = str(side or "left").strip().lower()
178
+ if side not in SIDES:
179
+ raise ValueError(
180
+ "corrplot side must be one of {}; got {!r}. It mirrors the figure: "
181
+ "the names go on that side and the colorbar on the other.".format(
182
+ ", ".join(SIDES), side
183
+ )
184
+ )
185
+
186
+ pitch = _geom(geometry, "pitch", default=4.2)
187
+ pitch_min = _geom(geometry, "pitch_min", default=2.6)
188
+ max_width = _geom(geometry, "max_width", default=170.0)
189
+ max_height = _geom(geometry, "max_height", default=247.0)
190
+ m_left = _geom(geometry, "margin", "left", default=11.0)
191
+ m_right = _geom(geometry, "margin", "right", default=11.0)
192
+ m_top = _geom(geometry, "margin", "top", default=4.0)
193
+ m_bottom = _geom(geometry, "margin", "bottom", default=11.0)
194
+ margin = (geometry.get("margin") or {}) if isinstance(geometry, Mapping) else {}
195
+ fit = bool(margin.get("fit", False))
196
+ fit_slack = _geom(geometry, "margin", "slack", default=1.0)
197
+ label_gap = _geom(geometry, "labels", "gap", default=1.4)
198
+ number_gap = _geom(geometry, "labels", "number_gap", default=1.0)
199
+ # `labels.width: null` means measured. An author states it when two
200
+ # figures have to line up down the page and the names differ.
201
+ authored_column = _geom(geometry, "labels", "width", default=0.0)
202
+ cb_w = _geom(geometry, "colorbar", "width", default=2.6)
203
+ # A fraction of the panel's height, not a length in mm: the bar is a key to
204
+ # the panel beside it, so it should keep the same visual weight against a
205
+ # 13-variable matrix and a 40-variable one.
206
+ cb_len = _geom(geometry, "colorbar", "length", default=0.22)
207
+ # ... and how far its outer edge stands in from the edge of the paper.
208
+ cb_offset = _geom(geometry, "colorbar", "offset", default=10.0)
209
+ # ... and how far its own axis label stands off its inward edge.
210
+ cb_label_gap = _geom(geometry, "colorbar", "label_gap", default=1.5)
211
+ logo_w = _geom(geometry, "logo", "width", default=5.0)
212
+ logo_h = _geom(geometry, "logo", "height", default=5.0)
213
+ logo_off = _geom(geometry, "logo", "offset", default=0.838)
214
+ logo_corner = str(
215
+ (geometry.get("logo") or {}).get("corner", "far")
216
+ if isinstance(geometry, Mapping) else "far"
217
+ ).strip().lower()
218
+
219
+ notes: list[str] = []
220
+
221
+ # --- the two bands beside the panel ------------------------------------
222
+ #
223
+ # The names are the whole point of this layout, so the column that holds
224
+ # them is measured, not guessed: `pad + widest name + gap`, which is where
225
+ # the text actually ends. `labels.width` overrides it for an author who
226
+ # wants two figures to line up; `margin.fit` is what lets it grow past the
227
+ # page margin, exactly as on the square card.
228
+ name_width = max_label_mm(labels, size_pt=label_size_pt, family=family)
229
+ # With `edge.numbers` the band is two columns, not one: the position, then
230
+ # the name. They are separate because they are set differently -- the
231
+ # number in the smaller, lighter face the same number wears out on the
232
+ # diagonal -- and a tick label is one string in one colour. Splitting them
233
+ # is also what lets the numbers line up: each column is anchored on its own
234
+ # outer edge, so the numbers make a column and the names hang off it.
235
+ number_width = (
236
+ max_label_mm(
237
+ [str(k + 1).zfill(len(str(n))) for k in range(n)],
238
+ size_pt=number_size_pt, family=family,
239
+ )
240
+ if edge_numbers and labels
241
+ else 0.0
242
+ )
243
+ number_block = (number_width + number_gap) if number_width else 0.0
244
+ name_pad = label_pad_mm + name_width
245
+ number_pad = name_pad + number_block
246
+ label_need = number_pad + label_gap
247
+ if authored_column:
248
+ label_block = authored_column
249
+ if label_need > label_block:
250
+ notes.append(
251
+ "labels.width is {:.1f} mm and the names need {:.1f} mm, so "
252
+ "they print past it.".format(label_block, label_need)
253
+ )
254
+ elif fit:
255
+ label_block = max(m_left if side == "left" else m_right, label_need + fit_slack)
256
+ else:
257
+ label_block = m_left if side == "left" else m_right
258
+ if label_need > label_block:
259
+ notes.append(
260
+ "the names need {:.1f} mm and the margin is {:.1f} mm, so they "
261
+ "print past it. margin.fit: true grows it instead.".format(
262
+ label_need, label_block
263
+ )
264
+ )
265
+
266
+ # The colorbar does not stand beside the panel on this card. Turning the
267
+ # matrix leaves a right-angled triangle of empty page above it -- as big as
268
+ # the matrix itself -- and a bar parked outside the panel would pay for a
269
+ # band of width twice over, once for itself and once for the hole. So it
270
+ # goes in the hole, the way a ternary card puts its bar in the corner
271
+ # beside the triangle. The figure is then the names plus the panel, and
272
+ # nothing else.
273
+ cblab = max_label_mm(colorbar_labels, size_pt=colorbar_label_size_pt, family=family)
274
+ cbtitle_w, cbtitle_h = (
275
+ measure_text_mm(colorbar_title, size_pt=colorbar_title_size_pt, family=family)
276
+ if colorbar_title
277
+ else (0.0, 0.0)
278
+ )
279
+
280
+ # The square card floors its bottom-left margin at the badge, because there
281
+ # a cell of the matrix reaches that corner. Here nothing does: the last
282
+ # variable's only cell sits at u = 1/2 and the whole lower corner of the
283
+ # panel's box is empty by construction, so the mark can sit in a 4 mm
284
+ # margin without anything to collide with. The `axlogo` offset is still
285
+ # the house one; it is only the *floor* that does not apply.
286
+ #
287
+ # Top and bottom are then the same band. Nothing prints above the matrix
288
+ # and nothing below it, so an asymmetric pair would read as a figure
289
+ # sitting crookedly on the page rather than as a title band doing
290
+ # something.
291
+ bottom_block = m_bottom
292
+ top_block = m_top
293
+ far_block = m_right if side == "left" else m_left
294
+
295
+ left_block, right_block = (
296
+ (label_block, far_block) if side == "left" else (far_block, label_block)
297
+ )
298
+
299
+ # --- the pitch, and the two page bounds --------------------------------
300
+ u_lo, u_hi, v_lo, v_hi = diamond_extent(n, diag=diag)
301
+ u_span, v_span = u_hi - u_lo, v_hi - v_lo
302
+
303
+ # Height binds first on any matrix worth drawing this way -- the panel is
304
+ # about twice as tall as it is wide -- but both are checked, because a
305
+ # figure that fits the page vertically and runs off it sideways is just as
306
+ # useless. The pitch is what gives; the font never does, for the same
307
+ # reason as on the square card.
308
+ clamped = False
309
+ room_h = (max_height - bottom_block - top_block) / v_span
310
+ room_w = (max_width - left_block - right_block) / u_span
311
+ bound, room = ("height", room_h) if room_h <= room_w else ("width", room_w)
312
+ if room < pitch:
313
+ clamped = True
314
+ if room < pitch_min:
315
+ over = (
316
+ bottom_block + v_span * pitch_min + top_block - max_height
317
+ if bound == "height"
318
+ else left_block + u_span * pitch_min + right_block - max_width
319
+ )
320
+ notes.append(
321
+ "{} variables do not fit {:.0f} mm of {} at the minimum row "
322
+ "pitch ({:.2f} mm needed, {:.2f} mm floor). The figure keeps "
323
+ "the floor and overruns the page by {:.1f} mm. Below the floor "
324
+ "the names collide, so the only real fixes are fewer variables "
325
+ "or a taller page.".format(
326
+ n,
327
+ max_height if bound == "height" else max_width,
328
+ bound, room, pitch_min, over,
329
+ )
330
+ )
331
+ pitch = pitch_min
332
+ else:
333
+ notes.append(
334
+ "{} pinned to {:.0f} mm: row pitch {:.2f} -> {:.2f} mm ({} "
335
+ "variables). Font sizes are unchanged.".format(
336
+ bound,
337
+ max_height if bound == "height" else max_width,
338
+ pitch, room, n,
339
+ )
340
+ )
341
+ pitch = room
342
+
343
+ panel_w = u_span * pitch
344
+ panel_h = v_span * pitch
345
+ width = left_block + panel_w + right_block
346
+ height = bottom_block + panel_h + top_block
347
+
348
+ # --- fractions, in the order add_axes wants them -----------------------
349
+ panel_rect = (
350
+ left_block / width,
351
+ bottom_block / height,
352
+ panel_w / width,
353
+ panel_h / height,
354
+ )
355
+
356
+ # The bar, inside the empty triangle. Two things fix it: its top is the
357
+ # panel's top -- a key that starts where the thing it explains starts reads
358
+ # as belonging to it, and a bar floating below the top edge reads as having
359
+ # drifted -- and its outer edge stands `colorbar.offset` in from the paper.
360
+ bar_len = cb_len * panel_h
361
+ bar_x = (
362
+ width - cb_offset - cb_w if side == "left" else cb_offset
363
+ )
364
+ bar_top = bottom_block + panel_h
365
+
366
+ # The panel's upper boundary is the line `v = u - 1/2` (the first
367
+ # variable's cells), so the headroom above the matrix grows with distance
368
+ # from the label column. Under the bar's *near* edge that headroom is what
369
+ # the bar has to fit in -- less the numbers, when they are printed along
370
+ # that diagonal.
371
+ def _u_of(x_mm: float) -> float:
372
+ return (
373
+ (x_mm - left_block) if side == "left" else (left_block + panel_w - x_mm)
374
+ ) / pitch
375
+
376
+ near = bar_x if side == "left" else bar_x + cb_w
377
+ inward = -1.0 if side == "left" else 1.0
378
+ # Half a cell is the diagonal's own overhang. The edge numbers add their
379
+ # offset plus half their height, and a perpendicular offset costs sqrt(2)
380
+ # as much measured straight down; 1.2 covers the card's own setting.
381
+ clearance = (1.2 if edge_numbers else 0.5) * pitch
382
+
383
+ def _head(reach_mm: float, allow: float) -> float:
384
+ """Page above the matrix, ``reach_mm`` inside the bar's near edge."""
385
+ return (_u_of(near + inward * reach_mm) - 0.5) * pitch - allow
386
+
387
+ def _fits(reach_mm: float, half_extent_mm) -> float:
388
+ """How long the bar may be with something standing ``reach_mm`` inside.
389
+
390
+ ``half_extent_mm`` is half of what that something occupies *along* the
391
+ bar, or ``None`` for text running its whole length. A wall clears the
392
+ diagonal only when the bar's own bottom does; a single line at the
393
+ bar's mid height only needs half the bar plus half itself to.
394
+ """
395
+ head = _head(reach_mm, clearance)
396
+ return head if half_extent_mm is None else 2.0 * (head - half_extent_mm)
397
+
398
+ # The numbers print on the bar's left and the label on its right, whichever
399
+ # way the figure is mirrored -- so *which* of them stands between the bar
400
+ # and the diagonal depends on the mirror, and the two are obstructions of
401
+ # different shapes. The numbers are a column as tall as the bar: a wall.
402
+ # The label is one line at its mid height, turned on its side, so what it
403
+ # reaches inward by is its line height and what it takes up along the bar
404
+ # is its text width.
405
+ numbers = (colorbar_tick_pad_mm + cblab, None) if cblab else None
406
+ title = (cb_label_gap + cbtitle_h, cbtitle_w / 2.0) if cbtitle_h else None
407
+ inward_text, outward_text = (
408
+ (numbers, title) if side == "left" else (title, numbers)
409
+ )
410
+ # These three are *measurements*, not limits. The bar's top, length and
411
+ # offset are the author's three numbers and the solve realises them; what
412
+ # it does here is say when they are tight, not quietly pick others. An
413
+ # earlier version shortened the bar to fit its own clearance allowance and
414
+ # so drew a figure that did not have the length it was asked for -- which
415
+ # is a worse fault than a tight one, because nothing on the page says so.
416
+ cells = _head(0.0, 0.0)
417
+ if bar_len > cells:
418
+ notes.append(
419
+ "the colorbar is {:.1f} mm and only {:.1f} mm of page stands above "
420
+ "the matrix under it, so it is drawn across the cells. A smaller "
421
+ "colorbar.length, or a smaller colorbar.offset -- which moves the "
422
+ "bar out toward the paper, where the triangle is taller -- clears "
423
+ "them.".format(bar_len, max(cells, 0.0))
424
+ )
425
+ elif inward_text is not None and bar_len > _fits(*inward_text):
426
+ notes.append(
427
+ "the colorbar clears the matrix, but the text on its inward side "
428
+ "comes within a cell of the diagonal. A smaller colorbar.offset "
429
+ "moves the bar out toward the paper, where the triangle is taller."
430
+ )
431
+ # ... and whatever is on the other side has the page margin to live in.
432
+ if outward_text is not None and outward_text[0] > cb_offset:
433
+ notes.append(
434
+ "the text on the outer side of the colorbar needs {:.1f} mm and "
435
+ "only {:.1f} mm of paper stands there, so it prints past the page "
436
+ "edge. A larger colorbar.offset moves the bar in.".format(
437
+ outward_text[0], cb_offset
438
+ )
439
+ )
440
+ colorbar_rect = (
441
+ bar_x / width,
442
+ (bar_top - bar_len) / height,
443
+ cb_w / width,
444
+ bar_len / height,
445
+ )
446
+ # The badge keeps the house offset -- 0.838 mm off two edges -- but not
447
+ # necessarily the *left* two. The corner beside the names is the one place
448
+ # on this card where something already is: the last variable's row sits on
449
+ # the panel's bottom edge and the lower run of numbers ends just inside it.
450
+ # The far corner is empty by construction, because the last variable's only
451
+ # cell is at u = 1/2. `logo.corner: bottom-left` puts it back.
452
+ logo_x = logo_off
453
+ if (logo_corner == "far" and side == "left") or logo_corner == "bottom-right":
454
+ logo_x = width - logo_off - logo_w
455
+ logo_rect = (
456
+ logo_x / width,
457
+ logo_off / height,
458
+ logo_w / width,
459
+ logo_h / height,
460
+ )
461
+
462
+ # `u` is negated for a right-hand column, so the triangle opens the other
463
+ # way and the names still sit against the panel's own edge. This is the
464
+ # entire difference between the two sides.
465
+ xlim = (u_lo, u_hi) if side == "left" else (-u_hi, -u_lo)
466
+ # Item 0 at the top, as on the square card and for the same reason: a
467
+ # matrix is read from its first row down.
468
+ ylim = (v_hi, v_lo)
469
+
470
+ return DiamondGeometry(
471
+ figsize=(width / MM_PER_INCH, height / MM_PER_INCH),
472
+ panel_rect=panel_rect,
473
+ colorbar_rect=colorbar_rect,
474
+ logo_rect=logo_rect,
475
+ xlim=xlim,
476
+ ylim=ylim,
477
+ pitch_mm=pitch,
478
+ width_mm=width,
479
+ height_mm=height,
480
+ label_block_mm=label_block,
481
+ label_text_mm=min(label_need, label_block),
482
+ name_pad_mm=name_pad,
483
+ number_pad_mm=number_pad,
484
+ number_size_pt=number_size_pt,
485
+ colorbar_w_mm=cb_w,
486
+ colorbar_label_gap_mm=cb_label_gap,
487
+ side=side,
488
+ clamped=clamped,
489
+ n_cells=n,
490
+ notes=notes,
491
+ )