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