Skip to main content

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 $n pointers
  • 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, and transform
  • 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-core models, 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; -1 is null. Keep source record IDs in RawEntity.entity_id. Construction validates indices and reference closure, not geometry or source-format compatibility.
  • Put length units and tolerances in AcisMetadata: units_mm is millimetres per source unit, resabs uses source length units, and resnor is dimensionless. Missing source values remain None. 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 RawEntity can omit record and instead retain binary raw_data and a SourceSpan(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 RawEntity and adapter reports in AcisModel.diagnostics (AcisDiagnostic). Unsupported geometry referenced by a converted body raises CadQueryConversionError; 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)

Source distribution for cq-acis 0.3.2
File Size Uploaded
cq_acis-0.3.2.tar.gz 85.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for cq-acis 0.3.2
File
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 log

Release 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 log

Release 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 log

Release 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 log

Release 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

Release history Release notifications | RSS feed

0.3.12

6 release files

0.3.11

6 release files

0.3.10

5 release files

0.3.9

5 release files

0.3.8

5 release files

0.3.7

5 release files

0.3.6

5 release files

0.3.5

5 release files

0.3.4

5 release files

0.3.3

5 release files

This release

0.3.2 This release

5 release files

0.2.0

5 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page