pytycho 0.2.0__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.
- pytycho-0.2.0/.claude-plugin/marketplace.json +18 -0
- pytycho-0.2.0/.claude-plugin/plugin.json +12 -0
- pytycho-0.2.0/.github/check_versions.py +46 -0
- pytycho-0.2.0/.github/dependabot.yml +42 -0
- pytycho-0.2.0/.github/release.yml +5 -0
- pytycho-0.2.0/.github/workflows/cd.yml +152 -0
- pytycho-0.2.0/.github/workflows/ci.yml +159 -0
- pytycho-0.2.0/.gitignore +37 -0
- pytycho-0.2.0/.pre-commit-config.yaml +84 -0
- pytycho-0.2.0/.readthedocs.yaml +17 -0
- pytycho-0.2.0/AGENTS.md +31 -0
- pytycho-0.2.0/AUTHORS +7 -0
- pytycho-0.2.0/CHANGELOG.md +31 -0
- pytycho-0.2.0/CONTRIBUTING.md +60 -0
- pytycho-0.2.0/LICENSE +202 -0
- pytycho-0.2.0/NOTICE +14 -0
- pytycho-0.2.0/PKG-INFO +161 -0
- pytycho-0.2.0/README.md +125 -0
- pytycho-0.2.0/SECURITY.md +5 -0
- pytycho-0.2.0/THIRD_PARTY_NOTICES.md +68 -0
- pytycho-0.2.0/docs/README.md +52 -0
- pytycho-0.2.0/docs/_static/tycho.css +6 -0
- pytycho-0.2.0/docs/agents.md +34 -0
- pytycho-0.2.0/docs/api/index.md +8 -0
- pytycho-0.2.0/docs/api/javascript.md +190 -0
- pytycho-0.2.0/docs/api/python.md +42 -0
- pytycho-0.2.0/docs/changelog.md +5 -0
- pytycho-0.2.0/docs/conf.py +181 -0
- pytycho-0.2.0/docs/conformance.md +4 -0
- pytycho-0.2.0/docs/gallery.md +42 -0
- pytycho-0.2.0/docs/guide/bodies.md +34 -0
- pytycho-0.2.0/docs/guide/clock.md +57 -0
- pytycho-0.2.0/docs/guide/frames.md +73 -0
- pytycho-0.2.0/docs/guide/html.md +49 -0
- pytycho-0.2.0/docs/guide/index.md +51 -0
- pytycho-0.2.0/docs/guide/panels.md +113 -0
- pytycho-0.2.0/docs/guide/thinning.md +26 -0
- pytycho-0.2.0/docs/guide/track-sets.md +85 -0
- pytycho-0.2.0/docs/guide/variables.md +39 -0
- pytycho-0.2.0/docs/guide/views.md +118 -0
- pytycho-0.2.0/docs/index.md +84 -0
- pytycho-0.2.0/docs/quickstart.md +95 -0
- pytycho-0.2.0/docs/requirements.txt +10 -0
- pytycho-0.2.0/docs/schema.md +15 -0
- pytycho-0.2.0/docs/spec.md +9 -0
- pytycho-0.2.0/examples/broucke/build.py +244 -0
- pytycho-0.2.0/hatch_build.py +19 -0
- pytycho-0.2.0/noxfile.py +68 -0
- pytycho-0.2.0/pyproject.toml +136 -0
- pytycho-0.2.0/skills/tycho/SKILL.md +78 -0
- pytycho-0.2.0/spec/conformance/README.md +65 -0
- pytycho-0.2.0/spec/conformance/broucke-mini/cases.json +16114 -0
- pytycho-0.2.0/spec/conformance/broucke-mini/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/broucke-mini/scene.tys +326 -0
- pytycho-0.2.0/spec/conformance/containers/encoded.tyd +0 -0
- pytycho-0.2.0/spec/conformance/containers/raw.tyd +0 -0
- pytycho-0.2.0/spec/conformance/edge-fraction/cases.json +195 -0
- pytycho-0.2.0/spec/conformance/edge-fraction/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/edge-fraction/rejections.json +446 -0
- pytycho-0.2.0/spec/conformance/edge-fraction/scene.tys +206 -0
- pytycho-0.2.0/spec/conformance/edge-value/cases.json +3428 -0
- pytycho-0.2.0/spec/conformance/edge-value/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/edge-value/scene.tys +199 -0
- pytycho-0.2.0/spec/conformance/generate.py +1283 -0
- pytycho-0.2.0/spec/conformance/laps/cases.json +44164 -0
- pytycho-0.2.0/spec/conformance/laps/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/laps/rejections.json +297 -0
- pytycho-0.2.0/spec/conformance/laps/scene.tys +251 -0
- pytycho-0.2.0/spec/conformance/rotating-loop/cases.json +8384 -0
- pytycho-0.2.0/spec/conformance/rotating-loop/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/rotating-loop/scene.tys +149 -0
- pytycho-0.2.0/spec/conformance/two-panels/cases.json +21588 -0
- pytycho-0.2.0/spec/conformance/two-panels/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/two-panels/scene.tys +257 -0
- pytycho-0.2.0/spec/conformance/value-clock/cases.json +3566 -0
- pytycho-0.2.0/spec/conformance/value-clock/data.tyd +0 -0
- pytycho-0.2.0/spec/conformance/value-clock/scene.tys +161 -0
- pytycho-0.2.0/spec/scene.schema.json +1414 -0
- pytycho-0.2.0/spec/tycho-scene.md +209 -0
- pytycho-0.2.0/src/pytycho/__init__.py +56 -0
- pytycho-0.2.0/src/pytycho/_evaluate.py +836 -0
- pytycho-0.2.0/src/pytycho/_static/tycho.js +4164 -0
- pytycho-0.2.0/src/pytycho/_thin.py +126 -0
- pytycho-0.2.0/src/pytycho/container.py +373 -0
- pytycho-0.2.0/src/pytycho/html.py +130 -0
- pytycho-0.2.0/src/pytycho/py.typed +0 -0
- pytycho-0.2.0/src/pytycho/scene.py +1581 -0
- pytycho-0.2.0/src/pytycho/template.html +16 -0
- pytycho-0.2.0/tests/test_conformance.py +91 -0
- pytycho-0.2.0/tests/test_container.py +318 -0
- pytycho-0.2.0/tests/test_evaluate.py +204 -0
- pytycho-0.2.0/tests/test_examples.py +67 -0
- pytycho-0.2.0/tests/test_scene.py +659 -0
- pytycho-0.2.0/tests/test_thin.py +54 -0
- pytycho-0.2.0/uv.lock +1533 -0
- pytycho-0.2.0/web/.prettierignore +3 -0
- pytycho-0.2.0/web/.prettierrc.json +4 -0
- pytycho-0.2.0/web/README.md +38 -0
- pytycho-0.2.0/web/dev/index.html +175 -0
- pytycho-0.2.0/web/package-lock.json +2339 -0
- pytycho-0.2.0/web/package.json +73 -0
- pytycho-0.2.0/web/scripts/frame-profile.mjs +338 -0
- pytycho-0.2.0/web/scripts/gen-types.mjs +36 -0
- pytycho-0.2.0/web/scripts/notices.mjs +61 -0
- pytycho-0.2.0/web/scripts/poster.mjs +91 -0
- pytycho-0.2.0/web/scripts/prepack.mjs +12 -0
- pytycho-0.2.0/web/src/container.ts +358 -0
- pytycho-0.2.0/web/src/errors.ts +18 -0
- pytycho-0.2.0/web/src/evaluate/clock.ts +94 -0
- pytycho-0.2.0/web/src/evaluate/frames.ts +261 -0
- pytycho-0.2.0/web/src/evaluate/index.ts +62 -0
- pytycho-0.2.0/web/src/evaluate/layers.ts +149 -0
- pytycho-0.2.0/web/src/evaluate/resolve.ts +154 -0
- pytycho-0.2.0/web/src/evaluate/sets.ts +158 -0
- pytycho-0.2.0/web/src/evaluate/types.ts +44 -0
- pytycho-0.2.0/web/src/index.ts +28 -0
- pytycho-0.2.0/web/src/runtime/colormaps.ts +44 -0
- pytycho-0.2.0/web/src/runtime/colors.ts +208 -0
- pytycho-0.2.0/web/src/runtime/element.ts +1985 -0
- pytycho-0.2.0/web/src/runtime/export.ts +439 -0
- pytycho-0.2.0/web/src/runtime/geometry.ts +627 -0
- pytycho-0.2.0/web/src/runtime/icons.ts +77 -0
- pytycho-0.2.0/web/src/runtime/layout.ts +80 -0
- pytycho-0.2.0/web/src/runtime/overlay.ts +45 -0
- pytycho-0.2.0/web/src/runtime/readouts.ts +351 -0
- pytycho-0.2.0/web/src/runtime/renderer.ts +1958 -0
- pytycho-0.2.0/web/src/runtime/state.ts +330 -0
- pytycho-0.2.0/web/src/runtime/tiles.ts +71 -0
- pytycho-0.2.0/web/src/scene.gen.ts +484 -0
- pytycho-0.2.0/web/src/scene.ts +47 -0
- pytycho-0.2.0/web/src/validate.ts +452 -0
- pytycho-0.2.0/web/test/chrome.test.ts +919 -0
- pytycho-0.2.0/web/test/conformance.test.ts +133 -0
- pytycho-0.2.0/web/test/container.test.ts +252 -0
- pytycho-0.2.0/web/test/corpus.ts +44 -0
- pytycho-0.2.0/web/test/dom-shim.ts +267 -0
- pytycho-0.2.0/web/test/evaluate.test.ts +234 -0
- pytycho-0.2.0/web/test/export.test.ts +364 -0
- pytycho-0.2.0/web/test/fit.test.ts +296 -0
- pytycho-0.2.0/web/test/geometry.test.ts +321 -0
- pytycho-0.2.0/web/test/helpers.ts +132 -0
- pytycho-0.2.0/web/test/runtime-helpers.test.ts +233 -0
- pytycho-0.2.0/web/test/scene-types.test.ts +305 -0
- pytycho-0.2.0/web/test/state.test.ts +191 -0
- pytycho-0.2.0/web/test/tsconfig.json +9 -0
- pytycho-0.2.0/web/tsconfig.build.json +11 -0
- pytycho-0.2.0/web/tsconfig.json +17 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "beeson-group",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Beeson Group"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "Agent skills for the Beeson Group's astrodynamics tools"
|
|
8
|
+
},
|
|
9
|
+
"plugins": [
|
|
10
|
+
{
|
|
11
|
+
"name": "tycho",
|
|
12
|
+
"source": "./",
|
|
13
|
+
"description": "Write TyCHO scenes with pytycho and render them as one self-contained HTML page",
|
|
14
|
+
"version": "0.2.0",
|
|
15
|
+
"category": "visualization"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tycho",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Write TyCHO scenes with pytycho from NumPy arrays and render them as one self-contained HTML page",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Claudio Vestini"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://pytycho.readthedocs.io/",
|
|
9
|
+
"repository": "https://github.com/beeson-group/tycho",
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"keywords": ["astrodynamics", "visualization", "three-body", "pytycho"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Fails unless pytycho, the npm package, and the Claude Code plugin carry the same version.
|
|
2
|
+
|
|
3
|
+
Given a tag such as ``v0.2.0``, it also fails unless the tag names this version and CHANGELOG.md has a
|
|
4
|
+
section for it. A release thus cannot go out with mismatched versions or without its notes. The script
|
|
5
|
+
uses only the standard library.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def versions() -> dict[str, str]:
|
|
19
|
+
init = (ROOT / "src" / "pytycho" / "__init__.py").read_text(encoding="utf-8")
|
|
20
|
+
match = re.search(r'^__version__ = "([^"]+)"', init, re.MULTILINE)
|
|
21
|
+
marketplace = json.loads((ROOT / ".claude-plugin" / "marketplace.json").read_text(encoding="utf-8"))
|
|
22
|
+
return {
|
|
23
|
+
"src/pytycho/__init__.py": match.group(1) if match else "<missing>",
|
|
24
|
+
"web/package.json": json.loads((ROOT / "web" / "package.json").read_text(encoding="utf-8"))["version"],
|
|
25
|
+
".claude-plugin/plugin.json": json.loads((ROOT / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8"))["version"],
|
|
26
|
+
".claude-plugin/marketplace.json": next(p["version"] for p in marketplace["plugins"] if p["name"] == "tycho"),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main(argv: list[str]) -> str | None:
|
|
31
|
+
found = versions()
|
|
32
|
+
if len(set(found.values())) != 1:
|
|
33
|
+
return "versions disagree: " + ", ".join(f"{path} {v}" for path, v in found.items())
|
|
34
|
+
version = next(iter(found.values()))
|
|
35
|
+
if argv:
|
|
36
|
+
tag = argv[0]
|
|
37
|
+
if tag != f"v{version}":
|
|
38
|
+
return f"tag {tag} does not name version {version}"
|
|
39
|
+
changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
|
|
40
|
+
if f"## [{version}]" not in changelog:
|
|
41
|
+
return f"CHANGELOG.md does not have a section ## [{version}]"
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
cooldown:
|
|
8
|
+
default-days: 7
|
|
9
|
+
groups:
|
|
10
|
+
actions:
|
|
11
|
+
patterns:
|
|
12
|
+
- "*"
|
|
13
|
+
- package-ecosystem: "pre-commit"
|
|
14
|
+
directory: "/"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "monthly"
|
|
17
|
+
cooldown:
|
|
18
|
+
default-days: 7
|
|
19
|
+
groups:
|
|
20
|
+
pre-commit:
|
|
21
|
+
patterns:
|
|
22
|
+
- "*"
|
|
23
|
+
- package-ecosystem: "pip"
|
|
24
|
+
directory: "/docs"
|
|
25
|
+
schedule:
|
|
26
|
+
interval: "monthly"
|
|
27
|
+
cooldown:
|
|
28
|
+
default-days: 7
|
|
29
|
+
groups:
|
|
30
|
+
docs:
|
|
31
|
+
patterns:
|
|
32
|
+
- "*"
|
|
33
|
+
- package-ecosystem: "npm"
|
|
34
|
+
directory: "/web"
|
|
35
|
+
schedule:
|
|
36
|
+
interval: "monthly"
|
|
37
|
+
cooldown:
|
|
38
|
+
default-days: 7
|
|
39
|
+
groups:
|
|
40
|
+
npm:
|
|
41
|
+
patterns:
|
|
42
|
+
- "*"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
name: CD
|
|
2
|
+
|
|
3
|
+
# On a published release, this workflow uploads pytycho to PyPI and @beeson-group/tycho to npm, both through
|
|
4
|
+
# trusted publishing with OpenID Connect, hence it does not store any token. The runtime is built once, thus
|
|
5
|
+
# both packages carry the same bundle, and neither is published unless both build and pass their tests. A
|
|
6
|
+
# manual run uploads pytycho to TestPyPI as a release candidate and does not publish to npm, which lacks a
|
|
7
|
+
# test registry. Trusted publishing to npm needs an existing package, and @beeson-group/tycho exists there.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
release:
|
|
12
|
+
types:
|
|
13
|
+
- published
|
|
14
|
+
|
|
15
|
+
permissions: {}
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: false # never cancel a publish halfway
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
FORCE_COLOR: 3
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
runtime:
|
|
26
|
+
name: Build, test, and pack the runtime
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- name: The versions agree and match the tag on a release
|
|
35
|
+
env:
|
|
36
|
+
TAG: ${{ github.event_name == 'release' && github.ref_name || '' }}
|
|
37
|
+
run: python3 .github/check_versions.py ${TAG:+"$TAG"}
|
|
38
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
39
|
+
with:
|
|
40
|
+
node-version: 24
|
|
41
|
+
package-manager-cache: false # a release workflow does not use a cache
|
|
42
|
+
- name: Test, then pack, which checks the notices and builds the bundles and the types
|
|
43
|
+
working-directory: web
|
|
44
|
+
run: npm ci && npm run typecheck && npm test && npm pack
|
|
45
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
46
|
+
with:
|
|
47
|
+
name: npm-package
|
|
48
|
+
path: web/beeson-group-tycho-*.tgz
|
|
49
|
+
if-no-files-found: error
|
|
50
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
51
|
+
with:
|
|
52
|
+
name: runtime
|
|
53
|
+
path: src/pytycho/_static/tycho.js
|
|
54
|
+
if-no-files-found: error
|
|
55
|
+
|
|
56
|
+
dist:
|
|
57
|
+
name: Build and check the distributions
|
|
58
|
+
needs: [runtime]
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
permissions:
|
|
61
|
+
contents: read
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
64
|
+
with:
|
|
65
|
+
persist-credentials: false
|
|
66
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
67
|
+
with:
|
|
68
|
+
name: runtime
|
|
69
|
+
path: src/pytycho/_static
|
|
70
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.14"
|
|
73
|
+
enable-cache: false # a release workflow does not use a cache
|
|
74
|
+
- run: uv run pytest
|
|
75
|
+
- run: uv build
|
|
76
|
+
- run: uvx twine check --strict dist/*
|
|
77
|
+
- name: The wheel carries the runtime and the notices
|
|
78
|
+
run: |
|
|
79
|
+
unzip -l dist/*.whl | tee wheel.txt
|
|
80
|
+
for f in pytycho/_static/tycho.js licenses/LICENSE licenses/NOTICE licenses/THIRD_PARTY_NOTICES.md; do
|
|
81
|
+
grep -q "$f" wheel.txt || { echo "::error::the wheel has no $f"; exit 1; }
|
|
82
|
+
done
|
|
83
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
84
|
+
with:
|
|
85
|
+
name: Packages
|
|
86
|
+
path: dist/*
|
|
87
|
+
if-no-files-found: error
|
|
88
|
+
|
|
89
|
+
publish-pypi:
|
|
90
|
+
name: Publish to PyPI
|
|
91
|
+
if: github.event_name == 'release' && github.event.action == 'published'
|
|
92
|
+
needs: [dist]
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
environment:
|
|
95
|
+
name: pypi
|
|
96
|
+
url: https://pypi.org/p/pytycho
|
|
97
|
+
permissions:
|
|
98
|
+
id-token: write # trusted publishing and the attestations need it
|
|
99
|
+
attestations: write # actions/attest writes the attestations
|
|
100
|
+
contents: read
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
103
|
+
with:
|
|
104
|
+
name: Packages
|
|
105
|
+
path: dist
|
|
106
|
+
- name: Generate artifact attestations for the sdist and the wheel
|
|
107
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
|
108
|
+
with:
|
|
109
|
+
subject-path: "dist/*"
|
|
110
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
111
|
+
|
|
112
|
+
publish-testpypi:
|
|
113
|
+
name: Publish to TestPyPI
|
|
114
|
+
if: github.event_name == 'workflow_dispatch'
|
|
115
|
+
needs: [dist]
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
environment:
|
|
118
|
+
name: testpypi
|
|
119
|
+
url: https://test.pypi.org/p/pytycho
|
|
120
|
+
permissions:
|
|
121
|
+
id-token: write # trusted publishing needs it
|
|
122
|
+
contents: read
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
125
|
+
with:
|
|
126
|
+
name: Packages
|
|
127
|
+
path: dist
|
|
128
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
129
|
+
with:
|
|
130
|
+
repository-url: https://test.pypi.org/legacy/
|
|
131
|
+
|
|
132
|
+
publish-npm:
|
|
133
|
+
name: Publish to npm
|
|
134
|
+
if: github.event_name == 'release' && github.event.action == 'published'
|
|
135
|
+
needs: [runtime, dist]
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
environment:
|
|
138
|
+
name: npm
|
|
139
|
+
url: https://www.npmjs.com/package/@beeson-group/tycho
|
|
140
|
+
permissions:
|
|
141
|
+
contents: read
|
|
142
|
+
id-token: write # trusted publishing and provenance need it
|
|
143
|
+
steps:
|
|
144
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
145
|
+
with:
|
|
146
|
+
name: npm-package
|
|
147
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
148
|
+
with:
|
|
149
|
+
node-version: 24 # trusted publishing needs npm 11.5.1 or later, and Node 24.21 bundles npm 11.19
|
|
150
|
+
registry-url: https://registry.npmjs.org
|
|
151
|
+
package-manager-cache: false
|
|
152
|
+
- run: npm publish ./beeson-group-tycho-*.tgz --access public
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
env:
|
|
17
|
+
FORCE_COLOR: 3
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
lint:
|
|
21
|
+
name: pre-commit
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
30
|
+
- run: uvx pre-commit run --all-files --show-diff-on-failure
|
|
31
|
+
|
|
32
|
+
tests:
|
|
33
|
+
name: Python ${{ matrix.python-version }} on ${{ matrix.runs-on }}
|
|
34
|
+
runs-on: ${{ matrix.runs-on }}
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
41
|
+
runs-on: [ubuntu-latest]
|
|
42
|
+
include:
|
|
43
|
+
- python-version: "3.10"
|
|
44
|
+
runs-on: macos-latest
|
|
45
|
+
- python-version: "3.14"
|
|
46
|
+
runs-on: macos-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
49
|
+
with:
|
|
50
|
+
persist-credentials: false
|
|
51
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
52
|
+
with:
|
|
53
|
+
python-version: ${{ matrix.python-version }}
|
|
54
|
+
- run: uv run pytest --durations=10
|
|
55
|
+
|
|
56
|
+
lowest:
|
|
57
|
+
name: Python 3.10 at the lowest direct dependencies
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
permissions:
|
|
60
|
+
contents: read
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
63
|
+
with:
|
|
64
|
+
persist-credentials: false
|
|
65
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.10"
|
|
68
|
+
- run: uv run --resolution lowest-direct pytest
|
|
69
|
+
|
|
70
|
+
web:
|
|
71
|
+
name: Runtime
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
permissions:
|
|
74
|
+
contents: read
|
|
75
|
+
defaults:
|
|
76
|
+
run:
|
|
77
|
+
working-directory: web
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
80
|
+
with:
|
|
81
|
+
persist-credentials: false
|
|
82
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
83
|
+
with:
|
|
84
|
+
node-version: 24
|
|
85
|
+
cache: npm
|
|
86
|
+
cache-dependency-path: web/package-lock.json
|
|
87
|
+
- run: npm ci
|
|
88
|
+
- run: npm run typecheck
|
|
89
|
+
- run: npm test
|
|
90
|
+
- name: Pack, which checks the notices and builds the bundles and the types
|
|
91
|
+
run: npm pack
|
|
92
|
+
- run: npx --yes publint@0.3 --strict
|
|
93
|
+
- run: npx --yes @arethetypeswrong/cli@0.18 ./beeson-group-tycho-*.tgz --profile esm-only --exclude-entrypoints ./iife
|
|
94
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
95
|
+
with:
|
|
96
|
+
name: runtime
|
|
97
|
+
path: src/pytycho/_static/tycho.js
|
|
98
|
+
if-no-files-found: error
|
|
99
|
+
|
|
100
|
+
dist:
|
|
101
|
+
name: Distributions
|
|
102
|
+
needs: [web]
|
|
103
|
+
runs-on: ubuntu-latest
|
|
104
|
+
permissions:
|
|
105
|
+
contents: read
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
108
|
+
with:
|
|
109
|
+
persist-credentials: false
|
|
110
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
111
|
+
with:
|
|
112
|
+
name: runtime
|
|
113
|
+
path: src/pytycho/_static
|
|
114
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
115
|
+
- run: python3 .github/check_versions.py
|
|
116
|
+
- run: uv build
|
|
117
|
+
- run: uvx twine check --strict dist/*
|
|
118
|
+
- name: The wheel carries the runtime and the notices, and the sdist does not carry example data
|
|
119
|
+
run: |
|
|
120
|
+
unzip -l dist/*.whl | tee wheel.txt
|
|
121
|
+
for f in pytycho/_static/tycho.js licenses/LICENSE licenses/NOTICE licenses/THIRD_PARTY_NOTICES.md; do
|
|
122
|
+
grep -q "$f" wheel.txt || { echo "::error::the wheel has no $f"; exit 1; }
|
|
123
|
+
done
|
|
124
|
+
if tar -tzf dist/*.tar.gz | grep -E '^[^/]+/examples/.*\.(csv|tys|tyd|html)$'; then
|
|
125
|
+
echo "::error::the sdist carries example data"; exit 1
|
|
126
|
+
fi
|
|
127
|
+
- name: The installed wheel writes a page that carries the runtime and its notices
|
|
128
|
+
run: |
|
|
129
|
+
uv venv /tmp/wheel-env
|
|
130
|
+
uv pip install --python /tmp/wheel-env/bin/python dist/*.whl
|
|
131
|
+
/tmp/wheel-env/bin/python - <<'EOF'
|
|
132
|
+
import numpy as np
|
|
133
|
+
import pytycho as ty
|
|
134
|
+
|
|
135
|
+
scene = ty.Scene(title="smoke")
|
|
136
|
+
frame = scene.frame("f", axes=("x", "y", "z"))
|
|
137
|
+
loop = scene.track_set("loop", frame=frame, periodic=True)
|
|
138
|
+
t = np.linspace(0.0, 2.0 * np.pi, 50)
|
|
139
|
+
loop.add(np.column_stack([np.cos(t), np.sin(t)]))
|
|
140
|
+
scene.clock(channels=["@index"], steps=50)
|
|
141
|
+
scene.view("main", frame=frame, driver=ty.driver(loop, 0), layers=[ty.track(loop)])
|
|
142
|
+
scene.panel("p", "main")
|
|
143
|
+
scene.write_html("smoke.html")
|
|
144
|
+
page = open("smoke.html", encoding="utf-8").read()
|
|
145
|
+
assert "This bundle contains" in page and "three 0." in page, "the page has no notice banner"
|
|
146
|
+
EOF
|
|
147
|
+
|
|
148
|
+
pass:
|
|
149
|
+
name: All checks pass
|
|
150
|
+
if: always()
|
|
151
|
+
needs: [lint, tests, lowest, web, dist]
|
|
152
|
+
runs-on: ubuntu-slim
|
|
153
|
+
timeout-minutes: 2
|
|
154
|
+
permissions: {}
|
|
155
|
+
steps:
|
|
156
|
+
- name: Decide whether the needed jobs succeeded or failed
|
|
157
|
+
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
|
|
158
|
+
with:
|
|
159
|
+
jobs: ${{ toJSON(needs) }}
|
pytycho-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# build outputs
|
|
2
|
+
web/node_modules/
|
|
3
|
+
web/dist/
|
|
4
|
+
src/pytycho/_static/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
__pycache__/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.nox/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
docs/_build/
|
|
14
|
+
docs/_extra/
|
|
15
|
+
|
|
16
|
+
# copied into web/ by its prepack script, from the repository root
|
|
17
|
+
web/LICENSE
|
|
18
|
+
web/NOTICE
|
|
19
|
+
web/AUTHORS
|
|
20
|
+
web/THIRD_PARTY_NOTICES.md
|
|
21
|
+
web/*.tgz
|
|
22
|
+
|
|
23
|
+
# example inputs computed with PyDyLAN, which are not distributed
|
|
24
|
+
examples/**/data/
|
|
25
|
+
|
|
26
|
+
# generated by examples
|
|
27
|
+
examples/**/*.tyd
|
|
28
|
+
examples/**/*.tys
|
|
29
|
+
examples/**/*.html
|
|
30
|
+
examples/**/*.png
|
|
31
|
+
|
|
32
|
+
# local environment
|
|
33
|
+
.venv/
|
|
34
|
+
.DS_Store
|
|
35
|
+
|
|
36
|
+
# editor-local language-server settings
|
|
37
|
+
.zed/
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# The lint job of .github/workflows/ci.yml runs these hooks, and Dependabot updates each rev monthly.
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: "v6.0.0"
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-added-large-files
|
|
7
|
+
args: ["--maxkb=1024"] # the conformance corpus has cases of several hundred KB
|
|
8
|
+
- id: check-case-conflict
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-symlinks
|
|
11
|
+
- id: check-yaml
|
|
12
|
+
- id: check-json
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: debug-statements
|
|
15
|
+
- id: end-of-file-fixer
|
|
16
|
+
- id: mixed-line-ending
|
|
17
|
+
- id: name-tests-test
|
|
18
|
+
args: ["--pytest-test-first"]
|
|
19
|
+
- id: trailing-whitespace
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/pre-commit/pygrep-hooks
|
|
22
|
+
rev: "v1.10.0"
|
|
23
|
+
hooks:
|
|
24
|
+
- id: rst-backticks
|
|
25
|
+
- id: rst-directive-colons
|
|
26
|
+
- id: rst-inline-touching-normal
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
29
|
+
rev: "v0.16.10"
|
|
30
|
+
hooks:
|
|
31
|
+
- id: ruff-check
|
|
32
|
+
args: ["--fix", "--show-fixes"]
|
|
33
|
+
- id: ruff-format
|
|
34
|
+
|
|
35
|
+
# pyright is used because src passes its standard mode as written, while mypy would need code changes
|
|
36
|
+
- repo: https://github.com/RobertCraigie/pyright-python
|
|
37
|
+
rev: "v1.1.414"
|
|
38
|
+
hooks:
|
|
39
|
+
- id: pyright
|
|
40
|
+
files: ^(src|tests|examples)/
|
|
41
|
+
additional_dependencies: ["numpy", "pytest", "jsonschema"]
|
|
42
|
+
|
|
43
|
+
# prettier runs in check mode, since the TypeScript follows web/.prettierrc.json and is reformatted by hand
|
|
44
|
+
- repo: https://github.com/rbubley/mirrors-prettier
|
|
45
|
+
rev: "v3.9.9"
|
|
46
|
+
hooks:
|
|
47
|
+
- id: prettier
|
|
48
|
+
entry: prettier --check --ignore-unknown
|
|
49
|
+
files: ^web/(src|test|scripts)/
|
|
50
|
+
types_or: [ts, javascript]
|
|
51
|
+
exclude: ^web/src/scene\.gen\.ts$ # web/scripts/gen-types.mjs writes it, and a test compares it verbatim
|
|
52
|
+
|
|
53
|
+
- repo: https://github.com/codespell-project/codespell
|
|
54
|
+
rev: "v2.4.3"
|
|
55
|
+
hooks:
|
|
56
|
+
- id: codespell
|
|
57
|
+
additional_dependencies:
|
|
58
|
+
- tomli; python_version<'3.11'
|
|
59
|
+
|
|
60
|
+
- repo: local
|
|
61
|
+
hooks:
|
|
62
|
+
- id: disallow-caps
|
|
63
|
+
name: Disallow improper capitalization
|
|
64
|
+
language: pygrep
|
|
65
|
+
entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|PyDylan|Pydylan|PyTycho
|
|
66
|
+
exclude: ^\.pre-commit-config\.yaml$
|
|
67
|
+
|
|
68
|
+
- repo: https://github.com/abravalheri/validate-pyproject
|
|
69
|
+
rev: "0.26"
|
|
70
|
+
hooks:
|
|
71
|
+
- id: validate-pyproject
|
|
72
|
+
additional_dependencies: ["validate-pyproject-schema-store[all]"]
|
|
73
|
+
|
|
74
|
+
- repo: https://github.com/python-jsonschema/check-jsonschema
|
|
75
|
+
rev: "0.38.2"
|
|
76
|
+
hooks:
|
|
77
|
+
- id: check-dependabot
|
|
78
|
+
- id: check-github-workflows
|
|
79
|
+
- id: check-readthedocs
|
|
80
|
+
|
|
81
|
+
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
|
82
|
+
rev: "v1.30.1"
|
|
83
|
+
hooks:
|
|
84
|
+
- id: zizmor
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Read the Docs serves the site at https://pytycho.readthedocs.io/. It builds the latest version on each push to
|
|
2
|
+
# main, the stable version from the highest release tag, and a preview for each pull request, linked from its checks.
|
|
3
|
+
# docs/conf.py puts src/ on the path, hence the build installs only the docs toolchain and NumPy, not the package.
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
os: ubuntu-24.04
|
|
8
|
+
tools:
|
|
9
|
+
python: "3.14"
|
|
10
|
+
|
|
11
|
+
sphinx:
|
|
12
|
+
configuration: docs/conf.py
|
|
13
|
+
fail_on_warning: true
|
|
14
|
+
|
|
15
|
+
python:
|
|
16
|
+
install:
|
|
17
|
+
- requirements: docs/requirements.txt
|
pytycho-0.2.0/AGENTS.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
TyCHO has two halves, which are released together at one version. pytycho (Python) writes a scene, i.e. the scene document `scene.tys` (JSON that specifies what to draw) and the data container `data.tyd` (the arrays), from NumPy arrays. It then inlines the browser runtime (TypeScript, `@beeson-group/tycho`) into one HTML page, which draws the scene.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
- `src/pytycho/` holds the `Scene` builder (`scene.py`), the data container (`container.py`), the evaluator (`_evaluate.py`, which computes what each panel draws at a frame index), thinning (`_thin.py`, which keeps a subset of the stored samples), and the page writer (`html.py`, `template.html`). Its `_static/tycho.js` is the built runtime, which is gitignored and shipped in the wheel, hence it has to be built in a fresh checkout before pytycho can write a page.
|
|
8
|
+
- `web/src/` holds the runtime, i.e. the container and scene readers, the evaluator (`evaluate/`), and the `<tycho-viewer>` element with its renderer (`runtime/`). The file `scene.gen.ts` is generated from the schema by `npm run gen:types`. Never edit it by hand, since a test fails when it differs from the types generated from the schema.
|
|
9
|
+
- `spec/` holds the format rules, the JSON Schema, and the conformance corpus, i.e. scenes with the answers that both evaluators must give.
|
|
10
|
+
- `tests/` holds the pytest suite, and `web/test/` holds the vitest suite of the runtime.
|
|
11
|
+
- `examples/` holds the Broucke example, which builds its page from `broucke-input.tyd`, a file attached to each GitHub Release. The input and the outputs are gitignored.
|
|
12
|
+
- `docs/` holds the Sphinx site. `skills/tycho/` holds the agent skill for people who use pytycho, and `.claude-plugin/` holds its plugin manifests, which make the repository a Claude Code plugin marketplace.
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm --prefix web ci && npm --prefix web run build # the runtime, into web/dist and src/pytycho/_static
|
|
18
|
+
npm --prefix web test && npm --prefix web run typecheck
|
|
19
|
+
uv run pytest
|
|
20
|
+
uvx pre-commit run --all-files # ruff, pyright, prettier --check, codespell, schema checks
|
|
21
|
+
uvx nox -s docs # the site, into docs/_build/html
|
|
22
|
+
uvx nox -s build # the sdist and the wheel, into dist/
|
|
23
|
+
python3 .github/check_versions.py # the four version fields agree
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Conventions
|
|
27
|
+
|
|
28
|
+
- Python lines run to 140 columns and are formatted by ruff. TypeScript lines run to 160 columns, use single quotes, and are checked by prettier, which only reports a misformatted file. Run `npx prettier --write <file>` from `web/` to apply the change.
|
|
29
|
+
- Where the Python and the TypeScript evaluators disagree, the answer in the corpus in `spec/` holds. Reword the rule to match it, add a case when the corpus does not cover the disagreement, and then fix both implementations.
|
|
30
|
+
- A version bump touches `src/pytycho/__init__.py`, `web/package.json`, and the two files in `.claude-plugin/` together, and `python3 .github/check_versions.py` fails while the four disagree.
|
|
31
|
+
- Every user-visible change gets a line under `## [Unreleased]` in `CHANGELOG.md`, since this section becomes the notes of the next release.
|
pytycho-0.2.0/AUTHORS
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# The TyCHO Authors
|
|
2
|
+
|
|
3
|
+
The people who hold copyright in TyCHO, which the file NOTICE names together as "The TyCHO Authors".
|
|
4
|
+
|
|
5
|
+
- Claudio Vestini, Beeson Group, Princeton University
|
|
6
|
+
- Nina Karampela, Beeson Group, Princeton University
|
|
7
|
+
- Jack Sydney Schwartz, Beeson Group, Princeton University
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to TyCHO are recorded here, for both packages, `pytycho` on PyPI and `@beeson-group/tycho` on npm, which are released together at one version. The format follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and the versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Before 1.0, a breaking change, i.e. one after which existing code or files may stop working, bumps the minor version, and a fix bumps the patch version.
|
|
4
|
+
|
|
5
|
+
The two file formats are versioned apart from the packages, i.e. either format version may stay the same across a package release. The notes of each release therefore give the versions of the scene document `scene.tys` and the data container `data.tyd` that the packages read and write.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
Version 0.2.0 introduces the scene grammar, i.e. a general vocabulary from which a visualization is declared. The packages at this version write and read scene format 0.2, which may still change incompatibly until 1.0, and data container version 1.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- TyCHO describes each scene in a scene grammar. A scene is two files, which a Python script writes from NumPy arrays and the browser interprets. The grammar covers track sets (collections of trajectories) with per-sample channels (numbers stored at every sample, such as the time), bodies (named points), a tree of reference frames, variables, one clock, views with layers, panels, controls, and readouts. The rules of the grammar are numbered in `spec/tycho-scene.md`, and `spec/scene.schema.json` gives its structure. The conformance corpus in `spec/conformance/` holds scenes with the answers an evaluator must give, and both the Python and the TypeScript evaluators pass it.
|
|
16
|
+
- Two files store a scene. The scene document `scene.tys` is JSON that refers to the arrays by name and specifies what they are. The data container `data.tyd` holds a `TYCD` magic number, a JSON header, and named little-endian typed arrays. Each array is stored raw or, when this is smaller, compressed without loss in chunks of about 1 MiB. The compression takes the bit pattern of each number, replaces each row by its difference from the row before (delta coding), groups the bytes by their position within each number (byte shuffling), and deflates the result. A browser inflates, i.e. decompresses, these chunks with its native `DecompressionStream`, and a reader can decode one chunk at a time. The input of the Broucke example takes 60 percent of its raw size, and its page takes 12.7 MB, against 22.1 MB with the whole container gzipped.
|
|
17
|
+
- pytycho provides the `Scene` builder, thinning, and `evaluate`. The builder checks every call, then checks the whole scene for every value its variables can take, and reports each failure with the number of the rule it breaks. Thinning keeps a subset of the stored samples, selected by arclength, measured in the frames listed in the call, by default the frame of the set. The function `evaluate` returns what the browser draws for a frame index, so that a test can check what a scene draws without a browser.
|
|
18
|
+
- The runtime provides the `<tycho-viewer>` element, which draws any scene in panels that can be rearranged by drag and drop. Each panel keeps separate values of the panel-scoped variables, e.g. the frame that it shows. The element draws sliders, tabs, toggles, a transport (the controls that play and pause the clock and switch its active channel), and readouts.
|
|
19
|
+
- Playback runs at the refresh rate of the display. Every refresh draws at the progress given by the wall time, i.e. the real time elapsed, and this progress may lie between two frame indices. The layers under the first layer that takes a clock reading do not move with the animation. They are drawn once per panel, until the camera or these layers change, and copied back every frame. Body trails are read from positions kept per lap, so that a frame copies a trail instead of computing it. The frame rate shown counts the frames drawn in the last second. The script `web/scripts/frame-profile.mjs` measures what a frame of an example page costs, in headless Chrome.
|
|
20
|
+
- The viewer exports an MP4 video of its current state, from its controls or through `exportVideo`. The video is encoded in the browser itself, with the WebCodecs API and the mp4-muxer library.
|
|
21
|
+
- The npm package ships type declarations, which let TypeScript check code that uses the package.
|
|
22
|
+
- A track set stores its positions and channels as float32 by default. pytycho now stores a clock channel as float64 in a float32 set when float32 would round the ends of a step between samples by more than 1% of the step, or would make the channel nonmonotone. A nonmonotone channel would break rule T3, under which a clock channel increases or decreases strictly along every track. The argument `track_set(channel_dtypes=)` sets the stored dtype of a channel by hand.
|
|
23
|
+
- `build_html` embeds the container as base64 text without further compression, since its arrays are already compressed. It rejects a scene or data whose base64 is longer than 536,870,888 characters, the most that Chrome holds in one string, since a longer text would leave the page empty.
|
|
24
|
+
- Both container readers reject a view key or an encoding they do not know (rule D2), since a reader that ignored either could read the bytes wrongly. Section 1 of the spec specifies which changes bump the container version (rule D3). `Container.parse` in the runtime and the `load` method of the element return promises, since they inflate the encoded views, which the browser does asynchronously.
|
|
25
|
+
- The Broucke example reads its trajectories from `broucke-input.tyd`, a data container attached to each GitHub Release. It imports only NumPy, pytycho, and the standard library, hence it runs without PyDyLAN.
|
|
26
|
+
- An agent skill, `skills/tycho/SKILL.md`, sets out the scene grammar for a coding agent. It can be installed as a Claude Code plugin from this repository or with `npx skills add beeson-group/tycho`.
|
|
27
|
+
- The npm package bundles three.js and mp4-muxer, and it does not have any runtime dependencies, hence installing it does not install any other package.
|
|
28
|
+
- Both packages declare the license `Apache-2.0 AND MIT`, since the bundle carries MIT-licensed code, and they ship `NOTICE` and `THIRD_PARTY_NOTICES.md`. Every bundle starts with a comment listing the bundled packages. Every page that pytycho writes inlines the bundle, and thus carries the same comment.
|
|
29
|
+
|
|
30
|
+
[Unreleased]: https://github.com/beeson-group/tycho/compare/v0.2.0...HEAD
|
|
31
|
+
[0.2.0]: https://github.com/beeson-group/tycho/releases/tag/v0.2.0
|