plot3 0.6.6__tar.gz → 0.6.7__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.
- {plot3-0.6.6 → plot3-0.6.7}/PKG-INFO +1 -1
- plot3-0.6.7/plot3/__version__.py +1 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/build.py +32 -21
- {plot3-0.6.6 → plot3-0.6.7}/plot3/function.py +10 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/geoms.py +8 -5
- {plot3-0.6.6 → plot3-0.6.7}/plot3/ggplot.py +29 -2
- {plot3-0.6.6 → plot3-0.6.7}/plot3.egg-info/PKG-INFO +1 -1
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_animation.py +30 -0
- plot3-0.6.6/plot3/__version__.py +0 -1
- {plot3-0.6.6 → plot3-0.6.7}/LICENSE +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/README.md +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/__init__.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/aesexpr.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/calculus.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/compose.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/contour.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/craft.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/encode.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/expr.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/flip.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/io.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/jupyter.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/latexin.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/masking.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/mathtext.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/payload.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/remote.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/scales.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/scaling.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/special.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/stat2d.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/static.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/stats3d.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/table.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/themes.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3/viewer.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3.egg-info/SOURCES.txt +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3.egg-info/dependency_links.txt +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3.egg-info/requires.txt +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/plot3.egg-info/top_level.txt +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/pyproject.toml +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/setup.cfg +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_2d_density.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_3d.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_annotate.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_api_public.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_array_backend.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_arrow.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_build_contract.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_contour.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_everyday_ggplot2.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_expr.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_facets_compose.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_geom_function.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_geoms_ds.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_ggplot2_extras2.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_ggplot2_parity.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_ggplot_extras.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_grammar.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_latex_input.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_lidar_scene.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_masking.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_math_layers.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_mathtext.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_payload.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_payload_display.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_point_cloud.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_r_oracle_parity.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_readme.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_remote_bridge.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_review_fixes.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_save_menu.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_scales_api.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_scales_themes.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_special.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_stat2d.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_static_save.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_stats_semantic.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_step3_geoms.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_table_backends.py +0 -0
- {plot3-0.6.6 → plot3-0.6.7}/tests/test_titles_theme.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.7"
|
|
@@ -1970,6 +1970,9 @@ def build_spec(g: ggplot) -> tuple[dict, list[tuple[str, str]]]:
|
|
|
1970
1970
|
"slider() cannot be combined with transition_time() "
|
|
1971
1971
|
"or transition_states()"
|
|
1972
1972
|
)
|
|
1973
|
+
if slider is not None:
|
|
1974
|
+
# Default steps shrink to fit while layers expand; keep that per render.
|
|
1975
|
+
slider = copy.copy(slider)
|
|
1973
1976
|
domains = None
|
|
1974
1977
|
if any(getattr(layer, "kind", None) == "function" for layer in layers_in):
|
|
1975
1978
|
from plot3.function import data_domains
|
|
@@ -1978,27 +1981,35 @@ def build_spec(g: ggplot) -> tuple[dict, list[tuple[str, str]]]:
|
|
|
1978
1981
|
addon_map: dict[int, list] = {}
|
|
1979
1982
|
for index, addon in getattr(g, "_addons", None) or []:
|
|
1980
1983
|
addon_map.setdefault(int(index), []).append(addon)
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
1984
|
+
def expand_all() -> list:
|
|
1985
|
+
out = []
|
|
1986
|
+
for index, geom in enumerate(layers_in):
|
|
1987
|
+
layer_data = getattr(geom, "layer_data", None)
|
|
1988
|
+
if getattr(geom, "kind", None) == "box3d":
|
|
1989
|
+
# Box classes take the figure theme's group colours.
|
|
1990
|
+
geom = copy.copy(geom)
|
|
1991
|
+
geom._palette = list(theme["cat"])
|
|
1992
|
+
result = expand_stat_geom(
|
|
1993
|
+
geom,
|
|
1994
|
+
g.mapping,
|
|
1995
|
+
data if layer_data is None else layer_data,
|
|
1996
|
+
domains,
|
|
1997
|
+
transition,
|
|
1998
|
+
slider,
|
|
1999
|
+
coord,
|
|
2000
|
+
addon_map.get(index),
|
|
2001
|
+
)
|
|
2002
|
+
if isinstance(result, list):
|
|
2003
|
+
out.extend(result)
|
|
2004
|
+
else:
|
|
2005
|
+
out.append(result)
|
|
2006
|
+
return out
|
|
2007
|
+
|
|
2008
|
+
steps_before = getattr(slider, "steps", None)
|
|
2009
|
+
expanded = expand_all()
|
|
2010
|
+
if getattr(slider, "steps", None) != steps_before:
|
|
2011
|
+
# A later layer lowered the default steps: every layer shares one grid.
|
|
2012
|
+
expanded = expand_all()
|
|
2002
2013
|
from plot3.calculus import mark_intersections
|
|
2003
2014
|
|
|
2004
2015
|
expanded = mark_intersections(expanded)
|
|
@@ -938,6 +938,16 @@ def _guard_slider(source: Any, n_samples: int, what: str) -> None:
|
|
|
938
938
|
cells = int(n_samples) * n_frames
|
|
939
939
|
if cells <= _SLIDER_CELL_CAP:
|
|
940
940
|
return
|
|
941
|
+
if getattr(source, "auto_steps", False):
|
|
942
|
+
# Default steps: take the largest count that fits. build_spec hands
|
|
943
|
+
# each render its own copy and re-expands if this lowered it.
|
|
944
|
+
count = len(source.ranges)
|
|
945
|
+
steps = int(source.steps)
|
|
946
|
+
while steps > 2 and int(n_samples) * steps**count > _SLIDER_CELL_CAP:
|
|
947
|
+
steps -= 1
|
|
948
|
+
if int(n_samples) * steps**count <= _SLIDER_CELL_CAP:
|
|
949
|
+
source.steps = steps
|
|
950
|
+
return
|
|
941
951
|
raise ValueError(
|
|
942
952
|
f"slider() would sample {n_frames} frames of {n_samples} {what} "
|
|
943
953
|
f"({cells} values). Pass a smaller steps= or n=."
|
|
@@ -2181,20 +2181,23 @@ class slider:
|
|
|
2181
2181
|
|
|
2182
2182
|
ggplot() + geom_function("y = a sin(k x)") + slider(a=(0, 3), k=(1, 5))
|
|
2183
2183
|
|
|
2184
|
-
``steps`` defaults to 25 per parameter
|
|
2185
|
-
|
|
2186
|
-
``steps``
|
|
2184
|
+
``steps`` defaults to 25 per parameter, lowered as far as needed for
|
|
2185
|
+
the whole grid to fit (three parameters on a curve get 9). An explicit
|
|
2186
|
+
``steps`` is kept as given and refused if it is too large; pass a
|
|
2187
|
+
smaller ``steps`` or ``n``. Do not combine with ``transition_time()``.
|
|
2187
2188
|
"""
|
|
2188
2189
|
|
|
2189
2190
|
kind = "slider"
|
|
2190
2191
|
|
|
2191
|
-
def __init__(self, *, steps: int =
|
|
2192
|
+
def __init__(self, *, steps: int | None = None, **ranges):
|
|
2192
2193
|
if not ranges:
|
|
2193
2194
|
raise TypeError(
|
|
2194
2195
|
"slider() needs a parameter range, for example slider(a=(0, 3))"
|
|
2195
2196
|
)
|
|
2197
|
+
# Only the default shrinks to fit; an explicit count is the user's.
|
|
2198
|
+
self.auto_steps = steps is None
|
|
2196
2199
|
try:
|
|
2197
|
-
nsteps = int(steps)
|
|
2200
|
+
nsteps = 25 if steps is None else int(steps)
|
|
2198
2201
|
except (TypeError, ValueError):
|
|
2199
2202
|
raise TypeError(
|
|
2200
2203
|
"slider() steps= must be an integer, for example steps=25"
|
|
@@ -42,6 +42,25 @@ from plot3.table import as_table, detect_backend
|
|
|
42
42
|
from plot3.themes import _THEMES
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def _error_text(exc: BaseException) -> str:
|
|
46
|
+
return f"plot3 could not draw this figure.\n{type(exc).__name__}: {exc}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _error_html(exc: BaseException) -> str:
|
|
50
|
+
import traceback
|
|
51
|
+
|
|
52
|
+
trace = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
|
53
|
+
return (
|
|
54
|
+
'<div class="plot3-error" style="border-left:4px solid #d9534f;'
|
|
55
|
+
"padding:8px 12px;font-family:ui-monospace,Menlo,monospace;"
|
|
56
|
+
'font-size:13px;white-space:pre-wrap">'
|
|
57
|
+
"<strong>plot3 could not draw this figure.</strong>\n"
|
|
58
|
+
f"{_htmlesc.escape(type(exc).__name__)}: {_htmlesc.escape(str(exc))}"
|
|
59
|
+
'<details style="margin-top:6px"><summary>Traceback</summary>'
|
|
60
|
+
f"{_htmlesc.escape(trace)}</details></div>"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
45
64
|
def _env_flag(name: str) -> bool | None:
|
|
46
65
|
raw = os.environ.get(name)
|
|
47
66
|
if raw is None or raw == "":
|
|
@@ -422,7 +441,10 @@ class ggplot:
|
|
|
422
441
|
# Used by hosts that only understand HTML reprs (and by tests).
|
|
423
442
|
# Prefer ``display(fig)`` / ``fig.show()`` so VS Code can open a browser.
|
|
424
443
|
self._maybe_hide_from_ai()
|
|
425
|
-
|
|
444
|
+
try:
|
|
445
|
+
return self._iframe()
|
|
446
|
+
except Exception as exc:
|
|
447
|
+
return _error_html(exc)
|
|
426
448
|
|
|
427
449
|
def _repr_mimebundle_(self, include=None, exclude=None):
|
|
428
450
|
"""HTML plus optional PlotPayload mime for CRAFT host handoff.
|
|
@@ -435,7 +457,12 @@ class ggplot:
|
|
|
435
457
|
from plot3.remote import MIME_PLOT3
|
|
436
458
|
|
|
437
459
|
self._maybe_hide_from_ai()
|
|
438
|
-
|
|
460
|
+
try:
|
|
461
|
+
bundle: dict = {"text/html": self._iframe()}
|
|
462
|
+
except Exception as exc:
|
|
463
|
+
# Notebook hosts swallow repr errors and fall back to the bare
|
|
464
|
+
# object repr, so the reason a plot is missing must be the output.
|
|
465
|
+
return {"text/html": _error_html(exc), "text/plain": _error_text(exc)}
|
|
439
466
|
try:
|
|
440
467
|
if self.facet is None and (
|
|
441
468
|
self._payload is not None or self.data is not None or self.layers
|
|
@@ -671,3 +671,33 @@ def test_swept_parameter_is_not_frozen_into_labels():
|
|
|
671
671
|
)
|
|
672
672
|
assert "t = 0" not in spec
|
|
673
673
|
assert "(a = 2)" in spec # fixed coefficients still show their value
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def test_default_slider_steps_shrink_to_fit():
|
|
677
|
+
from plot3 import geom_function
|
|
678
|
+
|
|
679
|
+
g = (
|
|
680
|
+
ggplot()
|
|
681
|
+
+ geom_function("y = a*(x-h)^2 + k", xlim=(-5, 5))
|
|
682
|
+
+ slider(a=(0.2, 3), h=(-3, 3), k=(-3, 3))
|
|
683
|
+
)
|
|
684
|
+
spec, _ = build_spec(g)
|
|
685
|
+
assert [p["n"] for p in spec["slider"]["params"]] == [9, 9, 9]
|
|
686
|
+
assert spec["slider"]["nFrames"] == 729
|
|
687
|
+
# The figure's own slider keeps its default for later renders.
|
|
688
|
+
assert g.slider.steps == 25
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def test_render_error_is_shown_instead_of_the_bare_repr():
|
|
692
|
+
from plot3 import geom_function
|
|
693
|
+
|
|
694
|
+
g = (
|
|
695
|
+
ggplot()
|
|
696
|
+
+ geom_function("y = a x + k")
|
|
697
|
+
+ slider(a=(0, 1), k=(0, 1), steps=2000)
|
|
698
|
+
)
|
|
699
|
+
bundle = g._repr_mimebundle_()
|
|
700
|
+
assert "plot3-error" in bundle["text/html"]
|
|
701
|
+
assert "smaller steps" in bundle["text/html"]
|
|
702
|
+
assert "ValueError" in bundle["text/plain"]
|
|
703
|
+
assert "smaller steps" in g._repr_html_()
|
plot3-0.6.6/plot3/__version__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.6.6"
|
|
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
|
|
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
|