plot3 0.4.1__tar.gz → 0.4.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 (81) hide show
  1. {plot3-0.4.1 → plot3-0.4.2}/PKG-INFO +1 -1
  2. plot3-0.4.2/plot3/__version__.py +1 -0
  3. {plot3-0.4.1 → plot3-0.4.2}/plot3/function.py +10 -1
  4. {plot3-0.4.1 → plot3-0.4.2}/plot3/viewer.py +24 -2
  5. {plot3-0.4.1 → plot3-0.4.2}/plot3.egg-info/PKG-INFO +1 -1
  6. {plot3-0.4.1 → plot3-0.4.2}/tests/test_animation.py +32 -0
  7. plot3-0.4.1/plot3/__version__.py +0 -1
  8. {plot3-0.4.1 → plot3-0.4.2}/LICENSE +0 -0
  9. {plot3-0.4.1 → plot3-0.4.2}/README.md +0 -0
  10. {plot3-0.4.1 → plot3-0.4.2}/plot3/__init__.py +0 -0
  11. {plot3-0.4.1 → plot3-0.4.2}/plot3/aesexpr.py +0 -0
  12. {plot3-0.4.1 → plot3-0.4.2}/plot3/build.py +0 -0
  13. {plot3-0.4.1 → plot3-0.4.2}/plot3/calculus.py +0 -0
  14. {plot3-0.4.1 → plot3-0.4.2}/plot3/compose.py +0 -0
  15. {plot3-0.4.1 → plot3-0.4.2}/plot3/contour.py +0 -0
  16. {plot3-0.4.1 → plot3-0.4.2}/plot3/craft.py +0 -0
  17. {plot3-0.4.1 → plot3-0.4.2}/plot3/encode.py +0 -0
  18. {plot3-0.4.1 → plot3-0.4.2}/plot3/expr.py +0 -0
  19. {plot3-0.4.1 → plot3-0.4.2}/plot3/flip.py +0 -0
  20. {plot3-0.4.1 → plot3-0.4.2}/plot3/geoms.py +0 -0
  21. {plot3-0.4.1 → plot3-0.4.2}/plot3/ggplot.py +0 -0
  22. {plot3-0.4.1 → plot3-0.4.2}/plot3/io.py +0 -0
  23. {plot3-0.4.1 → plot3-0.4.2}/plot3/jupyter.py +0 -0
  24. {plot3-0.4.1 → plot3-0.4.2}/plot3/latexin.py +0 -0
  25. {plot3-0.4.1 → plot3-0.4.2}/plot3/masking.py +0 -0
  26. {plot3-0.4.1 → plot3-0.4.2}/plot3/mathtext.py +0 -0
  27. {plot3-0.4.1 → plot3-0.4.2}/plot3/payload.py +0 -0
  28. {plot3-0.4.1 → plot3-0.4.2}/plot3/remote.py +0 -0
  29. {plot3-0.4.1 → plot3-0.4.2}/plot3/scales.py +0 -0
  30. {plot3-0.4.1 → plot3-0.4.2}/plot3/scaling.py +0 -0
  31. {plot3-0.4.1 → plot3-0.4.2}/plot3/special.py +0 -0
  32. {plot3-0.4.1 → plot3-0.4.2}/plot3/stat2d.py +0 -0
  33. {plot3-0.4.1 → plot3-0.4.2}/plot3/static.py +0 -0
  34. {plot3-0.4.1 → plot3-0.4.2}/plot3/stats3d.py +0 -0
  35. {plot3-0.4.1 → plot3-0.4.2}/plot3/table.py +0 -0
  36. {plot3-0.4.1 → plot3-0.4.2}/plot3/themes.py +0 -0
  37. {plot3-0.4.1 → plot3-0.4.2}/plot3.egg-info/SOURCES.txt +0 -0
  38. {plot3-0.4.1 → plot3-0.4.2}/plot3.egg-info/dependency_links.txt +0 -0
  39. {plot3-0.4.1 → plot3-0.4.2}/plot3.egg-info/requires.txt +0 -0
  40. {plot3-0.4.1 → plot3-0.4.2}/plot3.egg-info/top_level.txt +0 -0
  41. {plot3-0.4.1 → plot3-0.4.2}/pyproject.toml +0 -0
  42. {plot3-0.4.1 → plot3-0.4.2}/setup.cfg +0 -0
  43. {plot3-0.4.1 → plot3-0.4.2}/tests/test_2d_density.py +0 -0
  44. {plot3-0.4.1 → plot3-0.4.2}/tests/test_3d.py +0 -0
  45. {plot3-0.4.1 → plot3-0.4.2}/tests/test_annotate.py +0 -0
  46. {plot3-0.4.1 → plot3-0.4.2}/tests/test_api_public.py +0 -0
  47. {plot3-0.4.1 → plot3-0.4.2}/tests/test_array_backend.py +0 -0
  48. {plot3-0.4.1 → plot3-0.4.2}/tests/test_arrow.py +0 -0
  49. {plot3-0.4.1 → plot3-0.4.2}/tests/test_build_contract.py +0 -0
  50. {plot3-0.4.1 → plot3-0.4.2}/tests/test_contour.py +0 -0
  51. {plot3-0.4.1 → plot3-0.4.2}/tests/test_everyday_ggplot2.py +0 -0
  52. {plot3-0.4.1 → plot3-0.4.2}/tests/test_expr.py +0 -0
  53. {plot3-0.4.1 → plot3-0.4.2}/tests/test_facets_compose.py +0 -0
  54. {plot3-0.4.1 → plot3-0.4.2}/tests/test_geom_function.py +0 -0
  55. {plot3-0.4.1 → plot3-0.4.2}/tests/test_geoms_ds.py +0 -0
  56. {plot3-0.4.1 → plot3-0.4.2}/tests/test_ggplot2_extras2.py +0 -0
  57. {plot3-0.4.1 → plot3-0.4.2}/tests/test_ggplot2_parity.py +0 -0
  58. {plot3-0.4.1 → plot3-0.4.2}/tests/test_ggplot_extras.py +0 -0
  59. {plot3-0.4.1 → plot3-0.4.2}/tests/test_grammar.py +0 -0
  60. {plot3-0.4.1 → plot3-0.4.2}/tests/test_latex_input.py +0 -0
  61. {plot3-0.4.1 → plot3-0.4.2}/tests/test_lidar_scene.py +0 -0
  62. {plot3-0.4.1 → plot3-0.4.2}/tests/test_masking.py +0 -0
  63. {plot3-0.4.1 → plot3-0.4.2}/tests/test_math_layers.py +0 -0
  64. {plot3-0.4.1 → plot3-0.4.2}/tests/test_mathtext.py +0 -0
  65. {plot3-0.4.1 → plot3-0.4.2}/tests/test_payload.py +0 -0
  66. {plot3-0.4.1 → plot3-0.4.2}/tests/test_payload_display.py +0 -0
  67. {plot3-0.4.1 → plot3-0.4.2}/tests/test_point_cloud.py +0 -0
  68. {plot3-0.4.1 → plot3-0.4.2}/tests/test_r_oracle_parity.py +0 -0
  69. {plot3-0.4.1 → plot3-0.4.2}/tests/test_readme.py +0 -0
  70. {plot3-0.4.1 → plot3-0.4.2}/tests/test_remote_bridge.py +0 -0
  71. {plot3-0.4.1 → plot3-0.4.2}/tests/test_review_fixes.py +0 -0
  72. {plot3-0.4.1 → plot3-0.4.2}/tests/test_save_menu.py +0 -0
  73. {plot3-0.4.1 → plot3-0.4.2}/tests/test_scales_api.py +0 -0
  74. {plot3-0.4.1 → plot3-0.4.2}/tests/test_scales_themes.py +0 -0
  75. {plot3-0.4.1 → plot3-0.4.2}/tests/test_special.py +0 -0
  76. {plot3-0.4.1 → plot3-0.4.2}/tests/test_stat2d.py +0 -0
  77. {plot3-0.4.1 → plot3-0.4.2}/tests/test_static_save.py +0 -0
  78. {plot3-0.4.1 → plot3-0.4.2}/tests/test_stats_semantic.py +0 -0
  79. {plot3-0.4.1 → plot3-0.4.2}/tests/test_step3_geoms.py +0 -0
  80. {plot3-0.4.1 → plot3-0.4.2}/tests/test_table_backends.py +0 -0
  81. {plot3-0.4.1 → plot3-0.4.2}/tests/test_titles_theme.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plot3
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: ggplot2 grammar of graphics for Python: interactive WebGL figures and journal-ready PNG, SVG, and PDF
5
5
  Author: Rigoberto Leyva Salmeron
6
6
  License-Expression: MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.4.2"
@@ -135,9 +135,18 @@ def _bind_swept_symbols(geom, formula: Formula, ranges: dict) -> Formula:
135
135
  params = dict(getattr(geom, "params", None) or {})
136
136
  for name in swept:
137
137
  params[name] = float(ranges[name][0])
138
+ from dataclasses import replace
139
+
138
140
  from plot3.expr import parse_formula
139
141
 
140
- return parse_formula(source, params, defer_missing=True)
142
+ bound = parse_formula(source, params, defer_missing=True)
143
+ # Labels keep the swept symbol: the stand-in value would read "(t = 0)"
144
+ # in the legend and tooltip on every frame.
145
+ texts = (
146
+ "label", "latex", "pretty", "legend_latex", "legend_pretty",
147
+ "caption_latex", "caption_pretty",
148
+ )
149
+ return replace(bound, **{name: getattr(formula, name) for name in texts})
141
150
 
142
151
 
143
152
  def _reject_curve_extras(geom, addons) -> None:
@@ -16,7 +16,8 @@ body{display:flex;flex-direction:column}
16
16
  #canvas-host{position:absolute;z-index:1}
17
17
  #year{display:none;position:absolute;left:50%;top:46%;transform:translate(-50%,-50%);
18
18
  z-index:0;font-weight:700;line-height:1;letter-spacing:-0.04em;opacity:0.13;
19
- pointer-events:none;user-select:none}
19
+ pointer-events:none;user-select:none;font-variant-numeric:tabular-nums;
20
+ white-space:nowrap;text-align:right}
20
21
  #axes{position:absolute;inset:0;pointer-events:none;z-index:2}
21
22
  #grid{position:absolute;inset:0;pointer-events:none;z-index:0}
22
23
  #legend{position:absolute;right:10px;top:36px;z-index:4;padding:6px 9px;
@@ -1214,6 +1215,27 @@ function fmtParam(v) {
1214
1215
  if (a >= 1e6 || (a > 0 && a < 1e-3)) return v.toExponential(2);
1215
1216
  return v.toFixed(2);
1216
1217
  }
1218
+ // The big frame label must not move while it changes: pick one font size
1219
+ // for the whole animation and a box as wide as its widest label (in em, so
1220
+ // it scales with the window); digits are tabular and right-aligned in it.
1221
+ function steadyFrameLabel(el, nF) {
1222
+ const count = Math.min(Math.max(nF, 1), 240);
1223
+ const labels = new Set();
1224
+ for (let i = 0; i < count; i++) {
1225
+ labels.add(frameText(count <= 1 ? 0 : i * (nF - 1) / (count - 1)));
1226
+ }
1227
+ let longest = 0;
1228
+ for (const lab of labels) longest = Math.max(longest, lab.length);
1229
+ el.style.fontSize = longest > 8 ? '56px' : 'min(22vw, 148px)';
1230
+ el.style.width = 'auto';
1231
+ const size = parseFloat(getComputedStyle(el).fontSize) || 1;
1232
+ let widest = 0;
1233
+ for (const lab of labels) {
1234
+ el.textContent = lab;
1235
+ widest = Math.max(widest, el.getBoundingClientRect().width);
1236
+ }
1237
+ el.style.width = (widest / size + 0.05).toFixed(3) + 'em';
1238
+ }
1217
1239
  function frameText(t) {
1218
1240
  const tr = S.transition;
1219
1241
  if (tr.params && tr.params.length) {
@@ -1349,6 +1371,7 @@ function installPlayer(renderFrame) {
1349
1371
  btn.style.border = '1px solid ' + T.axis;
1350
1372
  yearEl.style.display = 'block';
1351
1373
  yearEl.style.color = T.ink;
1374
+ steadyFrameLabel(yearEl, nF);
1352
1375
  const titleTemplate = (S.labs && S.labs.title) || '';
1353
1376
  let shownLab = null;
1354
1377
  function syncChrome() {
@@ -1356,7 +1379,6 @@ function installPlayer(renderFrame) {
1356
1379
  frameLabel = lab;
1357
1380
  readout.textContent = lab;
1358
1381
  yearEl.textContent = lab;
1359
- yearEl.style.fontSize = lab.length > 8 ? '56px' : 'min(22vw, 148px)';
1360
1382
  range.value = (nF <= 1) ? '1000' : String(Math.round(playT / (nF - 1) * 1000));
1361
1383
  btn.textContent = playing ? 'Pause' : 'Play';
1362
1384
  btn.setAttribute('aria-label', playing ? 'Pause' : 'Play');
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plot3
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: ggplot2 grammar of graphics for Python: interactive WebGL figures and journal-ready PNG, SVG, and PDF
5
5
  Author: Rigoberto Leyva Salmeron
6
6
  License-Expression: MIT
@@ -639,3 +639,35 @@ def test_slider_refuses_a_grid_that_is_too_large():
639
639
  + geom_function("z = a sin(x) cos(y)")
640
640
  + slider(a=(0, 1), k=(1, 2), steps=25)
641
641
  )
642
+
643
+
644
+ def test_frame_label_stays_in_place_while_it_changes():
645
+ """The big frame label uses one size, a fixed box, and tabular digits."""
646
+ from plot3 import geom_function, ggplot, transition_time
647
+
648
+ doc = (
649
+ ggplot() + geom_function("y = sin(x - t)") + transition_time(t=(-5, 12))
650
+ ).html()
651
+ assert "font-variant-numeric:tabular-nums" in doc.split("#year{", 1)[1].split("}", 1)[0]
652
+ assert "steadyFrameLabel(yearEl, nF)" in doc
653
+ # The size is chosen once per animation, not per frame.
654
+ assert "lab.length > 8" not in doc
655
+
656
+
657
+ def test_swept_parameter_is_not_frozen_into_labels():
658
+ """transition_time(t=...) must not label curves with the first frame's t."""
659
+ import json
660
+
661
+ from plot3 import geom_function, ggplot, transition_time
662
+ from plot3.build import build_spec
663
+
664
+ spec = json.dumps(
665
+ build_spec(
666
+ ggplot()
667
+ + geom_function("y = sin(x - t)")
668
+ + geom_function("y = a cos(x - t)", a=2)
669
+ + transition_time(t=(0, 6.28))
670
+ )
671
+ )
672
+ assert "t = 0" not in spec
673
+ assert "(a = 2)" in spec # fixed coefficients still show their value
@@ -1 +0,0 @@
1
- __version__ = "0.4.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes