plot3 0.6.4__tar.gz → 0.6.5__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.4 → plot3-0.6.5}/PKG-INFO +5 -1
- {plot3-0.6.4 → plot3-0.6.5}/README.md +4 -0
- plot3-0.6.5/plot3/__version__.py +1 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/viewer.py +106 -8
- {plot3-0.6.4 → plot3-0.6.5}/plot3.egg-info/PKG-INFO +5 -1
- plot3-0.6.4/plot3/__version__.py +0 -1
- {plot3-0.6.4 → plot3-0.6.5}/LICENSE +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/__init__.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/aesexpr.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/build.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/calculus.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/compose.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/contour.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/craft.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/encode.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/expr.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/flip.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/function.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/geoms.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/ggplot.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/io.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/jupyter.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/latexin.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/masking.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/mathtext.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/payload.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/remote.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/scales.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/scaling.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/special.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/stat2d.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/static.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/stats3d.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/table.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3/themes.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3.egg-info/SOURCES.txt +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3.egg-info/dependency_links.txt +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3.egg-info/requires.txt +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/plot3.egg-info/top_level.txt +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/pyproject.toml +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/setup.cfg +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_2d_density.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_3d.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_animation.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_annotate.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_api_public.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_array_backend.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_arrow.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_build_contract.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_contour.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_everyday_ggplot2.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_expr.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_facets_compose.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_geom_function.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_geoms_ds.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_ggplot2_extras2.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_ggplot2_parity.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_ggplot_extras.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_grammar.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_latex_input.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_lidar_scene.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_masking.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_math_layers.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_mathtext.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_payload.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_payload_display.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_point_cloud.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_r_oracle_parity.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_readme.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_remote_bridge.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_review_fixes.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_save_menu.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_scales_api.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_scales_themes.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_special.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_stat2d.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_static_save.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_stats_semantic.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_step3_geoms.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_table_backends.py +0 -0
- {plot3-0.6.4 → plot3-0.6.5}/tests/test_titles_theme.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plot3
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
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
|
|
@@ -130,6 +130,10 @@ gapminder = pd.read_csv("https://raw.githubusercontent.com/kirenz/datasets/maste
|
|
|
130
130
|
`{frame_time}` in the title shows the current year as it plays.
|
|
131
131
|
`transition_states("phase")` steps through categories instead of time.
|
|
132
132
|
|
|
133
|
+
Click a bubble to trace it: its path through every frame appears, with
|
|
134
|
+
the years along it and its name (from `group=`) riding beside it as it
|
|
135
|
+
plays. Click it again, or empty space, to clear it. This works in 3D too.
|
|
136
|
+
|
|
133
137
|
<details>
|
|
134
138
|
<summary>Run this without downloading anything (made-up gapminder-shaped data)</summary>
|
|
135
139
|
|
|
@@ -103,6 +103,10 @@ gapminder = pd.read_csv("https://raw.githubusercontent.com/kirenz/datasets/maste
|
|
|
103
103
|
`{frame_time}` in the title shows the current year as it plays.
|
|
104
104
|
`transition_states("phase")` steps through categories instead of time.
|
|
105
105
|
|
|
106
|
+
Click a bubble to trace it: its path through every frame appears, with
|
|
107
|
+
the years along it and its name (from `group=`) riding beside it as it
|
|
108
|
+
plays. Click it again, or empty space, to clear it. This works in 3D too.
|
|
109
|
+
|
|
106
110
|
<details>
|
|
107
111
|
<summary>Run this without downloading anything (made-up gapminder-shaped data)</summary>
|
|
108
112
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.5"
|
|
@@ -702,6 +702,26 @@ const stepEntries = [];
|
|
|
702
702
|
let hoverHold = false;
|
|
703
703
|
let trail = null;
|
|
704
704
|
let trailId = null;
|
|
705
|
+
// The clicked bubble's path: points with their frame, its name and colour.
|
|
706
|
+
let trailInfo = null;
|
|
707
|
+
// A click toggles a bubble's path only on the bubble itself (a little
|
|
708
|
+
// slack for tiny dots), so a stray click near one draws nothing.
|
|
709
|
+
function clickRadius(L, i, minimum) {
|
|
710
|
+
return L._size ? Math.max(minimum, L._size[i] * 0.55) : minimum;
|
|
711
|
+
}
|
|
712
|
+
function trailKey(L, i) { return String(i) + '|' + (L.ids ? L.ids[i] : ''); }
|
|
713
|
+
function trailHint(L, i) {
|
|
714
|
+
return '<br><span style="opacity:.6;font-size:10px">click to ' +
|
|
715
|
+
(trailId === trailKey(L, i) ? 'hide' : 'show') + ' its path</span>';
|
|
716
|
+
}
|
|
717
|
+
// The frame's value (the year) for a point on the path, or '' if unknown.
|
|
718
|
+
function trailYear(f) {
|
|
719
|
+
const tr = S.transition;
|
|
720
|
+
if (!tr || !tr.times || !trailInfo || tr.times.length !== trailInfo.nF) return '';
|
|
721
|
+
const v = tr.times[f];
|
|
722
|
+
if (tr.integer) return String(Math.round(+v));
|
|
723
|
+
return typeof v === 'number' ? fmt(v) : String(v);
|
|
724
|
+
}
|
|
705
725
|
function framePresent(F, i) {
|
|
706
726
|
if (!F.x || !F.x.maskData || !F.x.maskData[i]) return false;
|
|
707
727
|
if (!F.y || !F.y.maskData || !F.y.maskData[i]) return false;
|
|
@@ -1167,6 +1187,7 @@ function clearTrail() {
|
|
|
1167
1187
|
trail = null;
|
|
1168
1188
|
}
|
|
1169
1189
|
trailId = null;
|
|
1190
|
+
trailInfo = null;
|
|
1170
1191
|
}
|
|
1171
1192
|
function setTrail(L, obj) {
|
|
1172
1193
|
clearTrail();
|
|
@@ -1208,7 +1229,18 @@ function setTrail(L, obj) {
|
|
|
1208
1229
|
}
|
|
1209
1230
|
scene.add(group);
|
|
1210
1231
|
trail = group;
|
|
1211
|
-
trailId =
|
|
1232
|
+
trailId = trailKey(L, obj);
|
|
1233
|
+
const pts = [];
|
|
1234
|
+
for (let f = 0; f < nF; f++) {
|
|
1235
|
+
const i = obj * nF + f;
|
|
1236
|
+
if (!framePresent(F, i)) continue;
|
|
1237
|
+
pts.push({ x: F.x.data[i], y: F.y.data[i], z: F.z ? F.z.data[i] : 0, f: f });
|
|
1238
|
+
}
|
|
1239
|
+
trailInfo = {
|
|
1240
|
+
L: L, obj: obj, nF: nF, ext: ext, pts: pts,
|
|
1241
|
+
name: L.ids && L.ids[obj] != null ? String(L.ids[obj]) : '',
|
|
1242
|
+
color: 'rgb(' + rgb.map(c => Math.round(c * 255)).join(',') + ')',
|
|
1243
|
+
};
|
|
1212
1244
|
}
|
|
1213
1245
|
let frameLabel = '';
|
|
1214
1246
|
const axisTitleSprites = [];
|
|
@@ -2038,10 +2070,48 @@ if (!S.is3d) {
|
|
|
2038
2070
|
const AY = Math.min(Math.max(Y, M.t + 12), M.t + H - 10);
|
|
2039
2071
|
s += '<text x="' + AX + '" y="' + AY + '" fill="' + T.ink + '" text-anchor="middle" dominant-baseline="middle" font-size="12" font-weight="600" paint-order="stroke" stroke="' + T.surface + '" stroke-width="4" stroke-linejoin="round">' + plot3Esc(ann.text) + '</text>';
|
|
2040
2072
|
}
|
|
2073
|
+
if (trailInfo) s += trailLabels2D();
|
|
2041
2074
|
svg.innerHTML = s;
|
|
2042
2075
|
if (gridSvg) gridSvg.innerHTML = grid;
|
|
2043
2076
|
}
|
|
2044
2077
|
|
|
2078
|
+
// The clicked bubble's name rides beside it; years mark its path,
|
|
2079
|
+
// spaced so they do not overlap (the first and last always show).
|
|
2080
|
+
function trailLabels2D() {
|
|
2081
|
+
const t = trailInfo, ext = t.ext;
|
|
2082
|
+
const inside = (X, Y) => X >= M.l && X <= M.l + W && Y >= M.t && Y <= M.t + H;
|
|
2083
|
+
const halo = ' paint-order="stroke" stroke="' + T.surface + '" stroke-width="3" stroke-linejoin="round"';
|
|
2084
|
+
// The name sits right of the bubble's current position; years keep clear.
|
|
2085
|
+
const L = t.L, i = t.obj;
|
|
2086
|
+
let nameBox = null, nameSvg = '';
|
|
2087
|
+
if (t.name && L._x && L._y) {
|
|
2088
|
+
const X = M.l + screenX(L._x[i] * ext[0]), Y = M.t + screenY(L._y[i] * ext[1]);
|
|
2089
|
+
if (inside(X, Y)) {
|
|
2090
|
+
const x0 = X + (L._size ? L._size[i] * 0.5 : 4) + 4;
|
|
2091
|
+
nameBox = [x0 - 4, Y - 9, x0 + 7.2 * t.name.length + 4, Y + 9];
|
|
2092
|
+
nameSvg = '<text x="' + x0 + '" y="' + Y + '" fill="' + t.color +
|
|
2093
|
+
'" font-size="12" font-weight="700" dominant-baseline="middle"' + halo + '>' +
|
|
2094
|
+
plot3Esc(t.name) + '</text>';
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
const underName = (X, Y) => nameBox && X + 5 + 26 > nameBox[0] && X + 5 < nameBox[2] &&
|
|
2098
|
+
Y - 5 > nameBox[1] - 6 && Y - 5 - 10 < nameBox[3];
|
|
2099
|
+
const kept = [];
|
|
2100
|
+
t.pts.forEach((p, k) => {
|
|
2101
|
+
const X = M.l + screenX(p.x * ext[0]), Y = M.t + screenY(p.y * ext[1]);
|
|
2102
|
+
const label = trailYear(p.f);
|
|
2103
|
+
if (!label || !inside(X, Y) || underName(X, Y)) return;
|
|
2104
|
+
const prev = kept[kept.length - 1];
|
|
2105
|
+
const near = prev && Math.hypot(X - prev.X, Y - prev.Y) < 34;
|
|
2106
|
+
if (near && k !== t.pts.length - 1) return;
|
|
2107
|
+
if (near && kept.length > 1) kept.pop(); // the last year wins a crowded end
|
|
2108
|
+
else if (near) return;
|
|
2109
|
+
kept.push({ X, Y, label });
|
|
2110
|
+
});
|
|
2111
|
+
return kept.map(k => '<text x="' + (k.X + 5) + '" y="' + (k.Y - 5) + '" fill="' + T.muted +
|
|
2112
|
+
'" font-size="10"' + halo + '>' + plot3Esc(k.label) + '</text>').join('') + nameSvg;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2045
2115
|
function layout() {
|
|
2046
2116
|
placeLegend();
|
|
2047
2117
|
W = Math.max(50, figEl.clientWidth - M.l - M.r);
|
|
@@ -2283,7 +2353,9 @@ if (!S.is3d) {
|
|
|
2283
2353
|
: head
|
|
2284
2354
|
+ fmtSpan('x', xv, xSpan) + ', '
|
|
2285
2355
|
+ fmtSpan('y', yv, ySpan) + sizeBit;
|
|
2286
|
-
|
|
2356
|
+
const moving = L.kind === 'point' && L.frames && S.transition;
|
|
2357
|
+
const cr = clickRadius(L, i, 6);
|
|
2358
|
+
consider(d, sx, sy, html + (moving && d <= cr * cr ? trailHint(L, i) : ''), !!moving);
|
|
2287
2359
|
}
|
|
2288
2360
|
}
|
|
2289
2361
|
}
|
|
@@ -2311,14 +2383,14 @@ if (!S.is3d) {
|
|
|
2311
2383
|
for (let i = 0; i < L.n; i++) {
|
|
2312
2384
|
if (L._alpha && L._alpha[i] <= 0.04) continue;
|
|
2313
2385
|
const sx = screenX(L._x[i]), sy = screenY(L._y[i]);
|
|
2314
|
-
const rad = L
|
|
2386
|
+
const rad = clickRadius(L, i, 6);
|
|
2315
2387
|
const d = (sx - mx) * (sx - mx) + (sy - my) * (sy - my);
|
|
2316
2388
|
if (d <= rad * rad && d < hitD) { hitD = d; hit = { L, i }; }
|
|
2317
2389
|
}
|
|
2318
2390
|
}
|
|
2391
|
+
tip.style.display = 'none';
|
|
2319
2392
|
if (!hit) { clearTrail(); draw(); return; }
|
|
2320
|
-
|
|
2321
|
-
if (trailId === key) { clearTrail(); draw(); return; }
|
|
2393
|
+
if (trailId === trailKey(hit.L, hit.i)) { clearTrail(); draw(); return; }
|
|
2322
2394
|
setTrail(hit.L, hit.i);
|
|
2323
2395
|
draw();
|
|
2324
2396
|
});
|
|
@@ -2660,6 +2732,7 @@ if (!S.is3d) {
|
|
|
2660
2732
|
}
|
|
2661
2733
|
}
|
|
2662
2734
|
hoverHold = !!(best && best[0].kind === 'point' && best[0].frames && S.transition);
|
|
2735
|
+
const hoverD = bestD;
|
|
2663
2736
|
if (!best) { tip3.style.display = 'none'; return; }
|
|
2664
2737
|
const [L, i, sx, sy, xn, yn, zn] = best;
|
|
2665
2738
|
const xv = fromScale('x', xn);
|
|
@@ -2687,6 +2760,10 @@ if (!S.is3d) {
|
|
|
2687
2760
|
+ axisPair('y', fmt(yv)) + ', '
|
|
2688
2761
|
+ axisPair('z', fmt(zv)) + sizeBit
|
|
2689
2762
|
: head + fmt(xv) + ', ' + fmt(yv) + ', ' + fmt(zv) + sizeBit;
|
|
2763
|
+
if (hoverHold) {
|
|
2764
|
+
const cr = sizeAtten ? 8 : clickRadius(L, i, 8);
|
|
2765
|
+
if (hoverD <= cr * cr) tip3.innerHTML += trailHint(L, i);
|
|
2766
|
+
}
|
|
2690
2767
|
plot3Typeset(tip3);
|
|
2691
2768
|
tip3.style.left = Math.min(w - 8, sx + 12) + 'px';
|
|
2692
2769
|
tip3.style.top = Math.max(8, sy - 10) + 'px';
|
|
@@ -2737,15 +2814,36 @@ if (!S.is3d) {
|
|
|
2737
2814
|
v.project(cam);
|
|
2738
2815
|
const sx = (v.x * 0.5 + 0.5) * w;
|
|
2739
2816
|
const sy = (-v.y * 0.5 + 0.5) * h;
|
|
2740
|
-
const rad =
|
|
2817
|
+
const rad = sizeAtten ? 8 : clickRadius(L, i, 8);
|
|
2741
2818
|
const d = (sx - mx) * (sx - mx) + (sy - my) * (sy - my);
|
|
2742
2819
|
if (d <= rad * rad && d < hitD) { hitD = d; hit = { L, i }; }
|
|
2743
2820
|
}
|
|
2744
2821
|
}
|
|
2822
|
+
tip3.style.display = 'none';
|
|
2745
2823
|
if (!hit) { clearTrail(); return; }
|
|
2746
|
-
|
|
2747
|
-
if (trailId === key) { clearTrail(); return; }
|
|
2824
|
+
if (trailId === trailKey(hit.L, hit.i)) { clearTrail(); return; }
|
|
2748
2825
|
setTrail(hit.L, hit.i);
|
|
2826
|
+
// The scene cannot move text every frame cheaply: label the path's
|
|
2827
|
+
// ends, the first year, and the name with the last year.
|
|
2828
|
+
if (trail && trailInfo && trailInfo.pts.length) {
|
|
2829
|
+
const t = trailInfo;
|
|
2830
|
+
const at = p => new THREE.Vector3(p.x * t.ext[0], p.y * t.ext[1], p.z * t.ext[2]);
|
|
2831
|
+
const first = t.pts[0], last = t.pts[t.pts.length - 1];
|
|
2832
|
+
const startText = trailYear(first.f);
|
|
2833
|
+
if (startText) {
|
|
2834
|
+
const sp = sprite(startText, true);
|
|
2835
|
+
sp.center.set(-0.1, -0.2);
|
|
2836
|
+
sp.position.copy(at(first));
|
|
2837
|
+
trail.add(sp);
|
|
2838
|
+
}
|
|
2839
|
+
const endText = [t.name, trailYear(last.f)].filter(Boolean).join(' ');
|
|
2840
|
+
if (endText) {
|
|
2841
|
+
const sp = sprite(endText, false);
|
|
2842
|
+
sp.center.set(-0.1, -0.2);
|
|
2843
|
+
sp.position.copy(at(last));
|
|
2844
|
+
trail.add(sp);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2749
2847
|
});
|
|
2750
2848
|
|
|
2751
2849
|
function layout() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plot3
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
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
|
|
@@ -130,6 +130,10 @@ gapminder = pd.read_csv("https://raw.githubusercontent.com/kirenz/datasets/maste
|
|
|
130
130
|
`{frame_time}` in the title shows the current year as it plays.
|
|
131
131
|
`transition_states("phase")` steps through categories instead of time.
|
|
132
132
|
|
|
133
|
+
Click a bubble to trace it: its path through every frame appears, with
|
|
134
|
+
the years along it and its name (from `group=`) riding beside it as it
|
|
135
|
+
plays. Click it again, or empty space, to clear it. This works in 3D too.
|
|
136
|
+
|
|
133
137
|
<details>
|
|
134
138
|
<summary>Run this without downloading anything (made-up gapminder-shaped data)</summary>
|
|
135
139
|
|
plot3-0.6.4/plot3/__version__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.6.4"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|