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.
- platenest-0.1.0/.gitattributes +1 -0
- platenest-0.1.0/.github/workflows/ci.yml +57 -0
- platenest-0.1.0/.github/workflows/release.yml +97 -0
- platenest-0.1.0/.gitignore +8 -0
- platenest-0.1.0/CHANGELOG.md +28 -0
- platenest-0.1.0/CONTRIBUTING.md +37 -0
- platenest-0.1.0/LICENSE +661 -0
- platenest-0.1.0/PKG-INFO +131 -0
- platenest-0.1.0/README.md +109 -0
- platenest-0.1.0/pyproject.toml +47 -0
- platenest-0.1.0/src/platenest/__init__.py +60 -0
- platenest-0.1.0/src/platenest/bambu3mf.py +340 -0
- platenest-0.1.0/src/platenest/pack.py +264 -0
- platenest-0.1.0/src/platenest/plate.py +107 -0
- platenest-0.1.0/src/platenest/preview.py +57 -0
- platenest-0.1.0/src/platenest/raster.py +104 -0
- platenest-0.1.0/src/platenest/types.py +127 -0
- platenest-0.1.0/src/platenest/zone.py +73 -0
- platenest-0.1.0/tests/shapes.py +151 -0
- platenest-0.1.0/tests/test_acceptance.py +103 -0
- platenest-0.1.0/tests/test_bambu3mf.py +152 -0
- platenest-0.1.0/tests/test_centre.py +43 -0
- platenest-0.1.0/tests/test_deferred_minors.py +91 -0
- platenest-0.1.0/tests/test_fill.py +41 -0
- platenest-0.1.0/tests/test_imports.py +16 -0
- platenest-0.1.0/tests/test_pack.py +70 -0
- platenest-0.1.0/tests/test_plate.py +67 -0
- platenest-0.1.0/tests/test_progress.py +49 -0
- platenest-0.1.0/tests/test_raster.py +56 -0
- platenest-0.1.0/tests/test_release_tool.py +73 -0
- platenest-0.1.0/tests/test_review_fixes.py +116 -0
- platenest-0.1.0/tests/test_types.py +39 -0
- platenest-0.1.0/tests/test_zone.py +30 -0
- platenest-0.1.0/tools/release.py +93 -0
- 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,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.
|