jarvisplot 1.3.1__tar.gz → 1.3.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 (75) hide show
  1. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/PKG-INFO +11 -10
  2. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/SOURCES.txt +22 -1
  3. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/PKG-INFO +11 -10
  4. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/README.md +10 -9
  5. jarvisplot-1.3.2/jarvisplot/Figure/adapters.py +6 -0
  6. jarvisplot-1.3.1/jarvisplot/Figure/adapters.py → jarvisplot-1.3.2/jarvisplot/Figure/adapters_rect.py +1 -72
  7. jarvisplot-1.3.2/jarvisplot/Figure/adapters_ternary.py +89 -0
  8. jarvisplot-1.3.2/jarvisplot/Figure/colorbar_runtime.py +221 -0
  9. jarvisplot-1.3.2/jarvisplot/Figure/config_runtime.py +87 -0
  10. jarvisplot-1.3.2/jarvisplot/Figure/figure.py +1009 -0
  11. jarvisplot-1.3.2/jarvisplot/Figure/layer_runtime.py +282 -0
  12. jarvisplot-1.3.2/jarvisplot/Figure/layout_runtime.py +228 -0
  13. jarvisplot-1.3.2/jarvisplot/Figure/load_data.py +100 -0
  14. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/preprocessor.py +17 -428
  15. jarvisplot-1.3.2/jarvisplot/Figure/preprocessor_runtime.py +479 -0
  16. jarvisplot-1.3.1/jarvisplot/Figure/load_data.py → jarvisplot-1.3.2/jarvisplot/Figure/profile_runtime.py +21 -138
  17. jarvisplot-1.3.2/jarvisplot/Figure/style_runtime.py +40 -0
  18. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/config.py +4 -3
  19. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/core.py +18 -280
  20. jarvisplot-1.3.2/jarvisplot/core_assets.py +63 -0
  21. jarvisplot-1.3.2/jarvisplot/core_runtime.py +239 -0
  22. jarvisplot-1.3.2/jarvisplot/data_loader.py +490 -0
  23. jarvisplot-1.3.2/jarvisplot/data_loader_hdf5.py +205 -0
  24. jarvisplot-1.3.2/jarvisplot/data_loader_runtime.py +910 -0
  25. jarvisplot-1.3.2/jarvisplot/data_loader_summary.py +83 -0
  26. jarvisplot-1.3.2/jarvisplot/utils/expression.py +66 -0
  27. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/utils/interpolator.py +6 -22
  28. jarvisplot-1.3.2/jarvisplot/utils/pathing.py +31 -0
  29. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/pyproject.toml +1 -1
  30. jarvisplot-1.3.2/tests/test_data_loader_contracts.py +96 -0
  31. jarvisplot-1.3.2/tests/test_docs_status_labels.py +46 -0
  32. jarvisplot-1.3.2/tests/test_figure_contracts.py +189 -0
  33. jarvisplot-1.3.2/tests/test_pathing_contracts.py +75 -0
  34. jarvisplot-1.3.2/tests/test_template_json.py +10 -0
  35. jarvisplot-1.3.1/jarvisplot/Figure/figure.py +0 -1891
  36. jarvisplot-1.3.1/jarvisplot/data_loader.py +0 -1669
  37. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  38. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  39. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/requires.txt +0 -0
  40. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/JarvisPLOT.egg-info/top_level.txt +0 -0
  41. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/__init__.py +0 -0
  42. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  43. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/data_pipelines.py +0 -0
  44. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/helper.py +0 -0
  45. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/Figure/method_registry.py +0 -0
  46. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/__init__.py +0 -0
  47. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cache_store.py +0 -0
  48. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  49. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
  50. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
  51. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
  52. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
  53. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
  54. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  55. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
  56. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  57. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  58. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/args.json +0 -0
  59. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/colors/colormaps.json +0 -0
  60. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/default/output.json +0 -0
  61. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  62. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
  63. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
  64. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
  65. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  66. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/icons/gambit.png +0 -0
  67. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/icons/gambit_small.png +0 -0
  68. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cards/style_preference.json +0 -0
  69. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/cli.py +0 -0
  70. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/client.py +0 -0
  71. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/inner_func.py +0 -0
  72. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/memtrace.py +0 -0
  73. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/utils/__init__.py +0 -0
  74. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/jarvisplot/utils/cmaps.py +0 -0
  75. {jarvisplot-1.3.1 → jarvisplot-1.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -121,18 +121,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
121
121
  ### Documentation
122
122
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
123
123
 
124
- ### Developer Docs
124
+ ### Repository Docs
125
125
 
126
- Tracked architecture and maintenance docs now live in `docs/dev/`.
126
+ Tracked project docs live in `docs/`.
127
127
 
128
- - `docs/dev/ARCHITECTURE_OVERVIEW.md`
129
- - `docs/dev/DATAFLOW_ARCHITECTURE.md`
130
- - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
131
- - `docs/dev/DEVELOPER_RULES.md`
132
- - `docs/dev/MEMTRACE_SYSTEM.md`
133
- - `docs/dev/CONTRIBUTION_GUIDE.md`
128
+ - `docs/README.md` - repo doc index
129
+ - `docs/context/JARVIS_PLOT_CONTEXT.md` - primary Codex-facing boundary doc
130
+ - `docs/context/CODE_MAP_JARVIS_PLOT.md` - concrete code owner map
131
+ - `docs/context/JARVIS_PLOT_FRAMEWORK_LOGIC.md` - runtime execution contract
132
+ - `docs/roadmap/IMPLEMENTATION_ROADMAP.md` - active backlog and future work list
133
+ - `docs/dev/DEVELOPER_RULES.md` - current pipeline and cache rules
134
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md` - narrow-table memory notes
134
135
 
135
- These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
136
+ Read the context docs and roadmap before changing parsing, transforms, rendering, or layout-related behavior.
136
137
 
137
138
  ---
138
139
 
@@ -12,17 +12,31 @@ jarvisplot/cli.py
12
12
  jarvisplot/client.py
13
13
  jarvisplot/config.py
14
14
  jarvisplot/core.py
15
+ jarvisplot/core_assets.py
16
+ jarvisplot/core_runtime.py
15
17
  jarvisplot/data_loader.py
18
+ jarvisplot/data_loader_hdf5.py
19
+ jarvisplot/data_loader_runtime.py
20
+ jarvisplot/data_loader_summary.py
16
21
  jarvisplot/inner_func.py
17
22
  jarvisplot/memtrace.py
18
23
  jarvisplot/Figure/__init__.py
19
24
  jarvisplot/Figure/adapters.py
25
+ jarvisplot/Figure/adapters_rect.py
26
+ jarvisplot/Figure/adapters_ternary.py
27
+ jarvisplot/Figure/colorbar_runtime.py
28
+ jarvisplot/Figure/config_runtime.py
20
29
  jarvisplot/Figure/data_pipelines.py
21
30
  jarvisplot/Figure/figure.py
22
31
  jarvisplot/Figure/helper.py
32
+ jarvisplot/Figure/layer_runtime.py
33
+ jarvisplot/Figure/layout_runtime.py
23
34
  jarvisplot/Figure/load_data.py
24
35
  jarvisplot/Figure/method_registry.py
25
36
  jarvisplot/Figure/preprocessor.py
37
+ jarvisplot/Figure/preprocessor_runtime.py
38
+ jarvisplot/Figure/profile_runtime.py
39
+ jarvisplot/Figure/style_runtime.py
26
40
  jarvisplot/Figure/cards/std_axes_adapter_config.json
27
41
  jarvisplot/cards/args.json
28
42
  jarvisplot/cards/style_preference.json
@@ -47,4 +61,11 @@ jarvisplot/cards/icons/gambit.png
47
61
  jarvisplot/cards/icons/gambit_small.png
48
62
  jarvisplot/utils/__init__.py
49
63
  jarvisplot/utils/cmaps.py
50
- jarvisplot/utils/interpolator.py
64
+ jarvisplot/utils/expression.py
65
+ jarvisplot/utils/interpolator.py
66
+ jarvisplot/utils/pathing.py
67
+ tests/test_data_loader_contracts.py
68
+ tests/test_docs_status_labels.py
69
+ tests/test_figure_contracts.py
70
+ tests/test_pathing_contracts.py
71
+ tests/test_template_json.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -121,18 +121,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
121
121
  ### Documentation
122
122
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
123
123
 
124
- ### Developer Docs
124
+ ### Repository Docs
125
125
 
126
- Tracked architecture and maintenance docs now live in `docs/dev/`.
126
+ Tracked project docs live in `docs/`.
127
127
 
128
- - `docs/dev/ARCHITECTURE_OVERVIEW.md`
129
- - `docs/dev/DATAFLOW_ARCHITECTURE.md`
130
- - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
131
- - `docs/dev/DEVELOPER_RULES.md`
132
- - `docs/dev/MEMTRACE_SYSTEM.md`
133
- - `docs/dev/CONTRIBUTION_GUIDE.md`
128
+ - `docs/README.md` - repo doc index
129
+ - `docs/context/JARVIS_PLOT_CONTEXT.md` - primary Codex-facing boundary doc
130
+ - `docs/context/CODE_MAP_JARVIS_PLOT.md` - concrete code owner map
131
+ - `docs/context/JARVIS_PLOT_FRAMEWORK_LOGIC.md` - runtime execution contract
132
+ - `docs/roadmap/IMPLEMENTATION_ROADMAP.md` - active backlog and future work list
133
+ - `docs/dev/DEVELOPER_RULES.md` - current pipeline and cache rules
134
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md` - narrow-table memory notes
134
135
 
135
- These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
136
+ Read the context docs and roadmap before changing parsing, transforms, rendering, or layout-related behavior.
136
137
 
137
138
  ---
138
139
 
@@ -101,18 +101,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
101
101
  ### Documentation
102
102
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
103
103
 
104
- ### Developer Docs
104
+ ### Repository Docs
105
105
 
106
- Tracked architecture and maintenance docs now live in `docs/dev/`.
106
+ Tracked project docs live in `docs/`.
107
107
 
108
- - `docs/dev/ARCHITECTURE_OVERVIEW.md`
109
- - `docs/dev/DATAFLOW_ARCHITECTURE.md`
110
- - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
111
- - `docs/dev/DEVELOPER_RULES.md`
112
- - `docs/dev/MEMTRACE_SYSTEM.md`
113
- - `docs/dev/CONTRIBUTION_GUIDE.md`
108
+ - `docs/README.md` - repo doc index
109
+ - `docs/context/JARVIS_PLOT_CONTEXT.md` - primary Codex-facing boundary doc
110
+ - `docs/context/CODE_MAP_JARVIS_PLOT.md` - concrete code owner map
111
+ - `docs/context/JARVIS_PLOT_FRAMEWORK_LOGIC.md` - runtime execution contract
112
+ - `docs/roadmap/IMPLEMENTATION_ROADMAP.md` - active backlog and future work list
113
+ - `docs/dev/DEVELOPER_RULES.md` - current pipeline and cache rules
114
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md` - narrow-table memory notes
114
115
 
115
- These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
116
+ Read the context docs and roadmap before changing parsing, transforms, rendering, or layout-related behavior.
116
117
 
117
118
  ---
118
119
 
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from .adapters_rect import StdAxesAdapter
4
+ from .adapters_ternary import TernaryAxesAdapter
5
+
6
+ __all__ = ["StdAxesAdapter", "TernaryAxesAdapter"]
@@ -13,6 +13,7 @@ from .helper import _auto_clip, _mask_by_extend, voronoi_finite_polygons_2d, _cl
13
13
 
14
14
 
15
15
  # —— Basic Adapter: Forward to the underlying Axes, merge default parameters, perform automatic clipping ——
16
+
16
17
  class StdAxesAdapter:
17
18
  def __init__(self, ax: Axes, defaults: Optional[Dict[str, Dict[str, Any]]] = None,
18
19
  clip_path=None):
@@ -963,75 +964,3 @@ class StdAxesAdapter:
963
964
  return [palette[i % len(palette)] for i in range(n)]
964
965
 
965
966
  # —— Ternary 适配器:在 Std 基础上增加 (a,b,c)->(x,y) 投影 ——
966
- class TernaryAxesAdapter(StdAxesAdapter):
967
- def __init__(self, ax: Axes, defaults: Optional[Dict[str, Any]] = None,
968
- clip_path=None):
969
- # Allow a flat defaults dict like {"facecolor": "..."} and keep it internal.
970
- d = dict(defaults) if isinstance(defaults, dict) else {}
971
- facecolor = d.pop("facecolor", None)
972
-
973
- super().__init__(ax, defaults=d or None, clip_path=clip_path)
974
-
975
- if facecolor is not None:
976
- self.set_facecolor(facecolor)
977
-
978
- self.status = "init"
979
-
980
- def set_facecolor(self, color, zorder=-100):
981
- self.ax.fill(
982
- [0.0, 1.0, 0.5],
983
- [0.0, 0.0, 1.0],
984
- facecolor=color,
985
- edgecolor="none",
986
- zorder=zorder
987
- )
988
-
989
- @staticmethod
990
- def _lbr_to_xy(a, b, c):
991
- s = (a + b + c)
992
- s = np.where(s == 0.0, 1.0, s) # 避免除零
993
- aa, bb, cc = a/s, b/s, c/s
994
- x = bb + 0.5 * cc
995
- y = cc
996
- return x, y
997
-
998
- def scatter(self, **kwargs):
999
- if {"left", "right", "bottom"}.issubset(kwargs.keys()):
1000
- x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
1001
- kwargs['x'] = x
1002
- kwargs['y'] = y
1003
- return super().scatter(**kwargs)
1004
-
1005
- def grid_profile(self, **kwargs):
1006
- if {"left", "right", "bottom"}.issubset(kwargs.keys()):
1007
- x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
1008
- kwargs['x'] = x
1009
- kwargs['y'] = y
1010
- return super().grid_profile(**kwargs)
1011
-
1012
-
1013
- def plot(self, **kwargs):
1014
- if {"left", "right", "bottom"}.issubset(kwargs.keys()):
1015
- x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
1016
- kwargs['x'] = x
1017
- kwargs['y'] = y
1018
- return super().plot(**kwargs)
1019
-
1020
-
1021
- def tricontour(self, **kwargs):
1022
- if {"left", "right", "bottom"}.issubset(kwargs.keys()):
1023
- x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
1024
- kwargs['x'] = x
1025
- kwargs['y'] = y
1026
- return super().tricontour( **kwargs)
1027
-
1028
-
1029
- def tricontourf(self, **kwargs):
1030
- if {"left", "right", "bottom"}.issubset(kwargs.keys()):
1031
- x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
1032
- kwargs['x'] = x
1033
- kwargs['y'] = y
1034
-
1035
- return super().tricontourf(**kwargs)
1036
- # else:
1037
- # raise ValueError("scatter() needs either (a,b,c) or (x,y) inputs")
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from __future__ import annotations
4
+ from typing import Any, Dict, Optional
5
+ import numpy as np
6
+ import os
7
+ import json
8
+
9
+ from matplotlib.axes import Axes
10
+ from matplotlib.collections import PolyCollection
11
+ #
12
+ from .helper import _auto_clip, _mask_by_extend, voronoi_finite_polygons_2d, _clip_poly_to_rect
13
+ from .adapters_rect import StdAxesAdapter
14
+
15
+
16
+ # —— Basic Adapter: Forward to the underlying Axes, merge default parameters, perform automatic clipping ——
17
+
18
+ class TernaryAxesAdapter(StdAxesAdapter):
19
+ def __init__(self, ax: Axes, defaults: Optional[Dict[str, Any]] = None,
20
+ clip_path=None):
21
+ # Allow a flat defaults dict like {"facecolor": "..."} and keep it internal.
22
+ d = dict(defaults) if isinstance(defaults, dict) else {}
23
+ facecolor = d.pop("facecolor", None)
24
+
25
+ super().__init__(ax, defaults=d or None, clip_path=clip_path)
26
+
27
+ if facecolor is not None:
28
+ self.set_facecolor(facecolor)
29
+
30
+ self.status = "init"
31
+
32
+ def set_facecolor(self, color, zorder=-100):
33
+ self.ax.fill(
34
+ [0.0, 1.0, 0.5],
35
+ [0.0, 0.0, 1.0],
36
+ facecolor=color,
37
+ edgecolor="none",
38
+ zorder=zorder
39
+ )
40
+
41
+ @staticmethod
42
+ def _lbr_to_xy(a, b, c):
43
+ s = (a + b + c)
44
+ s = np.where(s == 0.0, 1.0, s) # 避免除零
45
+ aa, bb, cc = a/s, b/s, c/s
46
+ x = bb + 0.5 * cc
47
+ y = cc
48
+ return x, y
49
+
50
+ def scatter(self, **kwargs):
51
+ if {"left", "right", "bottom"}.issubset(kwargs.keys()):
52
+ x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
53
+ kwargs['x'] = x
54
+ kwargs['y'] = y
55
+ return super().scatter(**kwargs)
56
+
57
+ def grid_profile(self, **kwargs):
58
+ if {"left", "right", "bottom"}.issubset(kwargs.keys()):
59
+ x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
60
+ kwargs['x'] = x
61
+ kwargs['y'] = y
62
+ return super().grid_profile(**kwargs)
63
+
64
+
65
+ def plot(self, **kwargs):
66
+ if {"left", "right", "bottom"}.issubset(kwargs.keys()):
67
+ x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
68
+ kwargs['x'] = x
69
+ kwargs['y'] = y
70
+ return super().plot(**kwargs)
71
+
72
+
73
+ def tricontour(self, **kwargs):
74
+ if {"left", "right", "bottom"}.issubset(kwargs.keys()):
75
+ x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
76
+ kwargs['x'] = x
77
+ kwargs['y'] = y
78
+ return super().tricontour( **kwargs)
79
+
80
+
81
+ def tricontourf(self, **kwargs):
82
+ if {"left", "right", "bottom"}.issubset(kwargs.keys()):
83
+ x, y = self._lbr_to_xy(kwargs.pop('left'), kwargs.pop('right'), kwargs.pop('bottom'))
84
+ kwargs['x'] = x
85
+ kwargs['y'] = y
86
+
87
+ return super().tricontourf(**kwargs)
88
+ # else:
89
+ # raise ValueError("scatter() needs either (a,b,c) or (x,y) inputs")
@@ -0,0 +1,221 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Mapping
4
+
5
+ import numpy as np
6
+ import matplotlib.colors as mcolors
7
+
8
+ from ..utils.expression import eval_dataframe_expression
9
+
10
+
11
+ def axc_is_horizontal(frame: Mapping[str, Any]) -> bool:
12
+ return str(frame.get("axc", {}).get("orientation", "")).lower() == "horizontal"
13
+
14
+
15
+ def axc_color_config(frame: Mapping[str, Any]) -> dict:
16
+ """Return normalized frame.axc.color config with legacy fallbacks."""
17
+ out: dict[str, Any] = {}
18
+ axc_cfg = frame.get("axc", {}) if isinstance(frame, Mapping) else {}
19
+ color_cfg = axc_cfg.get("color", {})
20
+ if not isinstance(color_cfg, dict):
21
+ color_cfg = {}
22
+
23
+ if "cmap" in color_cfg:
24
+ out["cmap"] = color_cfg.get("cmap")
25
+
26
+ for key in ("vmin", "vmax"):
27
+ if key in color_cfg:
28
+ try:
29
+ val = float(color_cfg.get(key))
30
+ if np.isfinite(val):
31
+ out[key] = val
32
+ except Exception:
33
+ pass
34
+
35
+ scale = color_cfg.get("scale", None)
36
+ if scale is None:
37
+ if axc_is_horizontal(frame):
38
+ scale = axc_cfg.get("xscale", axc_cfg.get("yscale", None))
39
+ else:
40
+ scale = axc_cfg.get("yscale", axc_cfg.get("xscale", None))
41
+ if isinstance(scale, str):
42
+ scale = scale.strip().lower()
43
+ if scale:
44
+ out["scale"] = scale
45
+
46
+ return out
47
+
48
+
49
+ def _coerce_scalar(v):
50
+ try:
51
+ vv = float(v)
52
+ if np.isfinite(vv):
53
+ return vv
54
+ except Exception:
55
+ pass
56
+ return None
57
+
58
+
59
+ def _resolve_norm(nv, *, vmin=None, vmax=None, logger=None):
60
+ if nv is None:
61
+ return None
62
+ if isinstance(nv, mcolors.Normalize):
63
+ return nv
64
+ if isinstance(nv, str):
65
+ key = nv.strip().lower()
66
+ if key in {"log", "lognorm"}:
67
+ if (vmin is None) or (vmax is None) or (vmin <= 0) or (vmax <= 0):
68
+ if logger:
69
+ logger.warning("axc log scale requires positive vmin/vmax; fallback to linear Normalize.")
70
+ return mcolors.Normalize(vmin=vmin, vmax=vmax)
71
+ return mcolors.LogNorm(vmin=vmin, vmax=vmax)
72
+ if key in {"twoslopenorm", "diverging"}:
73
+ return mcolors.TwoSlopeNorm(vcenter=0.0, vmin=vmin, vmax=vmax)
74
+ if key in {"symlog", "symlognorm"}:
75
+ return mcolors.SymLogNorm(linthresh=1.0, linscale=1.0, base=10, vmin=vmin, vmax=vmax)
76
+ return mcolors.Normalize(vmin=vmin, vmax=vmax)
77
+ if isinstance(nv, dict):
78
+ t = str(nv.get("type", "Normalize")).strip().lower()
79
+ _vmin = nv.get("vmin", vmin)
80
+ _vmax = nv.get("vmax", vmax)
81
+ if t in {"log", "lognorm"}:
82
+ if (_vmin is None) or (_vmax is None) or (_vmin <= 0) or (_vmax <= 0):
83
+ if logger:
84
+ logger.warning("axc log norm requires positive vmin/vmax; fallback to linear Normalize.")
85
+ return mcolors.Normalize(vmin=_vmin, vmax=_vmax)
86
+ return mcolors.LogNorm(vmin=_vmin, vmax=_vmax)
87
+ if t in {"twoslopenorm", "diverging"}:
88
+ vcenter = nv.get("vcenter", 0.0)
89
+ return mcolors.TwoSlopeNorm(vcenter=vcenter, vmin=_vmin, vmax=_vmax)
90
+ if t in {"symlog", "symlognorm"}:
91
+ linthresh = nv.get("linthresh", 1.0)
92
+ linscale = nv.get("linscale", 1.0)
93
+ base = nv.get("base", 10)
94
+ return mcolors.SymLogNorm(
95
+ linthresh=linthresh,
96
+ linscale=linscale,
97
+ base=base,
98
+ vmin=_vmin,
99
+ vmax=_vmax,
100
+ )
101
+ return mcolors.Normalize(vmin=_vmin, vmax=_vmax)
102
+ return mcolors.Normalize(vmin=vmin, vmax=vmax)
103
+
104
+
105
+ def collect_and_attach_colorbar(fig, style: dict, coor: dict, method_key: str, df):
106
+ axc = fig.axes.get("axc")
107
+ if axc is None or not hasattr(axc, "_cb"):
108
+ return style
109
+
110
+ s = dict(style)
111
+ colored_z_methods = {
112
+ "contour",
113
+ "contourf",
114
+ "tricontour",
115
+ "tricontourf",
116
+ "tripcolor",
117
+ "tripcolor_axes",
118
+ "pcolor",
119
+ "pcolormesh",
120
+ "imshow",
121
+ "voronoi",
122
+ "voronoif",
123
+ "grid_profile",
124
+ "grid_profiling",
125
+ }
126
+ uses_color = bool(s.get("cmap")) or ("c" in coor) or (("z" in coor) and (method_key in colored_z_methods))
127
+ if not uses_color:
128
+ return style
129
+
130
+ color_cfg = axc_color_config(fig.frame)
131
+
132
+ def _load_color_series():
133
+ arr = None
134
+ if isinstance(coor.get("z"), dict) and ("expr" in coor["z"]):
135
+ arr = eval_dataframe_expression(df, coor["z"]["expr"], logger=fig.logger)
136
+ elif isinstance(coor.get("c"), dict) and ("expr" in coor["c"]):
137
+ arr = eval_dataframe_expression(df, coor["c"]["expr"], logger=fig.logger)
138
+ elif "z" in s:
139
+ arr = s.get("z")
140
+ elif "c" in s and not isinstance(s.get("c"), str):
141
+ arr = s.get("c")
142
+ if arr is None:
143
+ return None
144
+ try:
145
+ arr = np.asarray(arr, dtype=float)
146
+ except Exception:
147
+ return None
148
+ arr = arr[np.isfinite(arr)]
149
+ return arr if arr.size else None
150
+
151
+ chosen_cmap = color_cfg.get("cmap", s.get("cmap"))
152
+ if chosen_cmap is not None:
153
+ s["cmap"] = chosen_cmap
154
+ fig.axc._cb["cmap"] = chosen_cmap
155
+
156
+ data_series = _load_color_series()
157
+ data_vmin = float(np.min(data_series)) if data_series is not None else None
158
+ data_vmax = float(np.max(data_series)) if data_series is not None else None
159
+
160
+ frame_vmin = _coerce_scalar(color_cfg.get("vmin", None))
161
+ frame_vmax = _coerce_scalar(color_cfg.get("vmax", None))
162
+ style_vmin = _coerce_scalar(s.get("vmin", None))
163
+ style_vmax = _coerce_scalar(s.get("vmax", None))
164
+
165
+ if frame_vmin is not None:
166
+ fig.axc._cb["vmin"] = frame_vmin
167
+ else:
168
+ cand_min = style_vmin if style_vmin is not None else data_vmin
169
+ if cand_min is not None:
170
+ cur_min = _coerce_scalar(fig.axc._cb.get("vmin", None))
171
+ fig.axc._cb["vmin"] = cand_min if cur_min is None else min(cur_min, cand_min)
172
+
173
+ if frame_vmax is not None:
174
+ fig.axc._cb["vmax"] = frame_vmax
175
+ else:
176
+ cand_max = style_vmax if style_vmax is not None else data_vmax
177
+ if cand_max is not None:
178
+ cur_max = _coerce_scalar(fig.axc._cb.get("vmax", None))
179
+ fig.axc._cb["vmax"] = cand_max if cur_max is None else max(cur_max, cand_max)
180
+
181
+ if (fig.axc._cb["vmin"] is not None) and (fig.axc._cb["vmax"] is not None):
182
+ vmin = float(fig.axc._cb["vmin"])
183
+ vmax = float(fig.axc._cb["vmax"])
184
+ if vmax < vmin:
185
+ vmin, vmax = vmax, vmin
186
+ fig.axc._cb["vmin"], fig.axc._cb["vmax"] = vmin, vmax
187
+
188
+ requested_scale = str(color_cfg.get("scale", "")).lower()
189
+ if requested_scale in {"linear", "norm", "normalize"}:
190
+ resolved = mcolors.Normalize(vmin=vmin, vmax=vmax)
191
+ elif requested_scale == "log":
192
+ resolved = _resolve_norm("lognorm", vmin=vmin, vmax=vmax, logger=fig.logger)
193
+ else:
194
+ user_norm = style.get("norm", None)
195
+ resolved = _resolve_norm(user_norm, vmin=vmin, vmax=vmax, logger=fig.logger)
196
+ if resolved is None:
197
+ resolved = mcolors.Normalize(vmin=vmin, vmax=vmax)
198
+
199
+ fig.axc._cb["norm"] = resolved
200
+ s["norm"] = fig.axc._cb["norm"]
201
+ if isinstance(fig.axc._cb["norm"], mcolors.LogNorm):
202
+ fig.axc._cb["mode"] = "log"
203
+ elif isinstance(fig.axc._cb["norm"], mcolors.TwoSlopeNorm):
204
+ fig.axc._cb["mode"] = "diverging"
205
+ elif isinstance(fig.axc._cb["norm"], mcolors.SymLogNorm):
206
+ fig.axc._cb["mode"] = "log"
207
+ else:
208
+ fig.axc._cb["mode"] = "norm"
209
+
210
+ if method_key in ("contour", "contourf", "tricontour", "tricontourf") and fig.axc._cb["levels"] is None:
211
+ lv = s.get("levels", 10)
212
+ if isinstance(lv, int) and fig.axc._cb["vmin"] is not None and fig.axc._cb["vmax"] is not None:
213
+ fig.axc._cb["levels"] = np.linspace(fig.axc._cb["vmin"], fig.axc._cb["vmax"], lv)
214
+ elif hasattr(lv, "__len__"):
215
+ fig.axc._cb["levels"] = lv
216
+ if "norm" in s and s["norm"] is not None:
217
+ s.pop("vmin", None)
218
+ s.pop("vmax", None)
219
+ s.pop("mode", None)
220
+ fig.axc._cb["used"] = uses_color
221
+ return s
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Mapping
5
+
6
+
7
+ def apply_figure_config(fig, info: Mapping) -> bool:
8
+ """Apply a YAML figure block to a Figure instance."""
9
+ if not isinstance(info, Mapping):
10
+ raise TypeError("from_dict expects a mapping/dict")
11
+
12
+ try:
13
+ changed = True
14
+ if "name" in info:
15
+ fig.name = info["name"]
16
+ else:
17
+ changed = False
18
+
19
+ if "yaml_dir" in info:
20
+ fig._yaml_dir = info.get("yaml_dir")
21
+ elif "_yaml_dir" in info:
22
+ fig._yaml_dir = info.get("_yaml_dir")
23
+ elif "yaml_path" in info:
24
+ try:
25
+ fig._yaml_dir = str(Path(info.get("yaml_path")).expanduser().resolve().parent)
26
+ except Exception:
27
+ pass
28
+
29
+ if "debug" in info:
30
+ fig.debug = info["debug"]
31
+ try:
32
+ fig.logger.debug("Loading plot -> {} in debug mode".format(fig.name))
33
+ except Exception:
34
+ pass
35
+
36
+ fig._enable = info.get("enable", True)
37
+ if not fig._enable:
38
+ fig.logger.warning("Skip plot -> {}".format(fig.name))
39
+ return False
40
+
41
+ if "style" in info:
42
+ fig.style = info["style"]
43
+ else:
44
+ fig.style = ["a4paper_2x1"]
45
+ fig.logger.debug("Figure style loaded")
46
+ if fig.style and "gambit" in str(fig.style[0]).lower():
47
+ fig.mode = "gambit"
48
+
49
+ if "frame" in info:
50
+ fig.frame = info["frame"]
51
+ fig.logger.debug("Figure frame information loaded")
52
+
53
+ import matplotlib.pyplot as plt
54
+
55
+ plt.rcParams["mathtext.fontset"] = "stix"
56
+ fig.fig = plt.figure(**fig.frame["figure"])
57
+
58
+ if fig.print:
59
+ try:
60
+ if isinstance(fig.frame.get("axes"), dict):
61
+ fig.frame["axes"].pop("axlogo", None)
62
+ fig.frame.pop("axlogo", None)
63
+ except Exception:
64
+ pass
65
+
66
+ fig.load_axes()
67
+
68
+ if "layers" in info:
69
+ fig.layers = info["layers"]
70
+ else:
71
+ changed = False
72
+
73
+ return changed
74
+ except Exception as e:
75
+ if fig.logger:
76
+ try:
77
+ import traceback
78
+
79
+ fig.logger.error(
80
+ "Failed to configure figure '{}': {}".format(
81
+ getattr(fig, "name", "<noname>"), e
82
+ )
83
+ )
84
+ fig.logger.debug(traceback.format_exc())
85
+ except Exception:
86
+ pass
87
+ return False