platenest 0.1.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 (35) hide show
  1. platenest-0.1.0/.gitattributes +1 -0
  2. platenest-0.1.0/.github/workflows/ci.yml +57 -0
  3. platenest-0.1.0/.github/workflows/release.yml +97 -0
  4. platenest-0.1.0/.gitignore +8 -0
  5. platenest-0.1.0/CHANGELOG.md +28 -0
  6. platenest-0.1.0/CONTRIBUTING.md +37 -0
  7. platenest-0.1.0/LICENSE +661 -0
  8. platenest-0.1.0/PKG-INFO +131 -0
  9. platenest-0.1.0/README.md +109 -0
  10. platenest-0.1.0/pyproject.toml +47 -0
  11. platenest-0.1.0/src/platenest/__init__.py +60 -0
  12. platenest-0.1.0/src/platenest/bambu3mf.py +340 -0
  13. platenest-0.1.0/src/platenest/pack.py +264 -0
  14. platenest-0.1.0/src/platenest/plate.py +107 -0
  15. platenest-0.1.0/src/platenest/preview.py +57 -0
  16. platenest-0.1.0/src/platenest/raster.py +104 -0
  17. platenest-0.1.0/src/platenest/types.py +127 -0
  18. platenest-0.1.0/src/platenest/zone.py +73 -0
  19. platenest-0.1.0/tests/shapes.py +151 -0
  20. platenest-0.1.0/tests/test_acceptance.py +103 -0
  21. platenest-0.1.0/tests/test_bambu3mf.py +152 -0
  22. platenest-0.1.0/tests/test_centre.py +43 -0
  23. platenest-0.1.0/tests/test_deferred_minors.py +91 -0
  24. platenest-0.1.0/tests/test_fill.py +41 -0
  25. platenest-0.1.0/tests/test_imports.py +16 -0
  26. platenest-0.1.0/tests/test_pack.py +70 -0
  27. platenest-0.1.0/tests/test_plate.py +67 -0
  28. platenest-0.1.0/tests/test_progress.py +49 -0
  29. platenest-0.1.0/tests/test_raster.py +56 -0
  30. platenest-0.1.0/tests/test_release_tool.py +73 -0
  31. platenest-0.1.0/tests/test_review_fixes.py +116 -0
  32. platenest-0.1.0/tests/test_types.py +39 -0
  33. platenest-0.1.0/tests/test_zone.py +30 -0
  34. platenest-0.1.0/tools/release.py +93 -0
  35. platenest-0.1.0/tools/smoke.py +24 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,57 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ workflow_dispatch:
8
+ # the release workflow runs all of this again on the tag before it publishes anything
9
+ workflow_call:
10
+
11
+ concurrency:
12
+ group: ci-${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ test:
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-24.04, windows-latest]
24
+ runs-on: ${{ matrix.os }}
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ - uses: actions/setup-python@v7
28
+ with:
29
+ python-version: "3.12"
30
+ - run: python -m pip install -e ".[dev]"
31
+ - run: ruff check .
32
+ - run: ruff format --check .
33
+ - run: pytest -q
34
+
35
+ build:
36
+ needs: test
37
+ runs-on: ubuntu-24.04
38
+ steps:
39
+ - uses: actions/checkout@v7
40
+ - uses: actions/setup-python@v7
41
+ with:
42
+ python-version: "3.12"
43
+ - run: python -m pip install build twine
44
+ - run: python -m build
45
+ - run: python -m twine check --strict dist/*
46
+ - name: The wheel installs and imports on its own
47
+ run: |
48
+ python -m venv /tmp/clean
49
+ /tmp/clean/bin/pip install -q dist/*.whl
50
+ cp tools/smoke.py /tmp/smoke.py
51
+ cd /tmp && /tmp/clean/bin/python smoke.py
52
+ - run: cd dist && sha256sum * > SHA256SUMS && cat SHA256SUMS
53
+ - uses: actions/upload-artifact@v7
54
+ with:
55
+ name: platenest-dist
56
+ path: dist/
57
+ if-no-files-found: error
@@ -0,0 +1,97 @@
1
+ name: Release
2
+
3
+ # A pushed `v<version>` tag publishes that version to GitHub and to PyPI:
4
+ #
5
+ # v0.2.0b1 / v0.2.0rc1 pre-release, from dev (or main), titled "platenest v0.2.0b1 (pre-release)"
6
+ # v0.2.0 stable, only from a commit on main, titled "platenest v0.2.0", marked latest
7
+ #
8
+ # Before anything is published: the whole CI (lint, tests on two OSes, the built packages), the tag equals
9
+ # `v` + project.version, and CHANGELOG.md has a non-empty `## [<version>]` section, which becomes the release notes.
10
+ # Tags are never moved or deleted: a broken release is followed by the next version, not replaced.
11
+ on:
12
+ push:
13
+ tags: ["v*"]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ concurrency:
19
+ group: release-${{ github.ref }}
20
+ cancel-in-progress: false
21
+
22
+ jobs:
23
+ checks:
24
+ uses: ./.github/workflows/ci.yml
25
+
26
+ prepare:
27
+ needs: checks
28
+ runs-on: ubuntu-24.04
29
+ outputs:
30
+ title: ${{ steps.prep.outputs.title }}
31
+ prerelease: ${{ steps.prep.outputs.prerelease }}
32
+ steps:
33
+ - uses: actions/checkout@v7
34
+ with:
35
+ fetch-depth: 0 # the branch history, to prove a stable tag is on main
36
+ - uses: actions/setup-python@v7
37
+ with:
38
+ python-version: "3.12"
39
+ - name: Check the tag and write the notes
40
+ id: prep
41
+ run: python tools/release.py prepare --tag "$GITHUB_REF_NAME" --notes notes.md >> "$GITHUB_OUTPUT"
42
+ - name: A stable release is cut from main
43
+ if: steps.prep.outputs.prerelease == 'false'
44
+ run: |
45
+ if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
46
+ echo "::error::$GITHUB_REF_NAME is a stable version but its commit is not on main"
47
+ exit 1
48
+ fi
49
+ - uses: actions/upload-artifact@v7
50
+ with:
51
+ name: release-notes
52
+ path: notes.md
53
+ if-no-files-found: error
54
+
55
+ # PyPI first: a version can be uploaded there only once, so if it refuses, no GitHub release claims it exists.
56
+ # Trusted publishing (OIDC): no API token anywhere. PyPI must know this repository, this workflow file and the
57
+ # "pypi" environment as the publisher of the project — configured once on pypi.org.
58
+ publish-pypi:
59
+ needs: prepare
60
+ runs-on: ubuntu-24.04
61
+ environment:
62
+ name: pypi
63
+ url: https://pypi.org/project/platenest/
64
+ permissions:
65
+ id-token: write
66
+ steps:
67
+ - uses: actions/download-artifact@v8
68
+ with:
69
+ name: platenest-dist
70
+ path: dist
71
+ - run: rm -f dist/SHA256SUMS
72
+ - uses: pypa/gh-action-pypi-publish@release/v1
73
+
74
+ github-release:
75
+ needs: [prepare, publish-pypi]
76
+ runs-on: ubuntu-24.04
77
+ permissions:
78
+ contents: write # create the release and attach its files
79
+ steps:
80
+ - uses: actions/download-artifact@v8
81
+ with:
82
+ name: platenest-dist
83
+ path: dist
84
+ - uses: actions/download-artifact@v8
85
+ with:
86
+ name: release-notes
87
+ path: .
88
+ - name: Publish
89
+ env:
90
+ GH_TOKEN: ${{ github.token }}
91
+ TITLE: ${{ needs.prepare.outputs.title }}
92
+ PRERELEASE: ${{ needs.prepare.outputs.prerelease }}
93
+ run: |
94
+ (cd dist && sha256sum -c SHA256SUMS)
95
+ if [ "$PRERELEASE" = "true" ]; then kind=(--prerelease --latest=false); else kind=(--latest); fi
96
+ gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" \
97
+ --verify-tag --title "$TITLE" --notes-file notes.md "${kind[@]}"
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ .superpowers/
3
+ __pycache__/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .ruff_cache/
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.1.0] - 2026-11-13
6
+
7
+ The first release.
8
+
9
+ - **The packer** (`from platenest import pack`). Parts are placed by their real top-down outline, so a small part
10
+ goes into the hole of a frame, the mouth of a U or between the arms of a bracket. You give meshes, quantities, a
11
+ work zone (plate size, areas parts may or may not be in, a maximum height) and a spacing; you get a plate index,
12
+ an offset and a rotation for every copy. Six combinations of ordering and direction are tried and the one with the
13
+ fewest plates is kept.
14
+ - **The spacing you ask for is the least you get** between any two parts, measured on the real outlines; half of it
15
+ is kept to the edge of the work zone and to excluded areas.
16
+ - **Fill mode**: a part marked `fill` gets a plate of its own with as many copies as fit, and the result says how
17
+ many that was.
18
+ - **Nothing is dropped silently.** A part with no top-down area, one taller than the zone allows or one too large
19
+ for an empty plate comes back in `rejected` with its reason, and the rest is still packed. A mesh in the wrong
20
+ units is refused at once instead of being rasterised first.
21
+ - The group on each plate can be centred in the work zone; a callback reports progress, another cancels a run;
22
+ `render_plate` draws a top view of a plate.
23
+ - **`platenest.bambu3mf`**, a separate module that needs only the standard library: `write_project` turns meshes,
24
+ placements and your own project settings into a multi-plate Bambu Studio / OrcaSlicer project 3MF that slices
25
+ without anything being re-arranged, and `read_project` reads such a file back. A file that is not one of ours,
26
+ is damaged, or unpacks larger than a limit you set is a `ValueError`.
27
+ - The same input gives the same placements and a byte-identical 3MF.
28
+ - No printer or slicer data is bundled: the work zone and the settings are always yours.
@@ -0,0 +1,37 @@
1
+ # Contributing
2
+
3
+ ## Branches
4
+
5
+ - `dev` — where work lands. Branch from `dev` and open pull requests against `dev`.
6
+ - `main` — released code only; it moves forward from `dev` when a stable version is released.
7
+
8
+ ## Before a pull request
9
+
10
+ ```bash
11
+ pip install -e ".[dev]"
12
+ ruff check . && ruff format --check .
13
+ pytest
14
+ ```
15
+
16
+ - The packer depends on `numpy` and `opencv-python-headless` and nothing else; `platenest.bambu3mf` on the standard
17
+ library only. Tests pin both.
18
+ - No printer or slicer data goes into the package: work zones and settings are the caller's.
19
+ - The implementation is original. Do not port code from other packers; several are under licences this project
20
+ cannot take.
21
+ - Test fixtures are generated in code. Do not commit model files.
22
+ - A behaviour change comes with a test that fails without it and a line in `CHANGELOG.md` under `[Unreleased]`.
23
+ - Conventional commits: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`.
24
+
25
+ ## Releases
26
+
27
+ Versions are `X.Y.Z` for stable releases and `X.Y.ZaN` / `X.Y.ZbN` / `X.Y.ZrcN` for pre-releases.
28
+
29
+ 1. Set `version` in `pyproject.toml` and rename `[Unreleased]` in `CHANGELOG.md` to `## [<version>] - <date>`
30
+ (leave a new empty `[Unreleased]` above it).
31
+ 2. Merge to `dev`, wait for CI to be green there. For a stable version, fast-forward `main` to `dev`.
32
+ 3. Tag that commit `v<version>` and push the tag.
33
+
34
+ The tag runs `.github/workflows/release.yml`: the whole CI again, then `tools/release.py` checks that the tag equals
35
+ the package version and that the changelog has a section for it, a stable tag must be on `main`, and only then the
36
+ wheel and the source archive are attached to a GitHub release and published to PyPI through trusted publishing — no
37
+ token is stored anywhere. Tags are never moved or deleted: a broken release is followed by the next version.