geomotif 1.0.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.
Files changed (126) hide show
  1. geomotif-1.0.0/.github/ISSUE_TEMPLATE/bug.md +27 -0
  2. geomotif-1.0.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. geomotif-1.0.0/.github/ISSUE_TEMPLATE/motif.md +30 -0
  4. geomotif-1.0.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  5. geomotif-1.0.0/.github/dependabot.yml +16 -0
  6. geomotif-1.0.0/.github/workflows/ci.yml +131 -0
  7. geomotif-1.0.0/.github/workflows/docs.yml +50 -0
  8. geomotif-1.0.0/.github/workflows/publish.yml +67 -0
  9. geomotif-1.0.0/.gitignore +32 -0
  10. geomotif-1.0.0/.pre-commit-config.yaml +24 -0
  11. geomotif-1.0.0/CHANGELOG.md +436 -0
  12. geomotif-1.0.0/CODE_OF_CONDUCT.md +27 -0
  13. geomotif-1.0.0/CONTRIBUTING.md +135 -0
  14. geomotif-1.0.0/LICENSE +21 -0
  15. geomotif-1.0.0/Makefile +99 -0
  16. geomotif-1.0.0/PKG-INFO +403 -0
  17. geomotif-1.0.0/README.md +370 -0
  18. geomotif-1.0.0/SECURITY.md +49 -0
  19. geomotif-1.0.0/docs/api-policy.md +85 -0
  20. geomotif-1.0.0/docs/assets/fractal.hilbert.svg +7 -0
  21. geomotif-1.0.0/docs/assets/knot.celtic-grid.svg +23 -0
  22. geomotif-1.0.0/docs/assets/mandala.svg +43 -0
  23. geomotif-1.0.0/docs/assets/rose.maurer.svg +7 -0
  24. geomotif-1.0.0/docs/assets/spiral.golden.svg +7 -0
  25. geomotif-1.0.0/docs/assets/tiling.penrose-p3.svg +346 -0
  26. geomotif-1.0.0/docs/catalogue.md +246 -0
  27. geomotif-1.0.0/docs/changelog.md +1 -0
  28. geomotif-1.0.0/docs/extending.md +194 -0
  29. geomotif-1.0.0/docs/guide/cli.md +145 -0
  30. geomotif-1.0.0/docs/guide/designs.md +143 -0
  31. geomotif-1.0.0/docs/guide/export.md +159 -0
  32. geomotif-1.0.0/docs/guide/plotting.md +113 -0
  33. geomotif-1.0.0/docs/guide/points.md +162 -0
  34. geomotif-1.0.0/docs/index.md +144 -0
  35. geomotif-1.0.0/docs/stylesheets/geomotif.css +53 -0
  36. geomotif-1.0.0/examples/basic.py +23 -0
  37. geomotif-1.0.0/examples/plugin/README.md +80 -0
  38. geomotif-1.0.0/examples/plugin/pyproject.toml +22 -0
  39. geomotif-1.0.0/examples/plugin/src/geomotif_superformula/__init__.py +103 -0
  40. geomotif-1.0.0/mkdocs.yml +127 -0
  41. geomotif-1.0.0/pyproject.toml +167 -0
  42. geomotif-1.0.0/src/geomotif/__init__.py +175 -0
  43. geomotif-1.0.0/src/geomotif/__main__.py +8 -0
  44. geomotif-1.0.0/src/geomotif/bases/__init__.py +44 -0
  45. geomotif-1.0.0/src/geomotif/bases/lsystem.py +198 -0
  46. geomotif-1.0.0/src/geomotif/bases/parametric.py +233 -0
  47. geomotif-1.0.0/src/geomotif/bases/polygon.py +85 -0
  48. geomotif-1.0.0/src/geomotif/bases/segments.py +150 -0
  49. geomotif-1.0.0/src/geomotif/bases/tiling.py +231 -0
  50. geomotif-1.0.0/src/geomotif/cli.py +587 -0
  51. geomotif-1.0.0/src/geomotif/compose/__init__.py +40 -0
  52. geomotif-1.0.0/src/geomotif/compose/mandala.py +430 -0
  53. geomotif-1.0.0/src/geomotif/core/__init__.py +80 -0
  54. geomotif-1.0.0/src/geomotif/core/motif.py +102 -0
  55. geomotif-1.0.0/src/geomotif/core/registry.py +394 -0
  56. geomotif-1.0.0/src/geomotif/core/sampling.py +378 -0
  57. geomotif-1.0.0/src/geomotif/core/spacing.py +358 -0
  58. geomotif-1.0.0/src/geomotif/core/transform.py +407 -0
  59. geomotif-1.0.0/src/geomotif/core/types.py +316 -0
  60. geomotif-1.0.0/src/geomotif/demo.py +104 -0
  61. geomotif-1.0.0/src/geomotif/io/__init__.py +44 -0
  62. geomotif-1.0.0/src/geomotif/io/dxf.py +179 -0
  63. geomotif-1.0.0/src/geomotif/io/points.py +270 -0
  64. geomotif-1.0.0/src/geomotif/io/spec.py +342 -0
  65. geomotif-1.0.0/src/geomotif/io/svg.py +210 -0
  66. geomotif-1.0.0/src/geomotif/motifs/__init__.py +391 -0
  67. geomotif-1.0.0/src/geomotif/motifs/_common.py +102 -0
  68. geomotif-1.0.0/src/geomotif/motifs/curves.py +808 -0
  69. geomotif-1.0.0/src/geomotif/motifs/fractals.py +988 -0
  70. geomotif-1.0.0/src/geomotif/motifs/girih.py +619 -0
  71. geomotif-1.0.0/src/geomotif/motifs/graphs.py +465 -0
  72. geomotif-1.0.0/src/geomotif/motifs/guilloche.py +286 -0
  73. geomotif-1.0.0/src/geomotif/motifs/illusions.py +676 -0
  74. geomotif-1.0.0/src/geomotif/motifs/knots.py +781 -0
  75. geomotif-1.0.0/src/geomotif/motifs/polar.py +483 -0
  76. geomotif-1.0.0/src/geomotif/motifs/primitives.py +761 -0
  77. geomotif-1.0.0/src/geomotif/motifs/roulettes.py +404 -0
  78. geomotif-1.0.0/src/geomotif/motifs/sacred.py +470 -0
  79. geomotif-1.0.0/src/geomotif/motifs/solids.py +377 -0
  80. geomotif-1.0.0/src/geomotif/motifs/spirals.py +672 -0
  81. geomotif-1.0.0/src/geomotif/motifs/stringart.py +271 -0
  82. geomotif-1.0.0/src/geomotif/motifs/tilings.py +731 -0
  83. geomotif-1.0.0/src/geomotif/motifs/voronoi.py +499 -0
  84. geomotif-1.0.0/src/geomotif/plotting.py +330 -0
  85. geomotif-1.0.0/src/geomotif/py.typed +0 -0
  86. geomotif-1.0.0/tests/__init__.py +0 -0
  87. geomotif-1.0.0/tests/readback.py +173 -0
  88. geomotif-1.0.0/tests/test_cli.py +415 -0
  89. geomotif-1.0.0/tests/test_conformance.py +215 -0
  90. geomotif-1.0.0/tests/test_curves.py +387 -0
  91. geomotif-1.0.0/tests/test_dxf.py +114 -0
  92. geomotif-1.0.0/tests/test_fractals.py +512 -0
  93. geomotif-1.0.0/tests/test_gendocs.py +146 -0
  94. geomotif-1.0.0/tests/test_girih.py +414 -0
  95. geomotif-1.0.0/tests/test_graphs.py +323 -0
  96. geomotif-1.0.0/tests/test_guilloche.py +191 -0
  97. geomotif-1.0.0/tests/test_illusions.py +396 -0
  98. geomotif-1.0.0/tests/test_io.py +194 -0
  99. geomotif-1.0.0/tests/test_knots.py +394 -0
  100. geomotif-1.0.0/tests/test_lsystem.py +215 -0
  101. geomotif-1.0.0/tests/test_mandala.py +346 -0
  102. geomotif-1.0.0/tests/test_motif.py +78 -0
  103. geomotif-1.0.0/tests/test_parametric.py +201 -0
  104. geomotif-1.0.0/tests/test_plotting.py +148 -0
  105. geomotif-1.0.0/tests/test_polar.py +310 -0
  106. geomotif-1.0.0/tests/test_polygon.py +100 -0
  107. geomotif-1.0.0/tests/test_primitives.py +411 -0
  108. geomotif-1.0.0/tests/test_readme.py +61 -0
  109. geomotif-1.0.0/tests/test_registry.py +380 -0
  110. geomotif-1.0.0/tests/test_roulettes.py +250 -0
  111. geomotif-1.0.0/tests/test_sacred.py +256 -0
  112. geomotif-1.0.0/tests/test_sampling.py +284 -0
  113. geomotif-1.0.0/tests/test_segments.py +162 -0
  114. geomotif-1.0.0/tests/test_solids.py +261 -0
  115. geomotif-1.0.0/tests/test_spacing.py +173 -0
  116. geomotif-1.0.0/tests/test_spec.py +216 -0
  117. geomotif-1.0.0/tests/test_spirals.py +424 -0
  118. geomotif-1.0.0/tests/test_stringart.py +218 -0
  119. geomotif-1.0.0/tests/test_svg.py +181 -0
  120. geomotif-1.0.0/tests/test_tiling.py +182 -0
  121. geomotif-1.0.0/tests/test_tilings.py +540 -0
  122. geomotif-1.0.0/tests/test_transform.py +292 -0
  123. geomotif-1.0.0/tests/test_types.py +180 -0
  124. geomotif-1.0.0/tests/test_voronoi.py +525 -0
  125. geomotif-1.0.0/tools/gendocs.py +576 -0
  126. geomotif-1.0.0/uv.lock +1386 -0
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something does not do what it says it does
4
+ labels: bug
5
+ ---
6
+
7
+ **What you did**
8
+
9
+ The smallest piece of code that shows it. A motif name and its parameters is
10
+ usually enough; a spec file works too.
11
+
12
+ ```python
13
+
14
+ ```
15
+
16
+ **What happened**
17
+
18
+ Output, traceback, or the exported file — whichever shows the problem.
19
+
20
+ **What you expected instead**
21
+
22
+ **Versions**
23
+
24
+ - geomotif:
25
+ - Python:
26
+ - OS:
27
+ - matplotlib / scipy, if the problem involves either:
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Question or idea
4
+ url: https://github.com/pianosuki/geomotif/discussions
5
+ about: For anything that is not a bug or a concrete request.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Motif request
3
+ about: A shape that ought to be in the catalogue
4
+ labels: motif
5
+ ---
6
+
7
+ **The shape**
8
+
9
+ What it is called, and a link to a description of it — Wikipedia, a paper, a
10
+ picture. If it has a formula, give the formula.
11
+
12
+ **How it is defined**
13
+
14
+ This decides which base class it uses, so it is the useful half of the request:
15
+
16
+ - a radius as a function of angle?
17
+ - one curve from one parameter?
18
+ - a set of corners?
19
+ - a grammar drawn with a turtle?
20
+ - points joined by edges?
21
+ - a cell repeated on a lattice, or tiles that subdivide?
22
+
23
+ **Parameters**
24
+
25
+ What should be adjustable, and what a good default would be.
26
+
27
+ **Anything everyone gets wrong about it**
28
+
29
+ Optional, and the most valuable part when there is one — a petal count that
30
+ depends on a parity rule, a spiral that is not the spiral people think it is.
@@ -0,0 +1,13 @@
1
+ **What this changes, and why**
2
+
3
+ **Checklist**
4
+
5
+ - [ ] `make check` passes (ruff, ruff-format, mypy strict, pytest)
6
+ - [ ] Tests cover what is *interesting* about the change, not just its lines
7
+ - [ ] `CHANGELOG.md` has an entry under `[Unreleased]`
8
+ - [ ] `make docs-gen` was run and the result committed, if a motif was added,
9
+ renamed or re-exampled
10
+ - [ ] The core is still zero-dependency, or the new dependency is behind an
11
+ extra and declared with `requires=`
12
+
13
+ See [CONTRIBUTING.md](../CONTRIBUTING.md) for the house style.
@@ -0,0 +1,16 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Actions only. The package has no runtime dependencies to update, and the
5
+ # dev and docs groups are floor-pinned rather than locked, so a bot PR there
6
+ # would be noise. A pinned action that quietly goes unmaintained is the real
7
+ # supply-chain risk in this repository.
8
+ - package-ecosystem: github-actions
9
+ directory: /
10
+ schedule:
11
+ interval: monthly
12
+ groups:
13
+ actions:
14
+ patterns: ["*"]
15
+ commit-message:
16
+ prefix: "ci"
@@ -0,0 +1,131 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ lint:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ # Pinned to the supported floor so linting checks against the
23
+ # oldest interpreter we promise to run on, not the newest.
24
+ python-version: "3.12"
25
+ cache: pip
26
+ cache-dependency-path: pyproject.toml
27
+ - run: pip install -e . --group dev
28
+ - run: ruff check --output-format=github .
29
+ # Belt and braces: fails the build on any syntax newer than the floor,
30
+ # even if pyproject's target-version is edited by accident.
31
+ - run: ruff check --target-version py312 --output-format=github .
32
+ - run: ruff format --check .
33
+ - run: mypy
34
+
35
+ test:
36
+ runs-on: ${{ matrix.os }}
37
+ strategy:
38
+ fail-fast: false
39
+ matrix:
40
+ os: [ubuntu-latest, windows-latest, macos-latest]
41
+ python-version: ["3.12", "3.13", "3.14"]
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ - uses: actions/setup-python@v5
45
+ with:
46
+ python-version: ${{ matrix.python-version }}
47
+ cache: pip
48
+ cache-dependency-path: pyproject.toml
49
+ - run: pip install -e . --group dev
50
+ - run: pytest
51
+
52
+ bare:
53
+ # The zero-dependency promise, checked rather than asserted: the core
54
+ # installs and its suite passes with no matplotlib and no scipy. The
55
+ # tests that need either skip themselves, so a motif family sneaking a
56
+ # hard dependency in shows up here as a failure, not as a heavier wheel.
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+ - uses: actions/setup-python@v5
61
+ with:
62
+ python-version: "3.12"
63
+ cache: pip
64
+ cache-dependency-path: pyproject.toml
65
+ - run: pip install -e . pytest
66
+ - name: Confirm the optional dependencies really are absent
67
+ run: |
68
+ ! pip list --format=freeze | grep -E '^(matplotlib|scipy)=='
69
+ - run: pytest
70
+
71
+ plugin:
72
+ # The third-party contract, checked rather than asserted: a package that
73
+ # declares a geomotif.motifs entry point is discovered, described and
74
+ # rendered by an installation that knows nothing about it. The suite
75
+ # simulates this; only a real install proves the packaging half.
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+ - uses: actions/setup-python@v5
80
+ with:
81
+ python-version: "3.12"
82
+ cache: pip
83
+ cache-dependency-path: pyproject.toml
84
+ - run: pip install -e .
85
+ # --no-deps deliberately: the plugin depends on a published geomotif,
86
+ # and what is being tested is this checkout.
87
+ - run: pip install --no-deps ./examples/plugin
88
+ - name: The plugin's motif is discovered, described and rendered
89
+ run: |
90
+ geomotif list --family curve | grep -q superformula
91
+ geomotif show superformula | grep -q Gielis
92
+ geomotif render superformula --m 5 --out sf.svg
93
+ test -s sf.svg
94
+
95
+ docs:
96
+ # The documentation is generated from the registry, so it can only be wrong
97
+ # in two ways: the build fails, or the committed half of it -- the
98
+ # catalogue and the README's images, which GitHub renders without ever
99
+ # running mkdocs -- has fallen behind. Both are checked here.
100
+ runs-on: ubuntu-latest
101
+ steps:
102
+ - uses: actions/checkout@v4
103
+ - uses: actions/setup-python@v5
104
+ with:
105
+ python-version: "3.12"
106
+ cache: pip
107
+ cache-dependency-path: pyproject.toml
108
+ # The extras are installed so that the four scipy motifs render; without
109
+ # them the gallery would quietly document itself as incomplete.
110
+ - run: pip install -e '.[all]' --group docs
111
+ - run: python tools/gendocs.py
112
+ - name: The committed documentation is current
113
+ run: |
114
+ git add --intent-to-add -- docs/catalogue.md docs/assets
115
+ git diff --exit-code -- docs/catalogue.md docs/assets \
116
+ || { echo "Run 'make docs-gen' and commit the result."; exit 1; }
117
+ - run: mkdocs build --strict
118
+
119
+ build:
120
+ runs-on: ubuntu-latest
121
+ needs: [lint, test]
122
+ steps:
123
+ - uses: actions/checkout@v4
124
+ - uses: actions/setup-python@v5
125
+ with:
126
+ python-version: "3.14"
127
+ cache: pip
128
+ cache-dependency-path: pyproject.toml
129
+ - run: pip install build twine
130
+ - run: python -m build
131
+ - run: twine check dist/*
@@ -0,0 +1,50 @@
1
+ name: Docs
2
+
3
+ # The site is rebuilt from the code on every push to main. Nothing about it is
4
+ # committed except the catalogue and the README's images, so "deploy" and
5
+ # "regenerate" are the same action and the published pages cannot describe a
6
+ # version of the library that no longer exists.
7
+
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+ pages: write
16
+ id-token: write
17
+
18
+ # One deployment at a time, and never cancel one that is in flight: a
19
+ # half-published site is worse than a slightly stale one.
20
+ concurrency:
21
+ group: pages
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ build:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+ cache: pip
33
+ cache-dependency-path: pyproject.toml
34
+ # With the extras, so the four scipy motifs are rendered rather than
35
+ # documented as unavailable.
36
+ - run: pip install -e '.[all]' --group docs
37
+ - run: mkdocs build --strict
38
+ - uses: actions/upload-pages-artifact@v3
39
+ with:
40
+ path: site
41
+
42
+ deploy:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: github-pages
47
+ url: ${{ steps.deployment.outputs.page_url }}
48
+ steps:
49
+ - id: deployment
50
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,67 @@
1
+ name: Publish
2
+
3
+ # Tag push publishes to PyPI through trusted publishing: PyPI verifies the
4
+ # workflow's OIDC identity directly, so there is no API token in this
5
+ # repository's secrets to leak, rotate or forget.
6
+ #
7
+ # Configure it once at https://pypi.org/manage/project/geomotif/settings/publishing/
8
+ # with owner `pianosuki`, repository `geomotif`, workflow `publish.yml` and
9
+ # environment `pypi`.
10
+
11
+ on:
12
+ push:
13
+ tags: ["v*"]
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ build:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+ cache: pip
28
+ cache-dependency-path: pyproject.toml
29
+
30
+ - name: The tag and the package agree about the version
31
+ # A tag is easy to push before the bump and impossible to move after
32
+ # PyPI has accepted the file it names.
33
+ if: startsWith(github.ref, 'refs/tags/')
34
+ run: |
35
+ python - <<'PY'
36
+ import os, pathlib, re, sys
37
+ tag = os.environ["GITHUB_REF_NAME"].removeprefix("v")
38
+ source = pathlib.Path("src/geomotif/__init__.py").read_text()
39
+ version = re.search(r'^__version__ = "([^"]+)"', source, re.M).group(1)
40
+ if tag != version:
41
+ sys.exit(f"tag {tag!r} does not match __version__ {version!r}")
42
+ print(f"publishing {version}")
43
+ PY
44
+
45
+ - run: pip install build twine
46
+ - run: python -m build
47
+ - run: twine check --strict dist/*
48
+ - uses: actions/upload-artifact@v4
49
+ with:
50
+ name: dist
51
+ path: dist/
52
+
53
+ publish:
54
+ needs: build
55
+ runs-on: ubuntu-latest
56
+ environment:
57
+ name: pypi
58
+ url: https://pypi.org/p/geomotif
59
+ permissions:
60
+ # The whole point: this is what PyPI checks instead of a token.
61
+ id-token: write
62
+ steps:
63
+ - uses: actions/download-artifact@v4
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Environments
10
+ .venv/
11
+ venv/
12
+
13
+ # Tooling caches
14
+ .pytest_cache/
15
+ .mypy_cache/
16
+ .ruff_cache/
17
+ .coverage
18
+ htmlcov/
19
+
20
+ # Editors
21
+ .idea/
22
+ .vscode/
23
+
24
+ # Demo output
25
+ spiral-demo.png
26
+
27
+ # Documentation regenerated from the code on every build by tools/gendocs.py.
28
+ # docs/catalogue.md and docs/assets/ are deliberately *not* here: GitHub renders
29
+ # the README without running mkdocs, so those two are committed and drift-checked.
30
+ docs/gallery/
31
+ docs/reference/
32
+ site/
@@ -0,0 +1,24 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.16.0
4
+ hooks:
5
+ - id: ruff-check
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: https://github.com/pre-commit/mirrors-mypy
10
+ rev: v2.3.0
11
+ hooks:
12
+ - id: mypy
13
+ additional_dependencies: ["matplotlib>=3.10"]
14
+ args: []
15
+
16
+ - repo: https://github.com/pre-commit/pre-commit-hooks
17
+ rev: v6.0.0
18
+ hooks:
19
+ - id: check-toml
20
+ - id: check-yaml
21
+ - id: end-of-file-fixer
22
+ - id: trailing-whitespace
23
+ - id: check-merge-conflict
24
+ - id: check-added-large-files