Skip to main content

emcad

A 2D polygon/CAD geometry kernel with a Rust core, plus a from-scratch ODB++ PCB design-format parser.

Written with AI assistance. The vast majority of this codebase was written by Claude Code, under human direction and review. See LICENSE for the license and an explicit note on what that means for reuse.

What this is

emcad is two largely independent things that happen to share one package:

  1. A 2D polygon boolean/geometry kernel (emcad.kernel / emcad.poly) -- exact-predicate union / intersect / subtract / fragment, robust hole and nested-island handling, RDP simplification, and a "de-zigzag" cleanup pass for the tiny step artifacts that round-capped, tessellated-circle unions tend to leave behind. Built around an exact-predicate half-edge arrangement engine (grid-snapped integer coordinates, no epsilon-tuning), so degenerate cases -- T-junctions, same-side overlaps, islands nested inside dynamically-formed holes -- resolve correctly instead of needing ad-hoc tolerance fixes.
  2. A native-Python ODB++ reader (emcad.odbpp) -- parses the ODB++ product-model tree (matrix, layers, features, symbols, drill tools) into a typed object graph, then resolves it into per-layer, boolean-unified 2D polygons and a Z-stack, ready to hand to a mesher or FEM tool.

The kernel is a compiled Rust extension (emcad._rs, built from rust/ with PyO3). It's a port of the original Numba/Python implementation (emcad 0.1) that reproduced its results bit-for-bit while running typically 20-100x faster; see benchmarks/RESULTS.md. There's no external geometry library dependency (no Shapely/CGAL/etc).

Installation

pip install emcad

Prebuilt wheels cover Linux (x86_64, aarch64), macOS (Intel, Apple Silicon) and Windows (x64) for every CPython >= 3.10. Building from source needs a Rust toolchain (rustup), since the package is built with maturin. Core dependencies: numpy, loguru, matplotlib (used by the debug plotting helpers), msgpack (used by the optional parsed-geometry cache).

See DEMO.md for a visual walkthrough of all of this rendered with emcad.plot.GeometryPlotter, including holes/nested islands, the dezigzag() cleanup pass, and via_wall_polygons.

Quick start: the polygon kernel

import emcad as cad

a = cad.Polygon([0, 10, 10, 0], [0, 0, 10, 10])
b = cad.Polygon([5, 15, 15, 5], [5, 5, 15, 15])

union = cad.add_polygons(a, b)          # [Polygon] -- fused into one shape
intersection = cad.intersect_polygons(a, b)
difference = cad.subtract_polygons((a,), (b,))   # a minus b
grown = a.offset(0.5)                   # [Polygon] -- boundary moved 0.5 outward, round corners
shrunk = a.offset(-0.5)                 # negative = shrink (may split or vanish)

# In-place cleanup, both fail-safe (never turn a valid polygon invalid --
# see Polygon.simplify()/.dezigzag()'s own docstrings for why):
union[0].simplify(1e-6)     # Ramer-Douglas-Peucker point reduction
union[0].dezigzag(1e-5)     # collapse tessellated-circle "step" artifacts
union[0].regularize(0.25e-3)  # snap edges back onto straight lines, drop small bumps

regularize(tol, dangle_deg=5.0) is map-making style outline regularization, meant for copper outlines whose straight edges are littered with via pads poking out after a union. RDP can only remove those bumps by keeping their tips, which tilts the long edges; regularize instead anchors on edges lying on a dangle_deg direction grid, flattens protrusions smaller than tol, and rebuilds corners hidden under pads, keeping real arcs. In the ODB++ pipeline it's the regularize=True option of resolve_layer_polygons / generate_traces (ODBImportConfig.trace_regularize).

offset(distance, max_angle_deg=15.0) (or cad.offset_polygon) is a true dilation/erosion: the polygon plus or minus a strip along every edge and a round sector at every corner that opens up, resolved in one boolean, so holes filling, slots closing, necks splitting and the whole shape vanishing all come out exactly. Corner arcs are circumscribed (never closer than distance); a max_angle_deg above a corner's turn angle gives a miter join there.

Polygon.holes nests arbitrarily deep (a hole can carry its own holes, i.e. islands, to any depth), and every boolean op / cleanup pass handles that nesting automatically via the even-odd rule -- no manual depth bookkeeping.

Quick start: parsing an ODB++ board

from emcad.odbpp import PCBView

pcb = PCBView("/path/to/MyBoard.odb", thickness_stack=[35e-6, 508e-6, 35e-6])

xs, ys = pcb.get_board_polygon()          # board outline, in meters
for z1, z2, material in pcb.iter_pcb_layers():
    ...                                      # physical copper/dielectric stack

for layer in pcb.iter_geo_layers():
    polygons = pcb.resolve_layer_polygons(layer)   # boolean-unified per layer
    for poly in polygons:
        xs, ys = poly.xs, poly.ys            # ready to feed to a mesher

for hole in pcb.iter_drill_holes():
    ...                                      # plated + non-plated drill holes

PCBView parses the board itself (via ProductModel.from_path, its one-step wrapper around the lower-level parser) -- the raw parsed object graph is still available afterward as pcb.pm, if you want to walk it yourself. If you already have a ProductModel (e.g. from your own call to ProductModel.from_path(...)), pass that instead and PCBView reuses it rather than parsing twice:

from emcad.odbpp import ProductModel, PCBView

pm = ProductModel.from_path("/path/to/MyBoard.odb")
pcb = PCBView(pm, thickness_stack=[35e-6, 508e-6, 35e-6])

Every coordinate everywhere in emcad.odbpp is a plain float in meters -- emcad.odbpp.units is the only place raw mm/inch/mil/micron conversion happens, so nothing downstream needs to think about units.

Re-parsing and re-resolving a large board on every run gets expensive; if you're iterating on downstream code against the same board repeatedly, cache the resolved geometry once:

from emcad.odbpp import save_pcb_cache, load_pcb_cache

save_pcb_cache(pcb, "board.pcbcache")
pcb_fast = load_pcb_cache("board.pcbcache")   # no re-parsing, no re-resolving

load_pcb_cache returns a drop-in stand-in for PCBView -- same public methods, so existing code doesn't need to change.

Architecture

See CLAUDE.md for a detailed map of both subsystems -- module-by-module responsibilities, the exact-predicate arrangement engine's design, and the ODB++ object-graph layering. It's written for an AI coding assistant working in this repo, but it's an accurate, up-to-date architecture reference for a human reader too.

Testing

uv sync --group dev                 # builds the Rust extension
uv run pytest
uv run python benchmarks/bench.py   # kernel benchmark

uv sync/uv run rebuild the extension automatically when anything under rust/ changes. tests/test_09_golden.py pins every kernel operation to golden results recorded from the original implementation.

The suite is almost entirely regression coverage for the boolean kernel (basic ops, touching polygons, holes, islands nested in holes, bounding-box clustering, de-zigzag, ring self-intersection safety) plus a round-trip test for the ODB++ geometry cache. It doesn't depend on any ODB++ board files being present.

License

MIT -- see LICENSE, which also explains what the AI-authorship note above means for reuse in more detail.

Metadata

Release files for emcad 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for emcad 0.2.0
File Size Uploaded Provenance
emcad-0.2.0.tar.gz 113.8 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for emcad 0.2.0
File
emcad-0.2.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
emcad-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl CPython 3.10 abi3 Linux musl 1.2+ x86-64 Details
emcad-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
emcad-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
emcad-0.2.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
emcad-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 3.8 MB

Release files / emcad-0.2.0.tar.gz

Download URL emcad-0.2.0.tar.gz
Size 113.8 kB
Tags Source
SHA-256 checksum
How to use checksums
46369ee7b9cbbf77f149b6f15deaa7a6770aaf567c0382f6abffb5658f0a0c66
BLAKE2b-256 checksum
How to use checksums
e60a744f39b5acbfbf58884bbded24bc2d448e151f032e8003387fa9328c0db9
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-win_amd64.whl

Download URL emcad-0.2.0-cp310-abi3-win_amd64.whl
Size 506.7 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
a80629f594fd813a6b9b22a92a65008001c5ccd7eb332a539a8cbc945e657897
BLAKE2b-256 checksum
How to use checksums
e9a8caa95d07804bba73f5397373ce5103650c9ababaa0660425755244dd51df
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl

Download URL emcad-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl
Size 835.8 kB
Tags CPython 3.10 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
bcc358e14e35771ad61e2dfb6ccb8af2eb3545a744d0d95673806c28a594cb63
BLAKE2b-256 checksum
How to use checksums
cd6f5ff7c4919e576eaa08095481b3130824d74c59ad236654da73db12aeee1d
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL emcad-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 624.9 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
fc88c4dc4ed78aff2008b47e4ba31282665a253d56bc5208c217ec47a20ccd46
BLAKE2b-256 checksum
How to use checksums
364f26897348a07c3b359b1fc5f7a8372a5c1331ad42a9aea370f821d2944a85
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL emcad-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 601.8 kB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
2b242a1f42390e57334c85226b6a1ce5eae4c7044d93f3e5951a475468804d17
BLAKE2b-256 checksum
How to use checksums
8f96098a23a9194180ca5c9438f92adab75bb6ff6d1eb7f8a4b1d05c7f13f9b7
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL emcad-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Size 566.1 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
9ecc2caa05b823eafe182a8c98b0bbfa8f2b9e825f5a4db912ae4f80124a4daf
BLAKE2b-256 checksum
How to use checksums
28085cfdfa22df12b9c6a46d95cd938b4d70aa0f4d98678723d54f30c12ce25b
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 Oct 15, 2026.

Transparency log

Release files / emcad-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL emcad-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 589.4 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
0ce493247f9c937d57d97ade984eaff5ca2f6a2602556158de3761d9778505a8
BLAKE2b-256 checksum
How to use checksums
e56036ccc898972e269a17dfd09d5048acf1b32e7ab2be8508e7755c8be8b62e
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 Oct 15, 2026.

Transparency log

Security for emcad 0.2.0

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.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for emcad-0.2.0.tar.gz

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 Oct 15, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

7 release files Full provenance

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