weldbox 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.
- weldbox-0.1.0/.github/workflows/ci.yml +15 -0
- weldbox-0.1.0/.github/workflows/publish.yml +34 -0
- weldbox-0.1.0/.gitignore +7 -0
- weldbox-0.1.0/LICENSE +21 -0
- weldbox-0.1.0/PKG-INFO +13 -0
- weldbox-0.1.0/PRD.md +49 -0
- weldbox-0.1.0/README.md +146 -0
- weldbox-0.1.0/docs/FUNCTIONALITY.md +323 -0
- weldbox-0.1.0/docs/images/weldbox_sample_1.jpg +0 -0
- weldbox-0.1.0/docs/images/weldbox_sample_2.jpg +0 -0
- weldbox-0.1.0/docs/samples/fabtech/material_list.md +1 -0
- weldbox-0.1.0/docs/samples/oshcut/material_list.md +1 -0
- weldbox-0.1.0/docs/samples/rfmg/material_list.md +204 -0
- weldbox-0.1.0/docs/samples/rfmg/pipe_1.25_sch40.step +131 -0
- weldbox-0.1.0/docs/samples/rfmg/pipe_1.5_sch40.step +132 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_2x1x0.125.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_3x1.5x0.083.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_3x2x0.125.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_4x2x0.120.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_4x2x0.188.step +1517 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_4x3x0.250.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_al_2x1x0.065.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/rect_al_3x1x0.125.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/round_2x0.125.step +131 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.25x0.065.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.25x0.083.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.5x0.083.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.5x0.095.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.5x0.120.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.75x0.095.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1.75x0.120.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1x0.049.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_4130_1x0.065.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_al_1.5x0.065.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_al_1.5x0.125.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_al_1x0.065.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_al_2x0.250.step +131 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_1.75x0.065.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_1.75x0.083.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_1.75x0.095.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_1.75x0.120.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_1.75x0.156.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_2x0.083.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_2x0.095.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_dom_2x0.120.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_ss304_1.5x0.065.step +245 -0
- weldbox-0.1.0/docs/samples/rfmg/round_ss304_1x0.065.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_ss304_1x0.083.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/round_ss304_2x0.065.step +244 -0
- weldbox-0.1.0/docs/samples/rfmg/shipping.md +31 -0
- weldbox-0.1.0/docs/samples/rfmg/square_1.5x1.5x0.083.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_1.5x1.5x0.120.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_1.75x1.75x0.083.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_1x1x0.0625.step +782 -0
- weldbox-0.1.0/docs/samples/rfmg/square_1x1x0.065.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2.5x2.5x0.250.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.083.step +1517 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.095.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.120.step +1517 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.125.step +782 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.1875.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_2x2x0.25.step +1517 -0
- weldbox-0.1.0/docs/samples/rfmg/square_3x3x0.120.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_3x3x0.1875.step +1518 -0
- weldbox-0.1.0/docs/samples/rfmg/square_4x4x0.125.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/square_al_1x1x0.065.step +782 -0
- weldbox-0.1.0/docs/samples/rfmg/square_al_1x1x0.125.step +782 -0
- weldbox-0.1.0/docs/samples/rfmg/square_al_3x3x0.125.step +781 -0
- weldbox-0.1.0/docs/samples/rfmg/square_ss304_1.5x1.5x0.065.step +782 -0
- weldbox-0.1.0/docs/samples/rfmg/square_ss304_1x1x0.065.step +782 -0
- weldbox-0.1.0/docs/samples/sendcutsend/.gitkeep +0 -0
- weldbox-0.1.0/docs/samples/sendcutsend/material_list.md +0 -0
- weldbox-0.1.0/examples/epoxy_machine_cell.yaml +25 -0
- weldbox-0.1.0/examples/winding_machine_cell.yaml +45 -0
- weldbox-0.1.0/examples/winding_machine_cell_1x1.yaml +45 -0
- weldbox-0.1.0/pyproject.toml +31 -0
- weldbox-0.1.0/src/weldbox/__init__.py +3 -0
- weldbox-0.1.0/src/weldbox/blocking.py +188 -0
- weldbox-0.1.0/src/weldbox/catalog.py +153 -0
- weldbox-0.1.0/src/weldbox/cli.py +98 -0
- weldbox-0.1.0/src/weldbox/consolidate.py +116 -0
- weldbox-0.1.0/src/weldbox/dedupe.py +73 -0
- weldbox-0.1.0/src/weldbox/features.py +281 -0
- weldbox-0.1.0/src/weldbox/frame.py +352 -0
- weldbox-0.1.0/src/weldbox/generate.py +180 -0
- weldbox-0.1.0/src/weldbox/geometry/__init__.py +0 -0
- weldbox-0.1.0/src/weldbox/geometry/assembly.py +177 -0
- weldbox-0.1.0/src/weldbox/geometry/cutters.py +106 -0
- weldbox-0.1.0/src/weldbox/geometry/member.py +49 -0
- weldbox-0.1.0/src/weldbox/geometry/profile.py +34 -0
- weldbox-0.1.0/src/weldbox/manifest.py +93 -0
- weldbox-0.1.0/src/weldbox/panels/__init__.py +0 -0
- weldbox-0.1.0/src/weldbox/panels/dxf.py +47 -0
- weldbox-0.1.0/src/weldbox/panels/layout.py +178 -0
- weldbox-0.1.0/src/weldbox/shipping.py +89 -0
- weldbox-0.1.0/src/weldbox/spec.py +174 -0
- weldbox-0.1.0/src/weldbox/units.py +78 -0
- weldbox-0.1.0/src/weldbox/vendors/__init__.py +3 -0
- weldbox-0.1.0/src/weldbox/vendors/base.py +99 -0
- weldbox-0.1.0/src/weldbox/vendors/data/fabtech.yaml +2 -0
- weldbox-0.1.0/src/weldbox/vendors/data/oshcut.yaml +2 -0
- weldbox-0.1.0/src/weldbox/vendors/data/rfmg.yaml +70 -0
- weldbox-0.1.0/src/weldbox/wizard.py +178 -0
- weldbox-0.1.0/tests/fixtures/winding_machine_cell.yaml +45 -0
- weldbox-0.1.0/tests/test_acceptance_winding_cell.py +111 -0
- weldbox-0.1.0/tests/test_catalog.py +55 -0
- weldbox-0.1.0/tests/test_consolidate.py +75 -0
- weldbox-0.1.0/tests/test_dedupe.py +108 -0
- weldbox-0.1.0/tests/test_features.py +139 -0
- weldbox-0.1.0/tests/test_frame.py +257 -0
- weldbox-0.1.0/tests/test_geometry.py +138 -0
- weldbox-0.1.0/tests/test_panels.py +151 -0
- weldbox-0.1.0/tests/test_shipping.py +79 -0
- weldbox-0.1.0/tests/test_spec.py +60 -0
- weldbox-0.1.0/tests/test_units.py +34 -0
- weldbox-0.1.0/uv.lock +1296 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
fast-tests:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
- name: Run fast tests (frame math, features, dedupe — no CAD kernel)
|
|
15
|
+
run: uv run pytest -m "not slow" -q
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
- name: Build sdist and wheel
|
|
15
|
+
run: uv build
|
|
16
|
+
- uses: actions/upload-artifact@v4
|
|
17
|
+
with:
|
|
18
|
+
name: dist
|
|
19
|
+
path: dist/
|
|
20
|
+
|
|
21
|
+
publish:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
environment:
|
|
25
|
+
name: pypi
|
|
26
|
+
url: https://pypi.org/p/weldbox
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write # OIDC for PyPI trusted publishing — no API token needed
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/download-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
weldbox-0.1.0/.gitignore
ADDED
weldbox-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Texabama Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
weldbox-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weldbox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate vendor-ready tube laser cut lists (STEP) and sheet panel DXFs for welded square-tube boxes
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: <3.13,>=3.10
|
|
7
|
+
Requires-Dist: build123d>=0.8
|
|
8
|
+
Requires-Dist: ezdxf>=1.3
|
|
9
|
+
Requires-Dist: pydantic>=2.7
|
|
10
|
+
Requires-Dist: pyyaml>=6.0
|
|
11
|
+
Requires-Dist: questionary>=2.0
|
|
12
|
+
Requires-Dist: rich>=13.0
|
|
13
|
+
Requires-Dist: typer>=0.12
|
weldbox-0.1.0/PRD.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Tube Box Generator
|
|
2
|
+
|
|
3
|
+
Many laser cutting services are exposing tube cutting as a service. Tubes, particularly square tube can be cut to length so that they can be welded together to form machinery boxes/cabinet bases.
|
|
4
|
+
|
|
5
|
+
Services that expose this service include:
|
|
6
|
+
|
|
7
|
+
https://www.fabworks.com/services/tube-laser-cutting
|
|
8
|
+
https://tube.oshcut.com/
|
|
9
|
+
https://www.rmfg.com/laser-tube-cutting
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
I would like to be able to spec the outer dimentions of a box (larger than 12" x 12" x 12") and have the pieces automatically generated as a cut list (multiple step files) for easy upload to the services. I have attached step and/or dfx patterns for RFMG and OshCut in /docs/samples/<vendor_name>/**
|
|
13
|
+
|
|
14
|
+
I have also put tables (partial) of the materials offered as a markdown file in /docs/samples/<vendor_name>/material_list.md
|
|
15
|
+
|
|
16
|
+
I think the experience would be improved by a user interface, but a terminal interface with questions like inclusion of gussets, or additional supports (blocking?) would be helpful. We might also want to spec sheet metal siding that can easily be tack welded or rivited (we pre-cut the rivet holes in the tube for sheet metal adhesion).
|
|
17
|
+
|
|
18
|
+
We have freecad installed, I'm ok with leveraging that as a partial visual for the final assembly. If possible, I would like to avoid creating a 3d viewer in javascript - that seems like a waste of time.
|
|
19
|
+
|
|
20
|
+
We want to have some some tab/slot setup to improve assembly and welding:
|
|
21
|
+
|
|
22
|
+
In tube laser cutting, tab-and-slot connections (or hook-in-slot designs) allow two pieces of metal to interlock and self-locate. This eliminates the need for expensive jigs, clamps, and manual layout during assembly. Tabs are typically designed to be slightly smaller than the slot width to create an easy slip-fit for welding.Design Best PracticesClearance: To achieve a proper slip fit, make your slot's width and length at least 0.010 inches (or roughly 0.25 mm) larger than the thickness of the mating tab.Radius and Corner Relief: Right angles and square corners create localized stress concentration points in the metal tubing. Always incorporate small radii or "dog-bone" reliefs in the corners of your slots to prevent cracking during use.Self-Fixturing: Design the tabs so the parts interlock perfectly at the correct angle (e.g., 90° for frames). This allows the connected tubes to hold themselves in position while being tack-welded.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Example Use Case:
|
|
26
|
+
|
|
27
|
+
Name: Winding Machine Cell
|
|
28
|
+
Vendor: RFMG
|
|
29
|
+
Material: 1.5 x 1.5 x .120 in Square Tube Square 1.5" × 1.5" wall-thickness:0.12 corner-radus:0.24
|
|
30
|
+
Exterior Size:
|
|
31
|
+
height: 2000mm
|
|
32
|
+
width: 1000mm
|
|
33
|
+
depth: 800mm
|
|
34
|
+
Blocking:
|
|
35
|
+
a. perpedicular to height axis at 1000mm ("work surface"). 3x evenly spaced blocking of work surface extending from front to back(depth)
|
|
36
|
+
b. vertical supports between base and this at midpoints
|
|
37
|
+
c. midpoint of top extending across the width
|
|
38
|
+
d. midpoint of bottom extending across the width
|
|
39
|
+
|
|
40
|
+
Sheet metal siding:
|
|
41
|
+
attachment: rivet 100mm spacing 1/4" rivet
|
|
42
|
+
pieces:
|
|
43
|
+
a. sides (stainless - 0.038")
|
|
44
|
+
b. back (stainless - 0.038")
|
|
45
|
+
quantity: 5
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
weldbox-0.1.0/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# weldbox
|
|
2
|
+
|
|
3
|
+
Vendor-ready tube-laser cut lists for jigless welded frames — STEP + DXF
|
|
4
|
+
from a YAML spec.
|
|
5
|
+
|
|
6
|
+
| Enclosure with riveted siding (`full_height_posts`) | Bench frame (`top_bottom_frames`) |
|
|
7
|
+
|---|---|
|
|
8
|
+
|  |  |
|
|
9
|
+
|
|
10
|
+
Generate laser tube cut lists for welded square-tube boxes and machine
|
|
11
|
+
bases. From a single YAML spec, weldbox produces:
|
|
12
|
+
|
|
13
|
+
- **`parts/*.step`** — one STEP file per unique tube part (deduplicated with
|
|
14
|
+
quantities), ready to upload to a tube laser service (RFMG, OshCut, Fabworks)
|
|
15
|
+
- **`panels/*.dxf`** — flat patterns for riveted sheet-metal siding, with
|
|
16
|
+
rivet holes that match the holes pre-cut in the tubes
|
|
17
|
+
- **`assembly.step`** — the full assembly for visual review (open in FreeCAD)
|
|
18
|
+
- **`cutlist.md` / `cutlist.csv`** — the manifest with lengths and quantities
|
|
19
|
+
|
|
20
|
+
Frames are self-fixturing: tee joints get through-wall tab-and-slot features
|
|
21
|
+
(slip fit +0.25mm, dog-bone corner reliefs per vendor best practice) so parts
|
|
22
|
+
interlock at 90° for tack welding without jigs.
|
|
23
|
+
|
|
24
|
+
**[Read the functionality deep dive →](docs/FUNCTIONALITY.md)** — topologies,
|
|
25
|
+
blocking primitives, the tab/slot system, part-count consolidation, siding,
|
|
26
|
+
shipping checks, and the full spec reference.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
uv sync
|
|
32
|
+
|
|
33
|
+
# browse a vendor's tube catalog
|
|
34
|
+
uv run weldbox catalog list --vendor rfmg --shape square
|
|
35
|
+
|
|
36
|
+
# check the cut list without generating CAD (fast)
|
|
37
|
+
uv run weldbox generate examples/winding_machine_cell.yaml --dry-run
|
|
38
|
+
|
|
39
|
+
# full generation
|
|
40
|
+
uv run weldbox generate examples/winding_machine_cell.yaml -o out
|
|
41
|
+
open out/winding-machine-cell/assembly.step # opens in FreeCAD
|
|
42
|
+
|
|
43
|
+
# author a new spec interactively
|
|
44
|
+
uv run weldbox wizard
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## The spec file
|
|
48
|
+
|
|
49
|
+
The YAML spec is the source of truth (the wizard just writes one). Lengths
|
|
50
|
+
accept unit suffixes (`2000mm`, `1.5in`, `0.038"`, `1/4in`); bare numbers are
|
|
51
|
+
millimetres. See `examples/winding_machine_cell.yaml` for a complete example:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
name: Winding Machine Cell
|
|
55
|
+
vendor: rfmg
|
|
56
|
+
material: {shape: square, size: [1.5in], wall: 0.120in, family: A500}
|
|
57
|
+
exterior: {height: 2000mm, width: 1000mm, depth: 800mm}
|
|
58
|
+
topology: full_height_posts
|
|
59
|
+
blocking:
|
|
60
|
+
- type: level # horizontal frame; top surface at `height`
|
|
61
|
+
name: work-surface
|
|
62
|
+
height: 1000mm
|
|
63
|
+
cross_members: {count: 3, axis: depth}
|
|
64
|
+
- type: supports # verticals between two layers, at rail midpoints
|
|
65
|
+
between: [base, work-surface]
|
|
66
|
+
- type: spanner # single member across a face
|
|
67
|
+
face: top
|
|
68
|
+
axis: width
|
|
69
|
+
siding:
|
|
70
|
+
attachment: {method: rivet, rivet: 0.25in, spacing: 100mm}
|
|
71
|
+
panels:
|
|
72
|
+
- {faces: [left, right, back], material: {alloy: "304", thickness: 0.038"}}
|
|
73
|
+
quantity: 5
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Conventions
|
|
77
|
+
|
|
78
|
+
- Box coordinates: X = width, Y = depth, Z = height; the exterior dimensions
|
|
79
|
+
are outer envelope dimensions.
|
|
80
|
+
- `full_height_posts`: 4 posts run full height; all rails butt between posts.
|
|
81
|
+
- `level` heights place the level's **top surface** at the given height (it
|
|
82
|
+
is a work surface); use `height_ref: centerline|bottom_face` to override.
|
|
83
|
+
- Rivet holes: rivet diameter + 0.15mm clearance, evenly pitched at
|
|
84
|
+
`spacing`, on every member whose outer face lies on a sided box face.
|
|
85
|
+
Panel DXF holes are derived from the same list, so they always line up.
|
|
86
|
+
- All joints get tab/slot by default, including box corners — corner slots
|
|
87
|
+
that land flush with a post end become open hook-in notches. Disable with
|
|
88
|
+
`joints: {corner_tabs: false}`. `joints.weld_gap` shortens butting ends.
|
|
89
|
+
- Sheet panels get a `siding.corner_radius` (default 5mm) applied to the
|
|
90
|
+
DXF outline and the assembly solids; panels carry their rivet holes in 3D.
|
|
91
|
+
- Same-size panels consolidate into one flat part (vendors price multiples
|
|
92
|
+
cheaper): a sheet with through-holes can be flipped or rotated 180 in
|
|
93
|
+
plane, so left/right, front/back, and top/bottom pairs share one DXF
|
|
94
|
+
(e.g. `left-right.dxf`, qty 2). When hole patterns differ, the union is
|
|
95
|
+
cut into both so the blank stays reversible; conflicting patterns stay
|
|
96
|
+
separate parts.
|
|
97
|
+
- In the assembly STEP, members are colored by role (posts slate, rails
|
|
98
|
+
gray, level rails teal, crosses orange, supports green, spanners purple)
|
|
99
|
+
and panels are translucent. The assembly is written as flat named
|
|
100
|
+
products (one per member/panel) with colors on every face, which FreeCAD
|
|
101
|
+
imports as individually selectable/hideable colored objects regardless
|
|
102
|
+
of its STEP import preferences.
|
|
103
|
+
- `consolidate: true` (default) adds sacrificial slots/holes so same-length
|
|
104
|
+
members collapse into one part number — the example box goes from 13
|
|
105
|
+
unique parts to 3. Set `consolidate: false` if you don't want unused
|
|
106
|
+
cuts on visible faces.
|
|
107
|
+
|
|
108
|
+
## Vendors
|
|
109
|
+
|
|
110
|
+
`rfmg` is fully encoded (from `docs/samples/rfmg/material_list.md`); `oshcut`
|
|
111
|
+
and `fabtech` are stubs awaiting material lists. Vendor shipping rules
|
|
112
|
+
(RFMG's LTL freight thresholds, see `docs/samples/rfmg/shipping.md`) drive
|
|
113
|
+
an order-level estimate: the generator weighs the cut list analytically and
|
|
114
|
+
warns — in the console and on `cutlist.md` — when a part dimension or the
|
|
115
|
+
order weight will trigger freight, with the specific reasons and the flat
|
|
116
|
+
surcharge. Vendor catalogs live in
|
|
117
|
+
`src/weldbox/vendors/data/*.yaml` — dimensions in inches as published, with
|
|
118
|
+
corner radius falling back to 2 x wall where unpublished.
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
uv run pytest -m "not slow" # fast tests: frame math, features, dedupe (<1s)
|
|
124
|
+
uv run pytest -m slow # geometry tests (build123d/OpenCascade)
|
|
125
|
+
uv run pytest # everything
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Pipeline: `spec.py` (YAML schema) → `frame.py`/`blocking.py` (member/joint
|
|
129
|
+
math, no CAD) → `features.py` (tab/slot/hole placement, no CAD) →
|
|
130
|
+
`dedupe.py` (symmetry-aware part grouping) → `geometry/` (build123d solids,
|
|
131
|
+
STEP) + `panels/` (DXF) → `manifest.py`.
|
|
132
|
+
|
|
133
|
+
All dimension and placement math is CAD-free and covered by fast tests;
|
|
134
|
+
geometry tests verify solids against analytic volumes and RFMG's published
|
|
135
|
+
stock STEP files, and assert zero interference at assembled joints.
|
|
136
|
+
|
|
137
|
+
## License and attribution
|
|
138
|
+
|
|
139
|
+
weldbox is released under the [MIT License](LICENSE).
|
|
140
|
+
|
|
141
|
+
The RFMG reference data in `docs/samples/rfmg/` (tube profile
|
|
142
|
+
specifications, stock STEP/DXF geometry, and shipping thresholds) is
|
|
143
|
+
published by RMFG as design-against reference material for their laser tube
|
|
144
|
+
cutting service: <https://www.rmfg.com/docs/services/laser-tube-cutting>.
|
|
145
|
+
It is included here for interoperability and remains theirs; weldbox is not
|
|
146
|
+
affiliated with or endorsed by RMFG.
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# weldbox — Functionality Deep Dive
|
|
2
|
+
|
|
3
|
+
weldbox turns a short YAML description of a welded square-tube box — a
|
|
4
|
+
machine base, enclosure, cart, bench, or cabinet frame — into everything a
|
|
5
|
+
laser tube cutting service needs to make the parts, and everything you need
|
|
6
|
+
to weld them together without a jig.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
spec.yaml ──▶ frame math ──▶ features ──▶ consolidation ──▶ outputs
|
|
10
|
+
(source of members + tabs/slots/ fewer unique STEP per part
|
|
11
|
+
truth) joints rivet holes part numbers DXF per panel
|
|
12
|
+
assembly STEP
|
|
13
|
+
cut list + shipping
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Everything up to geometry is pure math (no CAD kernel), so `--dry-run`
|
|
17
|
+
gives you a complete priced-out cut list in under a second.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. The workflow
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
uv run weldbox catalog list --vendor rfmg # 1. pick stock
|
|
25
|
+
uv run weldbox wizard # 2. author a spec (or write YAML by hand)
|
|
26
|
+
uv run weldbox generate my-box.yaml --dry-run # 3. sanity-check the cut list + shipping
|
|
27
|
+
uv run weldbox generate my-box.yaml -o out # 4. produce files
|
|
28
|
+
open out/my-box/assembly.step # 5. review in FreeCAD
|
|
29
|
+
# 6. upload out/my-box/parts/*.step and panels/*.dxf to the vendor
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The YAML spec is the source of truth. The wizard is only a convenience that
|
|
33
|
+
writes one; you can always edit the file directly and regenerate.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. The spec file, field by field
|
|
38
|
+
|
|
39
|
+
All lengths accept unit suffixes — `2000mm`, `1.5in`, `0.038"`, `1/4in`,
|
|
40
|
+
`3 1/2 in`, `2cm`, `1m` — and bare numbers are millimetres. Unknown fields
|
|
41
|
+
are **rejected with an error**, never silently ignored, so a typo cannot
|
|
42
|
+
quietly change your build.
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
name: Epoxy Machine Cell # used for output folder and file names
|
|
46
|
+
vendor: rfmg # rfmg | oshcut | fabtech (stubs)
|
|
47
|
+
material: # catalog lookup key, not free-form:
|
|
48
|
+
shape: square # must match a profile the vendor stocks
|
|
49
|
+
size: [1.5in] # [w] or [w, h]
|
|
50
|
+
wall: 0.120in
|
|
51
|
+
family: A500 # disambiguates A500 vs 304 vs 6061 at the same size
|
|
52
|
+
exterior: # OUTER envelope of the frame
|
|
53
|
+
height: 860mm # Z
|
|
54
|
+
width: 2000mm # X
|
|
55
|
+
depth: 660mm # Y
|
|
56
|
+
topology: top_bottom_frames # see section 3
|
|
57
|
+
joints: # all optional; defaults shown
|
|
58
|
+
style: through_wall_tab # or plain_butt (no tabs anywhere)
|
|
59
|
+
slot_clearance: 0.25mm # slip fit: slot = tab + this
|
|
60
|
+
tab_width_fraction: 0.5 # tab width as fraction of the receiving flat
|
|
61
|
+
dogbone_radius: 1.0mm # corner relief circles in slots
|
|
62
|
+
weld_gap: 0mm # shortens every butting end
|
|
63
|
+
corner_tabs: true # tab/slot at box corners too
|
|
64
|
+
blocking: [...] # see section 4
|
|
65
|
+
siding: {...} # see section 5
|
|
66
|
+
quantity: 5 # assemblies; multiplies the cut list
|
|
67
|
+
consolidate: true # see section 6
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`material` must resolve to a real vendor profile — weldbox models the
|
|
71
|
+
vendor's actual stock (including the published outside corner radius) so
|
|
72
|
+
the STEP files you upload match what the service quotes against.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 3. Topologies — how the box is framed
|
|
77
|
+
|
|
78
|
+
### `full_height_posts`
|
|
79
|
+
|
|
80
|
+
Four posts run the full height; every horizontal rail butts between them.
|
|
81
|
+
The classic cabinet/enclosure frame — vertical loads go straight down the
|
|
82
|
+
posts to the floor.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
┌─post──────post─┐ posts: height H x4
|
|
86
|
+
│ ═══rails═══ │ rails: width − 2×tube
|
|
87
|
+
║ ║ depth − 2×tube
|
|
88
|
+
│ ═══rails═══ │
|
|
89
|
+
└─post──────post─┘
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `top_bottom_frames`
|
|
93
|
+
|
|
94
|
+
The top and bottom are complete rectangular frames whose **width rails run
|
|
95
|
+
solid across the full exterior width**; the depth rails butt between them,
|
|
96
|
+
and the posts butt *up into* the frames, tabbing into the full-width rails.
|
|
97
|
+
Right for benches and long tables where you want unbroken members across
|
|
98
|
+
the span (and two frames you can weld up flat on a table first).
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
╔════ solid width rail ════╗ top frame
|
|
102
|
+
║ post post post post ║ posts: height − 2×tube, tab up/down
|
|
103
|
+
╚════ solid width rail ════╝ bottom frame
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 4. Blocking — interior structure
|
|
109
|
+
|
|
110
|
+
Three primitives, freely combined in the `blocking:` list.
|
|
111
|
+
|
|
112
|
+
### `level` — a horizontal frame at any height
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
- type: level
|
|
116
|
+
name: work-surface # referenced by supports
|
|
117
|
+
height: 1000mm
|
|
118
|
+
height_ref: top_face # top_face (default) | centerline | bottom_face
|
|
119
|
+
cross_members: {count: 3, axis: depth}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A level is a full perimeter frame (4 rails teed into the posts) with
|
|
123
|
+
optional evenly spaced cross members butted between its rails.
|
|
124
|
+
`height_ref: top_face` means the *surface you'd put something on* is at the
|
|
125
|
+
given height — it's a work surface. Heights are validated against the box:
|
|
126
|
+
if a level can't fit, the error tells you the exact legal range for your
|
|
127
|
+
tube size and box height.
|
|
128
|
+
|
|
129
|
+
### `supports` — verticals between two layers
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
- type: supports
|
|
133
|
+
between: [base, work-surface] # any two of: base, top, or a level name
|
|
134
|
+
at: midpoints
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Places a vertical member at the midpoint of each of the four rail pairs of
|
|
138
|
+
the two layers, butted between the lower rails' top faces and the upper
|
|
139
|
+
rails' bottom faces — load paths under a work surface without blocking the
|
|
140
|
+
interior.
|
|
141
|
+
|
|
142
|
+
### `spanner` — members across the top or bottom face
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
- type: spanner
|
|
146
|
+
face: [top, bottom] # one face or a list
|
|
147
|
+
axis: depth # width | depth
|
|
148
|
+
count: 3 # evenly spaced at k/(count+1)
|
|
149
|
+
position: 0.5 # used only when count == 1
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Spanners butt between the two face rails perpendicular to their axis —
|
|
153
|
+
mounting rails for equipment, caster plates, or deck support.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 5. Sheet metal siding
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
siding:
|
|
161
|
+
attachment: {method: rivet, rivet: 1/4in, spacing: 100mm, hole_clearance: 0.15mm}
|
|
162
|
+
panels:
|
|
163
|
+
- {faces: [left, right, back], material: {alloy: "304", thickness: 0.038"}}
|
|
164
|
+
panel_margin: 0mm # inset from the frame edge
|
|
165
|
+
corner_radius: 5mm # sheet corner radius (DXF + 3D)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
What you get:
|
|
169
|
+
|
|
170
|
+
- **Rivet holes pre-cut in the tubes.** Every member whose outer face lies
|
|
171
|
+
on a sided box face gets holes — rivet diameter + clearance (1/4" →
|
|
172
|
+
6.5mm), evenly pitched at `spacing`, symmetric about the member's middle
|
|
173
|
+
with a 20mm end margin. That includes mid-frame members (a level rail or
|
|
174
|
+
support on a sided face gets a rivet row too).
|
|
175
|
+
- **A DXF flat pattern per unique panel** — closed polyline outline with
|
|
176
|
+
true arc corners at `corner_radius`, CIRCLE holes, millimetre units
|
|
177
|
+
(`$INSUNITS = 4`), DXF R2000 — the format flat-laser vendors ingest
|
|
178
|
+
directly (SendCutSend, OshCut, Fabworks flat service).
|
|
179
|
+
- **Guaranteed alignment.** Panel holes are derived from the *same* planned
|
|
180
|
+
holes as the tube cuts — they cannot drift apart.
|
|
181
|
+
- Panels appear in the assembly STEP as translucent solids with their holes
|
|
182
|
+
and rounded corners, sitting proud on the frame face.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 6. Part-count consolidation
|
|
187
|
+
|
|
188
|
+
Vendors price multiples of the same part dramatically cheaper, so weldbox
|
|
189
|
+
works hard to minimize unique part numbers (`consolidate: true`, the
|
|
190
|
+
default).
|
|
191
|
+
|
|
192
|
+
**Tubes.** Members with the same profile and cut length often differ only
|
|
193
|
+
in *which faces* carry slots or holes. weldbox aligns every member of such
|
|
194
|
+
a group through the square tube's 8 symmetries (4 rotations × end-for-end
|
|
195
|
+
flip), unions the feature sets, and cuts the union into all of them. The
|
|
196
|
+
extra cuts are sacrificial — unused slots or holes over solid tube — and
|
|
197
|
+
the pass is guarded: it only ever *adds cuts* (never tabs), it never lets
|
|
198
|
+
two features come closer than a 0.5mm web, and a member that can't merge
|
|
199
|
+
safely keeps its own part number.
|
|
200
|
+
|
|
201
|
+
**Panels.** A flat sheet with through-holes can be flipped over or rotated
|
|
202
|
+
180° in plane, so left/right, front/back, and top/bottom panels of the same
|
|
203
|
+
size merge into one blank (`left-right.dxf`, qty 2). Differing hole
|
|
204
|
+
patterns are unioned under the best alignment; patterns that would collide
|
|
205
|
+
stay separate.
|
|
206
|
+
|
|
207
|
+
Real numbers: the PRD's winding machine cell is 25 members that would
|
|
208
|
+
naively be 13 unique parts — consolidation ships it as **3**. The epoxy
|
|
209
|
+
cell's 22 members also ship as **3** (its posts and supports turn out to be
|
|
210
|
+
literally the same part).
|
|
211
|
+
|
|
212
|
+
Set `consolidate: false` if you don't want sacrificial cuts on visible
|
|
213
|
+
faces — only exactly-identical parts merge then.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 7. The tab/slot system (jigless welding)
|
|
218
|
+
|
|
219
|
+
Every joint self-locates for tack welding — no jigs, clamps, or layout:
|
|
220
|
+
|
|
221
|
+
- At each butting end, the two walls parallel to the receiving member's
|
|
222
|
+
axis each grow a **tab**: the wall's own thickness, half the receiving
|
|
223
|
+
flat width wide, protruding exactly one receiving-wall thickness so it
|
|
224
|
+
finishes **flush with the far side of the wall** after welding.
|
|
225
|
+
- The receiving face gets matching **through-wall slots** at
|
|
226
|
+
tab + 0.25mm (the 0.010" slip fit tube-laser vendors recommend), with
|
|
227
|
+
**dog-bone relief circles** in the corners so the slot can't start a
|
|
228
|
+
crack.
|
|
229
|
+
- Slots always land on the flat region between the tube's corner radii.
|
|
230
|
+
- At box corners, the outer tab's slot lands flush with the post end and
|
|
231
|
+
becomes an **open hook-in notch** — drop the rail in, it self-squares.
|
|
232
|
+
- Everything is tunable per spec (`joints:`): clearance, tab width
|
|
233
|
+
fraction, dog-bone radius, weld gap, corner tabs on/off, or
|
|
234
|
+
`style: plain_butt` for plain saw-style cuts.
|
|
235
|
+
|
|
236
|
+
Correctness is enforced by the test suite: assembled members are checked
|
|
237
|
+
for **zero boolean interference** — every tab passes exactly through its
|
|
238
|
+
slot void.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 8. Outputs
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
out/<name>/
|
|
246
|
+
├── cutlist.md # human cut list + shipping estimate
|
|
247
|
+
├── cutlist.csv # same data for spreadsheets
|
|
248
|
+
├── parts/
|
|
249
|
+
│ └── <name>_<part>_<length>mm_x<qty>.step
|
|
250
|
+
├── panels/
|
|
251
|
+
│ └── <name>_<faces>.dxf
|
|
252
|
+
└── assembly.step # open in FreeCAD
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- **Part STEPs** are built in the vendor-stock convention (cross-section
|
|
256
|
+
centered on origin, extruded along +Z) and modeled against the vendor's
|
|
257
|
+
published geometry — the generated 1.5×1.5×0.120 profile matches RFMG's
|
|
258
|
+
own reference STEP within 0.5%.
|
|
259
|
+
- **The assembly STEP** contains one *named product per member and panel*
|
|
260
|
+
(`post-fl`, `work-surface-cross-2`, `panel-back`, …), colored by role —
|
|
261
|
+
posts slate, rails gray, level rails teal, crosses orange, supports
|
|
262
|
+
green, spanners purple, panels translucent — with colors on every face,
|
|
263
|
+
so FreeCAD shows an inspectable, hideable, colored model regardless of
|
|
264
|
+
its STEP import preferences.
|
|
265
|
+
- **The cut list** shows per-part length (mm and inches), quantity per
|
|
266
|
+
assembly, and total quantity across the order, plus total stock meters.
|
|
267
|
+
|
|
268
|
+
### Design-rule and shipping checks
|
|
269
|
+
|
|
270
|
+
Every run (including `--dry-run`) checks the vendor's design rules
|
|
271
|
+
(minimum hole diameter, dog-bone radius, maximum part length) and estimates
|
|
272
|
+
the **order shipping**: analytic part weights (section area × length ×
|
|
273
|
+
material density; sheet area × thickness) against the vendor's parcel
|
|
274
|
+
limits. For RFMG that means LTL freight is flagged — with the specific
|
|
275
|
+
trigger and the $200 flat surcharge — when any part exceeds 100 lb or 60"
|
|
276
|
+
(or 48"×30" on two dimensions), or the order exceeds 200 lb.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 9. Vendors
|
|
281
|
+
|
|
282
|
+
Vendor data lives in `src/weldbox/vendors/data/*.yaml` behind a small
|
|
283
|
+
`Vendor` interface (catalog, design rules, shipping rules, file naming):
|
|
284
|
+
|
|
285
|
+
- **rfmg** — fully encoded: 56 profiles (A500/4130/DOM steel, 304
|
|
286
|
+
stainless, 6061/6063 aluminum) with published corner radii, design notes,
|
|
287
|
+
and LTL freight thresholds. Source:
|
|
288
|
+
<https://www.rmfg.com/docs/services/laser-tube-cutting>
|
|
289
|
+
- **oshcut**, **fabtech** — registered stubs awaiting material lists.
|
|
290
|
+
|
|
291
|
+
To add a vendor: drop a `data/<slug>.yaml` (dimensions in inches as
|
|
292
|
+
published; omit corner radius where unpublished and weldbox assumes
|
|
293
|
+
2×wall), subclass `Vendor` with its rules, and register it. Catalog lookups
|
|
294
|
+
are tolerant (±0.02mm) and demand a `family` when a size/wall exists in
|
|
295
|
+
several alloys.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 10. Validation and errors
|
|
300
|
+
|
|
301
|
+
- Unknown/typo'd spec fields → error naming the exact path
|
|
302
|
+
(`blocking.1.spanner.cross_members: Extra inputs are not permitted`).
|
|
303
|
+
- Level heights → validated against the box with the legal range in the
|
|
304
|
+
message, in *your* `height_ref` terms.
|
|
305
|
+
- Exterior too small for the tube, unknown vendors/layers, ambiguous
|
|
306
|
+
catalog matches → specific errors.
|
|
307
|
+
- The CLI prints one clean error line and exits 1 — no tracebacks.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 11. Current limitations
|
|
312
|
+
|
|
313
|
+
- Frame math supports **square tube** only (rect/round profiles exist in
|
|
314
|
+
the catalog for future use; round stock is not applicable to this joint
|
|
315
|
+
system).
|
|
316
|
+
- One tube profile per box.
|
|
317
|
+
- Gussets are not yet generated.
|
|
318
|
+
- Blocking members can be placed at positions that collide with each other
|
|
319
|
+
(e.g. a spanner through a support); member-vs-member placement collision
|
|
320
|
+
checking is on the roadmap — the bounding-box overlap tests catch this in
|
|
321
|
+
the standard layouts.
|
|
322
|
+
- `oshcut`/`fabtech` catalogs are empty until their material lists are
|
|
323
|
+
transcribed.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
todo
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
todo
|