Metadata-Version: 2.2
Name: cadunpack
Version: 0.1.9
Summary: STEP assembly extraction and sheet-metal unfolding with raw DXF output.
Author: Andrew Balogun
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: C++
Classifier: Topic :: Scientific/Engineering
Project-URL: Homepage, https://github.com/a3get/cadunpack
Project-URL: Repository, https://github.com/a3get/cadunpack
Project-URL: Issues, https://github.com/a3get/cadunpack/issues
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: cibuildwheel>=2.21; extra == "dev"
Requires-Dist: ezdxf>=1.4; extra == "dev"
Requires-Dist: pybind11>=2.12; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: scikit-build-core>=0.10; extra == "dev"
Description-Content-Type: text/markdown

# cadunpack

`cadunpack` is a Python library with a C++/OpenCascade core for:

- reading STEP files
- traversing assemblies and extracted components
- identifying supported sheet-metal parts
- unfolding supported parts
- reporting sheet thickness, bend metadata, and best-effort material/color metadata
- returning raw DXF text and original folded previews for each extracted part

The public API is intentionally small. Your project gives `cadunpack` a STEP
file, and `cadunpack` gives you parsed component metadata or unfolded DXF text.

## What It Is For

Typical usage inside your own projects:

1. receive a STEP file from disk, upload, object storage, or a database
2. call `parse_step(...)` if you only need structure and names
3. call `unpack_step(...)` if you need unfolded DXF output
4. hand the returned DXF text to `ezdxf`, persist it, or feed it into your own
   manufacturing logic

`cadunpack` does not write DXF files for you. It returns raw DXF text so the
calling project stays in control of naming, storage, validation, and downstream
processing.

## Public Python API

The supported Python API is:

- `parse_step(path)`
- `parse_step_bytes(data, file_name)`
- `unpack_step(path)`
- `unpack_step_bytes(data, file_name)`

`parse_step(...)` returns assembly and component metadata.

`unpack_step(...)` returns raw DXF text per extracted component.

## Installation

For supported wheel targets, install with normal `pip`:

```bash
python -m pip install cadunpack
```

The release workflow is designed to publish self-contained wheels for:

- macOS arm64
- Linux x86_64
- CPython 3.9, 3.11, and 3.12

For those wheel targets, `pip install cadunpack` should not require you to
install OpenCascade separately.

If you are on an unsupported interpreter or platform and `pip` falls back to a
source install, you will still need native build dependencies. That is a source
build path, not the normal install path.

## Quick Start

### Parse a STEP file from disk

```python
from cadunpack import parse_step

model = parse_step("incoming.step")

for component in model.components:
    print(component.component_name, component.assembly_path, component.shape_kind)
    print(component.material, component.color)
```

### Unpack a STEP file from disk

```python
from cadunpack import unpack_step

result = unpack_step("incoming.step")

for component in result.components:
    print(component.summary.component_name)
    print(component.generated_dxf_file_name)
    print(component.generated_dxf_text[:120])
    print(component.preview_svg_file_name)
    print(component.preview_svg_data_uri[:120])
```

### Unpack from bytes

```python
from cadunpack import unpack_step_bytes

payload = uploaded_file.read()
result = unpack_step_bytes(payload, "uploaded.step")
```

### Write the returned DXFs and previews yourself

```python
from pathlib import Path

from cadunpack import unpack_step

result = unpack_step("incoming.step")
output_dir = Path("generated-dxfs")
output_dir.mkdir(exist_ok=True)

for component in result.components:
    dxf_target = output_dir / component.generated_dxf_file_name
    dxf_target.write_text(component.generated_dxf_text, encoding="utf-8")

    svg_target = output_dir / component.preview_svg_file_name
    svg_target.write_text(component.preview_svg_text, encoding="utf-8")
```

### Validate or post-process with `ezdxf`

```python
from io import StringIO

import ezdxf
from cadunpack import unpack_step

result = unpack_step("incoming.step")
document = ezdxf.read(StringIO(result.components[0].generated_dxf_text))
print(document.modelspace())
```

### Inspect sheet-metal metadata

```python
from cadunpack import unpack_step

result = unpack_step("incoming.step")

for component in result.components:
    print(component.summary.component_name)
    print(component.thickness)
    print(component.material.name if component.material else None)
    print(component.color.hex if component.color else None)

    for bend in component.bends:
        print(bend.bend_id, bend.angle_degrees, bend.direction.value)
```

## How It Fits Into Your Projects

`cadunpack` is meant to sit inside your application code. It is not a separate
service and it does not impose a storage model.

Typical integration patterns:

- upload pipeline: receive a STEP upload, call `unpack_step_bytes(...)`, then
  store each generated DXF in your own object storage
- API backend: call `parse_step(...)` first to inspect assembly/component
  structure before deciding whether to unfold
- batch processing: iterate over many STEP files, call `unpack_step(...)`, and
  archive the returned DXF text
- CAD pipeline: use `ezdxf` or your own DXF tooling after `cadunpack` returns
  the raw DXF payload

## Data Model

`parse_step(...)` returns `StepModel`:

- `file_name`
- `roots`
- `components`

`unpack_step(...)` returns `UnpackResult`:

- `model`
- `components`

Each `UnpackedComponent` includes:

- `summary`
- `generated_dxf_file_name`
- `generated_dxf_text`
- `preview_svg_file_name`
- `preview_svg_text`
- `preview_svg_data_uri`
- `thickness`
- `bends`
- `material`
- `color`
- `has_bends`
- `warnings`

`thickness` is reported in the STEP model units used by the native unfold
kernel, normally millimeters for the generated DXF output. It is `None` when a
supported sheet thickness cannot be inferred.

Each `BendInfo` includes:

- `bend_id`
- `start`
- `end`
- `angle_degrees`
- `signed_angle_degrees`
- `direction`
- `radius`
- `inside_radius`

`material` is best-effort STEP metadata. It is `None` when the source STEP file
does not contain a material assignment that OpenCascade can expose.

`color` is best-effort STEP color metadata. It is `None` when the source STEP
file does not contain a shape color assignment that OpenCascade can expose.

The preview fields represent the original folded STEP component, not the flat
pattern. `preview_svg_text` is an SVG projection of the original component shape
with filled, shaded faces and visible model edges, including bends and holes. If
STEP color metadata is available, the preview uses it as the base face color.
Use `preview_svg_data_uri` directly in browser image tags.

Bend `direction` is reported in cadunpack's flat-pattern convention. The native
kernel chooses the largest planar patch as the reference face, walks the bend
graph outward from that face, and signs each bend from the child face position
relative to the local parent face normal. This avoids treating mirrored seam
edge ordering as an opposite bend.

## Errors

`cadunpack` raises:

- `FileNotFoundError` for missing local paths
- `ValueError` for invalid Python-side inputs such as empty `file_name`
- native `CadunpackError` exceptions for unsupported or failed STEP/unfold
  operations
- `NativeBindingError` if the Python package is present but the compiled native
  extension cannot be imported

## Development Install

If you are working on `cadunpack` itself, not just consuming it, use a source
build environment.

### macOS development

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
brew install opencascade boost cmake ninja
export CMAKE_PREFIX_PATH="$(brew --prefix opencascade);$(brew --prefix);$(brew --prefix boost)"
python -m pip install .[dev]
```

### Linux development

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
sudo apt-get update
sudo apt-get install -y \
  build-essential \
  cmake \
  ninja-build \
  patchelf \
  libboost-dev \
  libocct-foundation-dev \
  libocct-modeling-data-dev \
  libocct-modeling-algorithms-dev \
  libocct-data-exchange-dev \
  libocct-ocaf-dev
python -m pip install .[dev]
```

Run the test suite with:

```bash
python -m unittest discover -s tests -v
```

## Release Workflow

Public releases are tag-driven and intended for PyPI Trusted Publishing.

Release flow:

```bash
git tag v0.1.4
git push origin v0.1.4
```

The release workflow:

- validates that the git tag matches `pyproject.toml`
- builds Linux wheels
- builds macOS wheels
- builds an sdist
- publishes the artifacts to PyPI

The public source distribution intentionally excludes the private regression
fixtures used in the repository test suite.

## Current Scope

`cadunpack` is focused on:

- STEP traversal
- component extraction
- supported sheet-metal unfolding
- raw DXF generation

It is not currently trying to be:

- a general-purpose CAD kernel wrapper
- a GUI application
- a DXF file management layer
- a public web service
