The C++ API

Embedding Core

Prism Core is a C++20 library with no dependencies beyond the standard library. Download the prebuilt library + headers from Releases, add the include path, and link it — everything lives under the prism:: namespace with the prism/ include root:

#include "prism/Document.h"
#include "prism/Serialize.h"
#include "prism/Evaluate.h"
using namespace prism;
c++ -std=c++20 -Iprism-core/include app.cpp -Lprism-core/lib -lprism-core

The Document API

A Document is a value type — copyable, comparable, serializable. The ergonomic way to shape it is append, which adds a prim at a path and returns a chainable handle you set values on:

Document doc;
doc.append(Path("/world"), "group");
doc.append(Path("/world/mat"), "material").set("out", Float3(0.8, 0.1, 0.1));
doc.append(Path("/world/ball"), "object")
   .set("radius", Float(1.5))
   .set("albedo", Float3(1, 1, 1))
   .setMeta("note", "hero object");           // string metadata, separate from typed properties

doc.connect(Path("/world/ball.albedo"), Path("/world/mat.out"));
doc.remove(Path("/world/old"));               // removes the subtree

The handle re-resolves the prim by path on each call, so it stays valid across further edits. Values are constructed with the uniform helpers — Float, Float2, Float3, Int, Bool, Str — so the call site reads cleanly. For the raw prim, doc.prim(Path(...)) returns a Prim* (or nullptr).

Iterate prims and connections directly — they are plain data, not hidden behind a visitor:

for (const Prim& p : doc.prims())  /* p.path(), p.type() */;
for (const Connection& c : doc.connections()) /* c.target, c.source */;

Reading values safely

The chainable handle (from edit) carries permissive getters that return a default, and strict require* getters that fail loudly, so a consumer never silently gets the wrong shape:

float r   = doc.edit(Path("/world/ball")).getFloat("radius", 1.0f);   // permissive, with a default
Vec3  rgb = doc.edit(Path("/world/ball")).requireFloat3("albedo");    // throws a located error if absent/mistyped

To resolve through connections and time samples, use the evaluator rather than reading the stored default:

Value v = evaluate(doc, Path("/world/ball.albedo"), /*time*/ 12);

Value comparison is the basis of undo

Because a Document is a comparable value, a before/after pair is an edit. That is exactly how the Editor implements universal undo: snapshot, mutate, snapshot, push the pair onto a command bus. You do not need change-tracking machinery inside the document — the value semantics give it to you.

Document before = doc;          // cheap value copy
mutate(doc);
bool changed = !(doc == before);

What lives where

Document.h / Prim.h / Path.h / Property.h / Value.h are the model; Serialize.h is the native ASCII and binary I/O; Evaluate.h is the evaluator and dependency graph; Compose.h is cross-file composition; Codec.h is the codec seam. To reach the model from another language, drop down to the C ABI.