cq-acis
Parse ACIS SAT/SAB data and convert the supported analytic B-rep subset to CadQuery/OpenCascade shapes.
日本語版: README.ja.md
This project is experimental. ACIS SAT versions and producer-specific records vary, so unsupported entities are preserved as raw records and conversion fails explicitly when an exact shape cannot be built.
Features
- SAT container detection and ASCII record framing
- Legacy one-line and modern three-line SAT headers
- Counted
@<length>strings used by SAT 7 and later - SAB and Autodesk ShapeManager SAB parsing for explicitly admitted profiles
- Entity-reference graph validation for
$npointers - Typed decoding for SAT save versions 105, 400, 600, and 700
- Typed topology and analytic geometry entities:
body,lump,shell,face,loop,coedge,edge,vertex,point,straight-curve,ellipse-curve,plane-surface,cone-surface,sphere-surface,torus-surface, andtransform - Exact CadQuery conversion for line, circular/elliptical edge, plane, cylindrical (including elliptical), circular-conical and qualified sphere/torus geometry
- Splitting of non-manifold ACIS shells into valid CadQuery solids and compounds
- Preservation of unsupported records as
RawEntity - Reusable Rust
acis-coremodels, reference validation, and analytic helpers, with a PyO3 binding and the existing Python dataclass API
M4 adds an experimental explicit SAT 700 exactsur NURBS surface adapter.
It also admits an experimental ASM 22700 / embedded 22601 direct explicit
NURBS profile, proved cone-apex degeneracies, circular sphere holes, and
elliptical-cone isosections. Other native spline profiles and non-similarity
placements remain unsupported. Projected 2D trims are numerically checked against unchanged 3D
curves at source precision. See M4 scope and validation.
Partial tolerant topology and nested subtype views keep model API 2.
From 0.3.2, cq-acis, acis-core, and acis-py-bridge share the workspace
version. Python builds bundle the matching core and bridge sources; downstream
Rust consumers require those crates to be published to crates.io.
Installation
The CadQuery converter is installed with the default dependencies:
python -m pip install cq-acis
For a source checkout, install Rust (1.83 or newer) and a C linker first. The native extension is built by maturin:
python -m pip install -e .
Quick start
Parse SAT records and resolve topology references:
from cq_acis import BodyEntity, parse_sat_model
with open("model.sat", "rb") as source:
model = parse_sat_model(source.read())
for body in model.bodies():
assert isinstance(body, BodyEntity)
lump = model.resolve(body.lump)
print(body.index, lump.index if lump else None)
Convert the bodies to a CadQuery Workplane:
from cq_acis import import_sat_file
result = import_sat_file("model.sat")
shape = result.val()
assert shape.isValid()
print(shape.Volume())
Shared model for external decoders
cq_acis.model provides AcisModel, AcisMetadata, and the geometry/entity
types independently of SAT framing. External decoders can construct
AcisModel(metadata=..., entities=...) directly; a SatHeader, SatEntityGraph,
or text SatRecord is not required. The same to_cadquery() and new
convert_model() functions accept both this model and the existing SatModel.
For existing SAT callers:
from pathlib import Path
from cq_acis import convert_model, parse_sat_model
sat_model = parse_sat_model(Path("model.sat").read_bytes())
model = sat_model.as_acis_model()
shapes = convert_model(model)
assert model.metadata == sat_model.metadata
assert model.entities is sat_model.entities
# Existing access to sat_model.graph and raw.record still works.
Adapter contracts:
- Use contiguous, zero-based model indices for
EntityRef;-1is null. Keep source record IDs inRawEntity.entity_id. Construction validates indices and reference closure, not geometry or source-format compatibility. - Put length units and tolerances in
AcisMetadata:units_mmis millimetres per source unit,resabsuses source length units, andresnoris dimensionless. Missing source values remainNone. For compatibility, the converter still defaults missing units to 1 mm and missing absolute tolerance to 1e-6 source units; external decoders should supply known values explicitly. - Preserve source encoding, save version, producer and dialect in metadata. The application year and kernel save version are separate values.
- A
RawEntitycan omitrecordand instead retain binaryraw_dataand aSourceSpan(source_id, start_offset, end_offset). Spans are half-open byte ranges in the named domain, including decompressed streams where applicable. - Keep unknown entities as
RawEntityand adapter reports inAcisModel.diagnostics(AcisDiagnostic). Unsupported geometry referenced by a converted body raisesCadQueryConversionError; an undecoded raw body is also rejected. Other retained records and diagnostics are not proof of complete conversion and should be inspected by callers.
All existing public entity imports and the SatModel(graph, entities)
constructor remain available. SAT framing and schema decoding are still Python;
SAB/ASM binary decoding now runs in Rust; Inventor CFB/RSe extraction is implemented
in the separate inventor-kit project. See binary support.
Rust core and Python boundary
crates/acis-core owns all 14 typed entity variants, raw
values, metadata, diagnostics, reference validation/resolution, vector math,
ellipse/cone evaluation, and transform calculations. It can be a direct Cargo
dependency of an Inventor decoder without Python or CadQuery. See its
README for a local dependency example.
crates/cq-acis-py provides the PyO3
boundary. The Python dataclasses preserve constructors, imports, equality,
dataclasses.replace, and the identity of existing model entities. Their math
and AcisModel validation/resolution call Rust. No Python fallback silently
substitutes for a missing extension; build/install it before running from source.
For a model owned entirely by Rust:
native = model.to_native() # Immutable snapshot; no SAT serialization/reparsing.
assert native.to_model() == model
shapes = convert_model(native)
NativeModel implements the converter's model interface. Its properties and
to_model() create Python dataclasses with equal values, without retaining
Python entity objects or their identities. The CadQuery adapter materializes
this Python view once per converter, avoiding repeated field copies during
topology traversal. Conversion copies fields directly,
including bytes and arbitrary-precision source integers; it does not use JSON.
Model references fit signed 64-bit indices, with -1 for null, while offsets
and lengths fit the native address range. Invalid source spans and dangling
diagnostic targets are also rejected. Unknown entity classes/values must be
represented by RawEntity/bytes instead of being silently dropped.
The maturin build creates a mixed Python/Rust wheel using the CPython 3.10 stable ABI. The package requires Python 3.11 or newer because it depends on CadQuery 2.8+. Wheel installation does not need Rust; source installation does. CadQuery's platform requirements still apply. An abi3 tag alone does not demonstrate testing on every Python version or operating system.
Example and visual inspection
examples/parse_sat.py parses a SAT file, prints body
statistics, and exports STEP, STL, and SVG files.
PYTHONPATH=src python examples/parse_sat.py
PYTHONPATH=src python examples/parse_sat.py path/to/model.sat \
--output-dir /tmp/cq-acis-output
Open the generated STL or STEP in any 3D CAD viewer. With the optional
ocp_vscode viewer installed, try:
PYTHONPATH=src python examples/parse_sat.py --show
Supported entities and conversion scope
The parser currently has typed support for:
body, lump, shell, face, loop, coedge, edge, vertex,
point, straight-curve, ellipse-curve,
plane-surface, cone-surface, transform
Typed support does not imply that every entity can already be converted to a
CadQuery shape. Conversion is exact-only and raises CadQueryConversionError
for unsupported geometry.
Development corpus
The pinned development corpus is not included in the Python wheel. It is kept
in this repository for parser and conversion regression tests. Sources,
upstream commits, and SHA-1 values are recorded in
corpus/sources.lock.json; downloaded file hashes
and SAT metadata are recorded in
corpus/manifest.jsonl.
python scripts/fetch_corpus.py
python scripts/fetch_corpus.py --check
Review corpus/README.md and corpus/licenses/ before
redistributing third-party files.
Testing
Run the full test suite from a source checkout, with its virtual environment active:
python -m pip install maturin
maturin develop
cargo test -p acis-core --locked
PYTHONPATH=src python -m unittest discover -s tests -v
The pinned corpus currently covers 25 artifacts, including 23 SAT files and 2 SAB files. CadQuery regression tests convert 23 SAT artifacts and 147 bodies. Native tests round-trip all 62,530 entities through Rust and cover SAT-free geometry, source bytes/integers, model validation, and Python API compatibility.
Project status
The package is under active development. The public API and supported SAT subset may change while broader producer/version coverage is added.
License
The project code is licensed under the MIT License. Third-party
corpus files have separate notices under corpus/licenses/;
consult those notices before redistribution.
SAB / ASM (stage 3)
parse_sab_model(data, source_id="model.sab") decodes the admitted binary
profiles directly through Rust. See bounded support and history limitations.
Python release CI
Publishing a GitHub Release tagged v<workspace.package.version> builds and validates
Linux, Windows, macOS ARM64/Intel ABI3 wheels and an sdist, then publishes to PyPI
with Trusted Publishing. Manual workflow runs build and verify without uploading.
The Python version is read from Cargo.toml through maturin's dynamic metadata.
See release setup, versioning, and retry instructions.
Metadata
Release files for cq-acis 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cq_acis-0.3.2.tar.gz | 85.5 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| cq_acis-0.3.2-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| cq_acis-0.3.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| cq_acis-0.3.2-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| cq_acis-0.3.2-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 2.0 MB
Release files / cq_acis-0.3.2.tar.gz
| Download URL | cq_acis-0.3.2.tar.gz |
|---|---|
| Size | 85.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
479579b41d9b732207e1dff908e9ae75f743bfd89e1d1528f7aee8fb88d72424
|
|
BLAKE2b-256 checksum How to use checksums |
61ef17c4e9e588ffcf0b97dad7ed35af8ab344c22e33c4b02b0ab859a1d44b7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / cq_acis-0.3.2-cp310-abi3-win_amd64.whl
| Download URL | cq_acis-0.3.2-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 393.9 kB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
beaa91c63d1e0d70e41c1d0c75bf3ba4281622787c9db21a4226ea4eeeed2146
|
|
BLAKE2b-256 checksum How to use checksums |
84d98eda7e30c310313039f34ae34d70ec48241019c397832515040be3297ae5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / cq_acis-0.3.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | cq_acis-0.3.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 540.8 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
386414570718084d8e8d46dceb1defd978dea9098b41adddf3d671f75e33d660
|
|
BLAKE2b-256 checksum How to use checksums |
2f50fb8edace39517e730fe62816c93ba6a15744d685fde3a908f9cccf12e666
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / cq_acis-0.3.2-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | cq_acis-0.3.2-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 492.9 kB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
dc3699978ec3bf04b2077231669d2bf30bdcf6105ecf3e3403393da249eaf1ca
|
|
BLAKE2b-256 checksum How to use checksums |
77b32079fe6834da3445848a9c07f5fb11ae5fe2ed28812a59e3a487216212e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / cq_acis-0.3.2-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | cq_acis-0.3.2-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 494.5 kB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
7dd272637e4db6819eefc7162f9c3524650dd49cf24e090b5d0ac5c943b7e8ce
|
|
BLAKE2b-256 checksum How to use checksums |
6330f0b63e0e598d6695abf5f7c45f55e503a92155b0d61b5a55e9dd9de7135a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency log