Getting started
From nothing to a first program that reads a CAD file: get the SDK, point the wrapper at the libraries, add the licence.
Get the libraries
The SDK repository holds the headers, one folder of wrappers per language, the samples and fetch.py. The libraries themselves are attached to each release; fetch.py downloads the right archive for the machine and unpacks it beside the wrappers:
git clone https://github.com/rdeioris/cadaclysm-sdk
cd cadaclysm-sdk
python fetch.py # the latest release -> lib/ and include/
python fetch.py v0.5.1 # or a given one
Python only needs pip install cadaclysm: the wheel carries both wrappers and the libraries of one release. See the Python page.
| Path | What |
|---|---|
lib/ | The two shared libraries: cadaclysm_capi (readers, mesher, exports) and cadaclysm_blacksmith (the modelling kernel). Windows x64, macOS 11+ (universal), Linux x64 and arm64. |
include/ | cadaclysm.h and cadaclysm_blacksmith.h — the C API. |
python/ csharp/ go/ java/ node/ rust/ swift/ luajit/ | The wrappers, each with a smoke test that opens a sample and builds a part. |
godot/ | The Godot extension's source, its example project and tests; the addon itself comes prebuilt with each release. |
schemas/ | ap203.exp, the AP203 schema as a file — optional: the libraries carry every schema built in. |
Keep the wrappers and lib/ from the same release: the wrappers read structs the library fills in, and a mismatched pair misreads them.
Unlicensed until you add one
No licence is needed to try it: everything works, and a notice is printed to stderr on every open and every export. A licence file removes the notice. python fetch.py --license KEY (the key from the purchase email) writes cadaclysm.lic into the SDK; the libraries look for it in the CADACLYSM_LICENSE environment variable (a path or the text itself), then beside the executable and in the working directory, or it is loaded from code with each library's license call. See the licence file and pricing.
Load it and run something
pip install cadaclysm installs the reader, cadaclysm, and the kernel, cadaclysm.blacksmith (also importable as cadaclysm_blacksmith, its SDK name), with the libraries of the same release beside them: Windows x64, macOS 11+, Linux x64 and arm64. Python 3.8 or later; numpy is imported only when a mesh or polylines are asked for, and pip install "cadaclysm[numpy]" brings it. Without pip, the SDK's python/cadaclysm.py and python/cadaclysm_blacksmith.py are the same two files, over the standard library's ctypes.
pip install "cadaclysm[numpy]"
python -c "import cadaclysm; print(cadaclysm.version())"
# or, from the SDK:
python fetch.py
export PYTHONPATH=$PWD/python
CADACLYSM_LIBRARY (for the kernel, CADACLYSM_BLACKSMITH_LIBRARY), a file or a directory; else beside the module file — where pip puts it; else a lib/ directory in any parent — where fetch.py puts it. cadaclysm.library_path() says which was used.
A first program — open a STEP file, print its tree, write glTF:
import cadaclysm
with cadaclysm.open("plate.stp") as scene:
print(scene.schema, scene.metres_per_unit, "m per unit")
# The tree: assemblies, parts and bodies, parents before children.
for node in scene.walk():
print(" " * node.depth + node.label, f"[{node.kind}]")
# What to draw: every placement of every shape, meshed on first ask.
for placement in scene.placements:
mesh = placement.geometry.mesh
print(placement.geometry.label, mesh.triangle_count, "triangles")
scene.save("plate.glb")
python read_plate.py
csharp/Cad.cs (the reader, namespace Cadaclysm) and csharp/Blacksmith.cs (the kernel, namespace Cadaclysm.Blacksmith): compile both into your project. .NET 8 or later, with unsafe blocks allowed. The reader's free functions live on the static class Cadaclysm.Cadaclysm, spelled in full from code that imports the namespace — Cadaclysm.Cadaclysm.Open(path).
<ItemGroup>
<Compile Include="path/to/sdk/csharp/Cad.cs" />
<Compile Include="path/to/sdk/csharp/Blacksmith.cs" />
</ItemGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
CADACLYSM_LIBRARY for the reader and CADACLYSM_BLACKSMITH_LIBRARY for the kernel, each a file or a directory; else beside the assembly; else a lib/ directory in any parent. Past that the reader tries the platform's own search, and the kernel stops and says where it looked, as Python's does. Shipping the libraries next to your executable needs no configuration.
A first program — open a STEP file, print its tree, write glTF:
using Cadaclysm;
using var scene = Cadaclysm.Cadaclysm.Open("plate.stp");
Console.WriteLine($"{scene.Schema} {scene.MetresPerUnit} m per unit");
// The tree: assemblies, parts and bodies, parents before children.
foreach (var node in scene.Walk())
Console.WriteLine($"{new string(' ', 2 * (int)node.Depth)}{node.Label} [{node.Kind}]");
// What to draw: every placement of every shape, meshed on first ask.
foreach (var placement in scene.Placements)
{
var mesh = placement.Geometry.Mesh;
Console.WriteLine($"{placement.Geometry.Label} {mesh?.TriangleCount ?? 0} triangles");
}
scene.Save("plate.glb");
dotnet run
The packages github.com/rdeioris/cadaclysm-sdk/go/cadaclysm (the reader) and .../go/blacksmith (the kernel), over cgo — so a C compiler is needed at build time (MinGW-w64 on Windows). Go 1.24 or later.
go get github.com/rdeioris/cadaclysm-sdk/go
# the loader must find the library at run time:
export LD_LIBRARY_PATH=$PWD/lib # macOS: DYLD_LIBRARY_PATH; Windows: add lib to PATH
cgo links against the SDK's lib/ at build time (set CGO_LDFLAGS=-L<dir> for another place); at run time the operating system's loader finds the library, so its directory goes on PATH (Windows), LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS), or the library ships beside the executable.
A first program — open a STEP file, print its tree, write glTF:
package main
import (
"fmt"
"log"
"strings"
"github.com/rdeioris/cadaclysm-sdk/go/cadaclysm"
)
func main() {
scene, err := cadaclysm.Open("plate.stp")
if err != nil {
log.Fatal(err)
}
defer scene.Close()
fmt.Println(scene.Schema(), scene.MetresPerUnit(), "m per unit")
// The tree: assemblies, parts and bodies, parents before children.
for _, node := range scene.Walk() {
fmt.Printf("%s%s [%s]\n", strings.Repeat(" ", int(node.Depth())), node.Label(), node.Kind())
}
// What to draw: every placement of every shape, meshed on first ask.
for _, placement := range scene.Placements() {
mesh, err := placement.Geometry().Mesh()
if err != nil {
log.Fatal(err)
}
fmt.Println(placement.Geometry().Label(), mesh.TriangleCount(), "triangles")
}
if err := scene.Save("plate.glb", "glb"); err != nil {
log.Fatal(err)
}
}
go run .
java/Cad.java (the reader) and java/Blacksmith.java (the kernel), in the default package: compile them with your code. JDK 22 or later — they use the Foreign Function and Memory API — and run with --enable-native-access=ALL-UNNAMED. Types are nested: Cad.Scene, Blacksmith.Solid.
javac --release 22 -d classes java/Cad.java java/Blacksmith.java MyApp.java
java --enable-native-access=ALL-UNNAMED -cp classes MyApp
CADACLYSM_LIBRARY for the reader and CADACLYSM_BLACKSMITH_LIBRARY for the kernel, each a file or a directory; else beside the classes or jar; else a lib/ directory in any parent. Past that the reader tries the platform's own search, and the kernel stops and says where it looked, as Python's does.
A first program — open a STEP file, print its tree, write glTF:
public class ReadPlate {
public static void main(String[] args) {
try (Cad.Scene scene = Cad.open("plate.stp")) {
System.out.println(scene.schema() + " " + scene.metresPerUnit() + " m per unit");
// The tree: assemblies, parts and bodies, parents before children.
for (Cad.Node node : scene.walk()) {
System.out.println(" ".repeat(node.depth()) + node.label() + " [" + node.kind() + "]");
}
// What to draw: every placement of every shape, meshed on first ask.
for (Cad.Placement placement : scene.placements()) {
Cad.Mesh mesh = placement.geometry().mesh();
System.out.println(placement.geometry().label() + " " + mesh.triangleCount() + " triangles");
}
scene.save("plate.glb");
}
}
}
javac --release 22 -d classes Cad.java Blacksmith.java ReadPlate.java
java --enable-native-access=ALL-UNNAMED -cp classes ReadPlate
node/cadaclysm.js (the reader, require('cadaclysm')) and node/cadaclysm_blacksmith.js (the kernel, require('cadaclysm/blacksmith')), over koffi, with TypeScript declarations beside them. Node.js 18 or later.
cd node && npm install # koffi
npm install ./path/to/sdk/node # from your own project
CADACLYSM_LIBRARY (for the kernel, CADACLYSM_BLACKSMITH_LIBRARY), a file or a directory; else beside the module; else a lib/ directory in any parent.
A first program — open a STEP file, print its tree, write glTF:
const cadaclysm = require('cadaclysm');
const scene = cadaclysm.open('plate.stp');
try {
console.log(scene.schema, scene.metresPerUnit, 'm per unit');
// The tree: assemblies, parts and bodies, parents before children.
for (const node of scene.walk()) {
console.log(' '.repeat(node.depth) + node.label, `[${node.kind}]`);
}
// What to draw: every placement of every shape, meshed on first ask.
for (const placement of scene.placements()) {
console.log(placement.geometry.label, placement.geometry.mesh().triangleCount, 'triangles');
}
scene.save('plate.glb');
} finally {
scene.close();
}
node read_plate.js
The crate cadaclysm-sdk (use cadaclysm_sdk): the reader in the crate root, the kernel in cadaclysm_sdk::blacksmith. It opens the libraries when the program runs, through libloading — no C toolchain, no import library, and no cadaclysm source. Rust 1.70 or later.
cargo add cadaclysm-sdk
# or without the download, with the SDK's libraries:
cargo add cadaclysm-sdk --no-default-features
CADACLYSM_LIBRARY for the reader and CADACLYSM_BLACKSMITH_LIBRARY for the kernel, each a file or a directory; else beside the executable, where the download copies them; else where the build downloaded them; else a lib/ directory in any parent of the executable or the working directory; else a target/release or target/debug there. Nothing else is searched: past that it stops and says where it looked (and why the download failed, if it did). cadaclysm_sdk::library_path() says which was used, and load(path) names one outright.
A first program — open a STEP file, print its tree, write glTF:
fn main() -> cadaclysm_sdk::Result<()> {
let scene = cadaclysm_sdk::open("plate.stp")?;
println!("{} {} m per unit", scene.schema(), scene.metres_per_unit());
// The tree: assemblies, parts and bodies, parents before children.
for node in scene.walk() {
println!("{}{} [{}]", " ".repeat(node.depth() as usize), node.label(), node.kind());
}
// What to draw: every placement of every shape, meshed on first ask.
for placement in scene.placements() {
let geometry = placement.geometry();
println!("{} {} triangles", geometry.label(), geometry.mesh().triangle_count());
}
scene.save("plate.glb", "glb")
}
cargo run
swift/, a Swift package with two library products: Cadaclysm (the reader) and Blacksmith (the kernel), over the two C headers imported as Clang modules — no generated bindings, no hand-copied structs. Swift 5.9 or later on macOS 13+, Linux or Windows. Depend on it by path from your own package; module functions are spelled with the module's name, Cadaclysm.open(path).
// In an SDK checkout, after `python fetch.py`, this builds and checks both libraries:
// swift run --package-path swift cadaclysm-smoke samples/cube.scad
// Package.swift of your own package
dependencies: [.package(path: "path/to/cadaclysm-sdk/swift")],
targets: [
.executableTarget(name: "App", dependencies: [
.product(name: "Cadaclysm", package: "swift"),
.product(name: "Blacksmith", package: "swift"),
]),
]
The libraries are linked when the package is built, as Go's are: from CADACLYSM_LIB_DIR when it is set, else the SDK's lib/ beside swift/. On macOS and Linux that directory is written into the executable's rpath; on Windows it has to be on PATH when the program runs, or the DLLs ship beside the executable. CADACLYSM_LIBRARY is a run-time loader's variable and does not apply.
A first program — open a STEP file, print its tree, write glTF:
import Cadaclysm
let scene = try Cadaclysm.open("plate.stp")
print(scene.schema, scene.metresPerUnit, "m per unit")
// The tree: assemblies, parts and bodies, parents before children.
for node in scene.walk() {
print(String(repeating: " ", count: node.depth) + node.label, "[\(node.kind)]")
}
// What to draw: every placement of every shape, meshed on first ask.
for placement in scene.placements {
let mesh = placement.geometry.mesh
print(placement.geometry.label, mesh.triangleCount, "triangles")
}
try scene.save("plate.glb")
swift run
luajit/cadaclysm.lua (the reader) and luajit/cadaclysm_blacksmith.lua (the kernel), each with its generated FFI declarations beside it (cadaclysm_cdef.lua, cadaclysm_blacksmith_cdef.lua), over LuaJIT's FFI — nothing to compile. Any LuaJIT 2.1 with its FFI: a plain luajit, LÖVE 11.3 or later, LÖVR. cadaclysm_love.lua and cadaclysm_lovr.lua turn meshes and edges into those engines' own meshes. What Python spells as a property is a field here (node.name, solid.faces), what Python calls is a method (scene:query(filter)); indices the library counts count from zero.
-- put the luajit/ folder on the module path, or copy its files beside main.lua
package.path = "path/to/cadaclysm-sdk/luajit/?.lua;" .. package.path
local cadaclysm = require("cadaclysm")
local blacksmith = require("cadaclysm_blacksmith")
print(cadaclysm.version())
CADACLYSM_LIBRARY for the reader and CADACLYSM_BLACKSMITH_LIBRARY for the kernel, each a file or a directory; else beside the Lua file; else, in LÖVE, beside the fused game's executable; else a lib/ directory — where fetch.py puts it — or a target/release in any parent. cadaclysm.library_path() says which was used, and load(path) names one outright: a library a game unpacks to its save directory, say.
A first program — open a STEP file, print its tree, write glTF:
local cadaclysm = require("cadaclysm")
local scene = cadaclysm.open("plate.stp")
print(scene.schema, scene.metres_per_unit, "m per unit")
-- The tree: assemblies, parts and bodies, parents before children.
for node in scene:walk() do
print((" "):rep(node.depth) .. node.label, "[" .. node.kind .. "]")
end
-- What to draw: every placement of every shape, meshed on first ask.
for _, placement in ipairs(scene.placements) do
local mesh = placement.geometry.mesh
print(placement.geometry.label, mesh.triangle_count, "triangles")
end
scene:save("plate.glb")
scene:close()
luajit read_plate.lua
The addon, addons/cadaclysm/: a GDExtension for Godot 4.4 and later, prebuilt in every SDK release as cadaclysm-<version>-godot.zip for Windows x64, macOS 11+ (universal) and Linux x64 and arm64. Copy its addons/cadaclysm/ into the project: there is no plugin to enable, and the classes are there in GDScript. To build it yourself, the SDK's godot/ folder holds its Rust source (Rust 1.94 or later) and build.py; see the Godot page. The classes are Python's, name for name, with Cadaclysm in front — CadaclysmScene, CadaclysmSolid — and Python's module functions are static functions of Cadaclysm (the reader) and CadaclysmBlacksmith (the kernel). Properties are read-only properties here too; Godot's own types stand in where Godot has one: a Transform3D, an AABB, a Color, an ArrayMesh. Files open Y up in metres, Godot's own space, unless asked otherwise.
# addons/cadaclysm/ copied into the project; then, from any script:
func _ready() -> void:
print(Cadaclysm.version(), " ", CadaclysmBlacksmith.version())
Beside the extension: the addon's bin/<platform>/, or next to an exported game's executable. CADACLYSM_LIBRARY for the reader and CADACLYSM_BLACKSMITH_LIBRARY for the kernel, each a file or a directory, name others and win over those; past that the SDK's own search runs (beside the executable, a lib/ in any parent). Cadaclysm.library_path() says which was used, and Cadaclysm.load(path) names one outright.
A first program — open a STEP file, print its tree, write glTF:
# Run headless from the project's folder: godot --headless --script read_plate.gd
extends SceneTree
func _initialize() -> void:
var scene := CadaclysmScene.open("plate.stp") # Y up, in metres: Godot's own space
if scene == null:
push_error(Cadaclysm.last_error())
quit(1)
return
print(scene.schema, " ", scene.metres_per_unit, " m per unit")
# The tree: assemblies, parts and bodies, parents before children.
for node: CadaclysmNode in scene.walk():
print(" ".repeat(node.depth), node.label, " [", node.kind, "]")
# What to draw: every placement of every shape, meshed on first ask.
for placement: CadaclysmPlacement in scene.placements:
var mesh := placement.geometry.mesh
print(placement.geometry.label, " ", mesh.triangle_count, " triangles")
scene.save("plate.glb", "glb")
scene.close()
quit()
godot --headless --script read_plate.gd # in the project's folder
Where to go from here
Concepts explains the object model once for every language; the examples build nine parts in Python, C#, Go, Java, Node.js, Rust and Swift; the language pages list every call.