gitcad-mech 0.7.7__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.
- gitcad_mech-0.7.7/.gitignore +38 -0
- gitcad_mech-0.7.7/PKG-INFO +50 -0
- gitcad_mech-0.7.7/README.md +23 -0
- gitcad_mech-0.7.7/pyproject.toml +46 -0
- gitcad_mech-0.7.7/src/gitcad/analysis.py +181 -0
- gitcad_mech-0.7.7/src/gitcad/derive.py +60 -0
- gitcad_mech-0.7.7/src/gitcad/document.py +662 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/__init__.py +14 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/assembly.py +103 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/dxf.py +63 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/hlr.py +39 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/pdf.py +108 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/sections.py +137 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/sheet.py +286 -0
- gitcad_mech-0.7.7/src/gitcad/drawing/svg.py +144 -0
- gitcad_mech-0.7.7/src/gitcad/importers/fcstd.py +163 -0
- gitcad_mech-0.7.7/src/gitcad/importers/fcstd_tree.py +183 -0
- gitcad_mech-0.7.7/src/gitcad/importers/recognize.py +111 -0
- gitcad_mech-0.7.7/src/gitcad/importers/step.py +41 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/__init__.py +32 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/auto.py +96 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/null.py +254 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/occt.py +719 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/ref.py +781 -0
- gitcad_mech-0.7.7/src/gitcad/kernel/rustref.py +232 -0
- gitcad_mech-0.7.7/src/gitcad/select.py +51 -0
- gitcad_mech-0.7.7/src/gitcad/sheetmetal.py +397 -0
- gitcad_mech-0.7.7/src/gitcad/sketch.py +164 -0
- gitcad_mech-0.7.7/src/gitcad/sketch_solver.py +230 -0
- gitcad_mech-0.7.7/src/gitcad/weldment.py +126 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Generated geometry is a build artifact, NOT source (ADR-0004). Never commit it.
|
|
2
|
+
*.brep
|
|
3
|
+
*.step
|
|
4
|
+
*.stp
|
|
5
|
+
*.stl
|
|
6
|
+
*.gltf
|
|
7
|
+
*.glb
|
|
8
|
+
*.iges
|
|
9
|
+
*.igs
|
|
10
|
+
# Drawings are generated from the model too.
|
|
11
|
+
/build-artifacts/
|
|
12
|
+
*.dxf
|
|
13
|
+
# PDFs generated from drawings (keep hand-authored docs elsewhere if needed).
|
|
14
|
+
/out/
|
|
15
|
+
|
|
16
|
+
# Python
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.eggs/
|
|
21
|
+
build/
|
|
22
|
+
dist/
|
|
23
|
+
.venv/
|
|
24
|
+
venv/
|
|
25
|
+
|
|
26
|
+
# Tooling
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
.coverage
|
|
29
|
+
.coverage.*
|
|
30
|
+
htmlcov/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
|
|
34
|
+
# Editors / OS
|
|
35
|
+
.vscode/
|
|
36
|
+
.idea/
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitcad-mech
|
|
3
|
+
Version: 0.7.7
|
|
4
|
+
Summary: gitcad mechanical: document model, exact B-rep kernel, sketches, drawings, mech importers
|
|
5
|
+
Project-URL: Homepage, https://gitcad.xyz
|
|
6
|
+
Project-URL: Repository, https://github.com/gitcad-xyz/gitcad
|
|
7
|
+
Project-URL: Issues, https://github.com/gitcad-xyz/gitcad/issues
|
|
8
|
+
Project-URL: Kernel (forgekernel), https://github.com/gitcad-xyz/forge
|
|
9
|
+
Author-email: Dan Willis <danielcwillis@gmail.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: brep,cad,exact-arithmetic,kernel,nurbs,occt,sheet-metal,step
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Manufacturing
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: forgekernel-rs>=0.1.0; platform_machine in 'x86_64 AMD64 aarch64 arm64'
|
|
22
|
+
Requires-Dist: forgekernel>=0.1.0
|
|
23
|
+
Requires-Dist: gitcad-core==0.7.7
|
|
24
|
+
Provides-Extra: occt
|
|
25
|
+
Requires-Dist: cadquery-ocp>=7.7; extra == 'occt'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# gitcad-mech
|
|
29
|
+
|
|
30
|
+
Mechanical domain for [**gitcad**](https://gitcad.xyz) — the parametric document
|
|
31
|
+
model and geometry kernels.
|
|
32
|
+
|
|
33
|
+
- **Document model** — feature tree with stable identity; builds to a solid and a
|
|
34
|
+
reviewable report.
|
|
35
|
+
- **Exact kernel by default** — ships [`forgekernel`](https://pypi.org/project/forgekernel/),
|
|
36
|
+
a from-scratch B-rep kernel whose topological decisions are made in exact
|
|
37
|
+
rational arithmetic (ℚ), never by floating-point epsilon. OCCT
|
|
38
|
+
(`cadquery-ocp`) is available as an independent oracle / fallback.
|
|
39
|
+
- **Sketches, extrude/revolve, loft, sweep, fillet/chamfer, shell, patterns,
|
|
40
|
+
sheet metal, weldments, holes with thread specs.**
|
|
41
|
+
- **Associative drawings** — HLR views, section/detail views, dimensions, GD&T,
|
|
42
|
+
weld & surface-finish symbols → SVG/PDF/DXF.
|
|
43
|
+
- **Importers** — STEP, FCStd (parametric tree), with verified feature recovery.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install gitcad-mech # exact kernel + native accelerator (default where a wheel exists)
|
|
47
|
+
pip install gitcad-mech[occt] # + OCCT oracle/fallback (cadquery-ocp)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Apache-2.0 · https://github.com/gitcad-xyz/gitcad
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# gitcad-mech
|
|
2
|
+
|
|
3
|
+
Mechanical domain for [**gitcad**](https://gitcad.xyz) — the parametric document
|
|
4
|
+
model and geometry kernels.
|
|
5
|
+
|
|
6
|
+
- **Document model** — feature tree with stable identity; builds to a solid and a
|
|
7
|
+
reviewable report.
|
|
8
|
+
- **Exact kernel by default** — ships [`forgekernel`](https://pypi.org/project/forgekernel/),
|
|
9
|
+
a from-scratch B-rep kernel whose topological decisions are made in exact
|
|
10
|
+
rational arithmetic (ℚ), never by floating-point epsilon. OCCT
|
|
11
|
+
(`cadquery-ocp`) is available as an independent oracle / fallback.
|
|
12
|
+
- **Sketches, extrude/revolve, loft, sweep, fillet/chamfer, shell, patterns,
|
|
13
|
+
sheet metal, weldments, holes with thread specs.**
|
|
14
|
+
- **Associative drawings** — HLR views, section/detail views, dimensions, GD&T,
|
|
15
|
+
weld & surface-finish symbols → SVG/PDF/DXF.
|
|
16
|
+
- **Importers** — STEP, FCStd (parametric tree), with verified feature recovery.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install gitcad-mech # exact kernel + native accelerator (default where a wheel exists)
|
|
20
|
+
pip install gitcad-mech[occt] # + OCCT oracle/fallback (cadquery-ocp)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Apache-2.0 · https://github.com/gitcad-xyz/gitcad
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gitcad-mech"
|
|
7
|
+
version = "0.7.7"
|
|
8
|
+
description = "gitcad mechanical: document model, exact B-rep kernel, sketches, drawings, mech importers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [{name = "Dan Willis", email = "danielcwillis@gmail.com"}]
|
|
13
|
+
keywords = ["cad", "brep", "kernel", "nurbs", "exact-arithmetic", "sheet-metal", "step", "occt"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Manufacturing",
|
|
18
|
+
"License :: OSI Approved :: Apache Software License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Topic :: Scientific/Engineering",
|
|
22
|
+
"Topic :: Multimedia :: Graphics :: 3D Modeling",
|
|
23
|
+
]
|
|
24
|
+
# The exact forge kernel is the default backend. forgekernel (pure Python) is
|
|
25
|
+
# the always-present floor; the native forgekernel_rs accelerator installs BY
|
|
26
|
+
# DEFAULT on mainstream architectures (identical results, faster hot paths) and
|
|
27
|
+
# is skipped only on exotic arches with no wheel — where forge auto-falls-back
|
|
28
|
+
# to pure Python at runtime, so nothing breaks.
|
|
29
|
+
dependencies = [
|
|
30
|
+
"gitcad-core==0.7.7",
|
|
31
|
+
"forgekernel>=0.1.0",
|
|
32
|
+
"forgekernel_rs>=0.1.0; platform_machine in 'x86_64 AMD64 aarch64 arm64'",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
# OCCT independent oracle / fallback kernel.
|
|
37
|
+
occt = ["cadquery-ocp>=7.7"]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://gitcad.xyz"
|
|
41
|
+
Repository = "https://github.com/gitcad-xyz/gitcad"
|
|
42
|
+
Issues = "https://github.com/gitcad-xyz/gitcad/issues"
|
|
43
|
+
"Kernel (forgekernel)" = "https://github.com/gitcad-xyz/forge"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/gitcad"]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Mechanical analysis: exact inertia through the seam + DFM checks.
|
|
2
|
+
|
|
3
|
+
`inertia` surfaces the exact rational inertia tensor forge computes for
|
|
4
|
+
its solid classes (planar and freeform, via the divergence-theorem flux)
|
|
5
|
+
and falls back to OCCT's float `MatrixOfInertia` for OCCT shapes.
|
|
6
|
+
`draft_analysis` is a moldability check: faces whose draft angle to a
|
|
7
|
+
pull direction is below a minimum can't release from the tool.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import math
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def inertia(kernel, shape) -> dict[str, Any]:
|
|
17
|
+
"""Volume, centroid, and inertia tensor (about the centroid) + the
|
|
18
|
+
three principal moments. Exact rationals when forge built the shape;
|
|
19
|
+
floats via OCCT otherwise. Returns floats for a uniform interface,
|
|
20
|
+
with ``exact=True`` when the underlying tensor is rational."""
|
|
21
|
+
exact = False
|
|
22
|
+
try:
|
|
23
|
+
from forgekernel.bsolid import (PatchSolid, mass_properties,
|
|
24
|
+
polyhedron_mass_properties)
|
|
25
|
+
from forgekernel.brep import Solid as _Solid
|
|
26
|
+
|
|
27
|
+
if isinstance(shape, _Solid):
|
|
28
|
+
mp = polyhedron_mass_properties(shape)
|
|
29
|
+
exact = True
|
|
30
|
+
elif isinstance(shape, PatchSolid):
|
|
31
|
+
mp = mass_properties(shape)
|
|
32
|
+
exact = True
|
|
33
|
+
else:
|
|
34
|
+
mp = None
|
|
35
|
+
except ImportError:
|
|
36
|
+
mp = None
|
|
37
|
+
|
|
38
|
+
if mp is not None:
|
|
39
|
+
V = float(mp["volume"])
|
|
40
|
+
c = tuple(float(x) for x in mp["centroid"])
|
|
41
|
+
I = [[float(mp["inertia"][i][j]) for j in range(3)] for i in range(3)]
|
|
42
|
+
else:
|
|
43
|
+
# any kernel's mass_props returns the tensor about the centroid;
|
|
44
|
+
# route through the seam (no direct OCP import — ADR-0002).
|
|
45
|
+
m = kernel.mass_props(shape)
|
|
46
|
+
V = float(m["volume"])
|
|
47
|
+
c = (m["cx"], m["cy"], m["cz"])
|
|
48
|
+
I = [[m["ixx"], m["ixy"], m["ixz"]],
|
|
49
|
+
[m["ixy"], m["iyy"], m["iyz"]],
|
|
50
|
+
[m["ixz"], m["iyz"], m["izz"]]]
|
|
51
|
+
|
|
52
|
+
principal = sorted(_sym_eigs_3x3(I))
|
|
53
|
+
return {"volume": V, "centroid": c, "inertia": I,
|
|
54
|
+
"principal_moments": principal, "exact": exact}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _sym_eigs_3x3(A):
|
|
58
|
+
"""Eigenvalues of a symmetric 3×3 matrix (closed form; analysis only)."""
|
|
59
|
+
p1 = A[0][1] ** 2 + A[0][2] ** 2 + A[1][2] ** 2
|
|
60
|
+
q = (A[0][0] + A[1][1] + A[2][2]) / 3
|
|
61
|
+
p2 = ((A[0][0] - q) ** 2 + (A[1][1] - q) ** 2 + (A[2][2] - q) ** 2
|
|
62
|
+
+ 2 * p1)
|
|
63
|
+
if p2 == 0:
|
|
64
|
+
return [A[0][0], A[1][1], A[2][2]]
|
|
65
|
+
p = math.sqrt(p2 / 6)
|
|
66
|
+
B = [[(A[i][j] - (q if i == j else 0)) / p for j in range(3)]
|
|
67
|
+
for i in range(3)]
|
|
68
|
+
detB = (B[0][0] * (B[1][1] * B[2][2] - B[1][2] * B[2][1])
|
|
69
|
+
- B[0][1] * (B[1][0] * B[2][2] - B[1][2] * B[2][0])
|
|
70
|
+
+ B[0][2] * (B[1][0] * B[2][1] - B[1][1] * B[2][0]))
|
|
71
|
+
r = max(-1.0, min(1.0, detB / 2))
|
|
72
|
+
phi = math.acos(r) / 3
|
|
73
|
+
e1 = q + 2 * p * math.cos(phi)
|
|
74
|
+
e3 = q + 2 * p * math.cos(phi + 2 * math.pi / 3)
|
|
75
|
+
e2 = 3 * q - e1 - e3
|
|
76
|
+
return [e1, e2, e3]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def draft_analysis(kernel, shape, pull=(0, 0, 1),
|
|
80
|
+
min_angle_deg: float = 1.0) -> dict[str, Any]:
|
|
81
|
+
"""Moldability: for a pull (ejection) direction, report planar faces
|
|
82
|
+
whose draft angle — 90° minus the angle between the face normal and
|
|
83
|
+
the pull — is below ``min_angle_deg``. Zero-draft walls (parallel to
|
|
84
|
+
the pull) fail hardest; floors/ceilings (perpendicular) pass."""
|
|
85
|
+
if math.sqrt(sum(float(c) ** 2 for c in pull)) < 1e-12:
|
|
86
|
+
raise ValueError("draft_analysis: pull direction must be non-zero")
|
|
87
|
+
d = _unit(pull)
|
|
88
|
+
faces = kernel.entities(shape, "face")
|
|
89
|
+
violations = []
|
|
90
|
+
for i, f in enumerate(faces):
|
|
91
|
+
n = f.get("plane")
|
|
92
|
+
if n is None: # curved face: skip (report as such)
|
|
93
|
+
continue
|
|
94
|
+
n = _unit(n)
|
|
95
|
+
dot = abs(sum(n[c] * d[c] for c in range(3)))
|
|
96
|
+
theta = math.degrees(math.acos(max(-1.0, min(1.0, dot))))
|
|
97
|
+
draft = 90.0 - theta
|
|
98
|
+
if draft < min_angle_deg:
|
|
99
|
+
violations.append({"face": i, "draft_deg": round(draft, 4),
|
|
100
|
+
"normal": [round(x, 6) for x in n]})
|
|
101
|
+
return {"pull": list(d), "min_angle_deg": min_angle_deg,
|
|
102
|
+
"faces_checked": len(faces), "violations": violations,
|
|
103
|
+
"ok": not violations}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _unit(v):
|
|
107
|
+
m = math.sqrt(sum(float(c) ** 2 for c in v)) or 1.0
|
|
108
|
+
return tuple(float(c) / m for c in v)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def thickness_analysis(kernel, shape, min_wall: float = 1.0) -> dict[str, Any]:
|
|
112
|
+
"""Min-wall (moldability/printability) estimate for a planar forge
|
|
113
|
+
solid: the smallest separation between an anti-parallel pair of faces
|
|
114
|
+
whose projections overlap. Exact for prismatic parts (a box reports
|
|
115
|
+
min(dx,dy,dz)); a first-order estimate elsewhere (not a full medial
|
|
116
|
+
axis). Raises for non-forge shapes."""
|
|
117
|
+
try:
|
|
118
|
+
from forgekernel.brep import Solid as _Solid
|
|
119
|
+
except ImportError: # pragma: no cover
|
|
120
|
+
_Solid = ()
|
|
121
|
+
if not isinstance(shape, _Solid):
|
|
122
|
+
raise NotImplementedError(
|
|
123
|
+
"thickness_analysis: planar forge solids only")
|
|
124
|
+
# group polygons by (unit normal, plane offset)
|
|
125
|
+
faces = []
|
|
126
|
+
for poly in shape.polys:
|
|
127
|
+
n = _unit(poly.plane.n)
|
|
128
|
+
# a point on the plane → signed offset d = n·p
|
|
129
|
+
v0 = poly.verts[0]
|
|
130
|
+
d = sum(n[c] * float(v0[c]) for c in range(3))
|
|
131
|
+
pts = [tuple(float(x) for x in v) for v in poly.verts]
|
|
132
|
+
faces.append((n, d, pts))
|
|
133
|
+
thin = []
|
|
134
|
+
min_t = float("inf")
|
|
135
|
+
for i in range(len(faces)):
|
|
136
|
+
ni, di, pi = faces[i]
|
|
137
|
+
for j in range(i + 1, len(faces)):
|
|
138
|
+
nj, dj, pj = faces[j]
|
|
139
|
+
# anti-parallel?
|
|
140
|
+
if abs(ni[0] + nj[0]) > 1e-9 or abs(ni[1] + nj[1]) > 1e-9 \
|
|
141
|
+
or abs(ni[2] + nj[2]) > 1e-9:
|
|
142
|
+
continue
|
|
143
|
+
# projected-bbox overlap onto the plane's two in-plane axes
|
|
144
|
+
if not _proj_overlap(ni, pi, pj):
|
|
145
|
+
continue
|
|
146
|
+
# nj = −ni. In the nᵢ coordinate face i sits at di and face j at
|
|
147
|
+
# −dj; SOLID lies between them only when di + dj > 0 (that IS the
|
|
148
|
+
# wall thickness). di + dj < 0 is an EMPTY slot/pocket — not a
|
|
149
|
+
# wall — so the sign must NOT be discarded with abs() (that
|
|
150
|
+
# flagged open slots as thin material, a false DFM failure).
|
|
151
|
+
t = di + dj
|
|
152
|
+
if t <= 1e-12:
|
|
153
|
+
continue
|
|
154
|
+
if t < min_t:
|
|
155
|
+
min_t = t
|
|
156
|
+
if t < min_wall:
|
|
157
|
+
thin.append({"faces": [i, j], "thickness": round(t, 6)})
|
|
158
|
+
return {"min_wall_target": min_wall,
|
|
159
|
+
"min_thickness": None if min_t == float("inf") else round(min_t, 6),
|
|
160
|
+
"thin_regions": thin, "ok": min_t >= min_wall}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _proj_overlap(n, pa, pb) -> bool:
|
|
164
|
+
"""Do two planar point-sets overlap when projected onto the plane ⟂ n?"""
|
|
165
|
+
# two in-plane basis vectors
|
|
166
|
+
ref = (1.0, 0.0, 0.0) if abs(n[0]) < 0.9 else (0.0, 1.0, 0.0)
|
|
167
|
+
dot = sum(ref[c] * n[c] for c in range(3))
|
|
168
|
+
u = tuple(ref[c] - dot * n[c] for c in range(3))
|
|
169
|
+
um = math.sqrt(sum(c * c for c in u)) or 1.0
|
|
170
|
+
u = tuple(c / um for c in u)
|
|
171
|
+
w = (n[1] * u[2] - n[2] * u[1], n[2] * u[0] - n[0] * u[2],
|
|
172
|
+
n[0] * u[1] - n[1] * u[0])
|
|
173
|
+
|
|
174
|
+
def box(pts):
|
|
175
|
+
us = [sum(p[c] * u[c] for c in range(3)) for p in pts]
|
|
176
|
+
ws = [sum(p[c] * w[c] for c in range(3)) for p in pts]
|
|
177
|
+
return min(us), max(us), min(ws), max(ws)
|
|
178
|
+
|
|
179
|
+
au0, au1, aw0, aw1 = box(pa)
|
|
180
|
+
bu0, bu1, bw0, bw1 = box(pb)
|
|
181
|
+
return au0 <= bu1 and bu0 <= au1 and aw0 <= bw1 and bw0 <= aw1
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Deriving part interfaces from real domain data (ADR-0008 domain wiring).
|
|
2
|
+
|
|
3
|
+
The board side lives on :meth:`gitcad.ecad.board.Board.to_part`. This module
|
|
4
|
+
covers the mechanical side: build a Document against a kernel and derive the
|
|
5
|
+
envelope from actual geometry. Ports/frames remain *declared* for mech in v1 —
|
|
6
|
+
a solid doesn't announce which faces are mounting bosses; that semantic tag is
|
|
7
|
+
design intent (a follow-up will attach ports to stable entity ids so they
|
|
8
|
+
re-derive positions from geometry too).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from gitcad.document import Document
|
|
14
|
+
from gitcad.part.interface import Frame, Interface, Port
|
|
15
|
+
from gitcad.part.manifest import PartManifest
|
|
16
|
+
from gitcad.seams import Kernel
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def model_to_part(
|
|
20
|
+
doc: Document,
|
|
21
|
+
kernel: Kernel,
|
|
22
|
+
*,
|
|
23
|
+
part_id: str,
|
|
24
|
+
name: str,
|
|
25
|
+
version: str = "0.1.0",
|
|
26
|
+
frames: dict[str, Frame] | None = None,
|
|
27
|
+
ports: dict[str, Port] | None = None,
|
|
28
|
+
properties: dict | None = None,
|
|
29
|
+
) -> PartManifest:
|
|
30
|
+
"""Build ``doc`` and derive its ``part.json``: envelope from the built
|
|
31
|
+
geometry's bounding box; declared frames/ports carried through."""
|
|
32
|
+
if not len(doc):
|
|
33
|
+
raise ValueError("document has no features")
|
|
34
|
+
final = doc.build(kernel).final(doc)
|
|
35
|
+
(minx, miny, minz), (maxx, maxy, maxz) = kernel.bbox(final)
|
|
36
|
+
# Round to fixed precision: a derived envelope feeds interface-semver
|
|
37
|
+
# (ADR-0009), so float noise between rebuilds must never register as an
|
|
38
|
+
# interface change. 1e-6 mm is far below manufacturing relevance.
|
|
39
|
+
minx, miny, minz, maxx, maxy, maxz = (round(v, 6) for v in
|
|
40
|
+
(minx, miny, minz, maxx, maxy, maxz))
|
|
41
|
+
|
|
42
|
+
iface_frames = {"origin": Frame()}
|
|
43
|
+
iface_frames.update(frames or {})
|
|
44
|
+
props = {"kernel": kernel.name}
|
|
45
|
+
props.update(properties or {})
|
|
46
|
+
measures = kernel.measure(final)
|
|
47
|
+
if "volume" in measures:
|
|
48
|
+
props["volume_mm3"] = round(measures["volume"], 3)
|
|
49
|
+
|
|
50
|
+
return PartManifest(
|
|
51
|
+
id=part_id, name=name, domain="mech", version=version,
|
|
52
|
+
interface=Interface(
|
|
53
|
+
envelope={"origin": [minx, miny, minz],
|
|
54
|
+
"dx": maxx - minx, "dy": maxy - miny, "dz": maxz - minz},
|
|
55
|
+
frames=iface_frames,
|
|
56
|
+
ports=dict(ports or {}),
|
|
57
|
+
properties=props,
|
|
58
|
+
),
|
|
59
|
+
body={"model": f"{name}.model"},
|
|
60
|
+
)
|