The C++ API

Embedding Core

Kinogaki Core is a C++ and Python library; the C++ build is C++20 and depends only on the standard library. Download the prebuilt library and headers from Releases, add the include path, and link it. Everything lives under the kinogaki:: namespace with the kinogaki/ include root:

#include "kinogaki/Document.h"
#include "kinogaki/Serialize.h"
#include "kinogaki/Evaluate.h"
using namespace kinogaki;
c++ -std=c++20 -Ikinogaki-core/include app.cpp -Lkinogaki-core/lib -lkinogaki-core

The Document API

A Document is a value type: copyable, comparable, serializable. The ergonomic way to shape it is append, which adds an element 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/mat.out"), Path("/world/ball.albedo"));  // source output drives target input
doc.remove(Path("/world/old"));               // removes the subtree

The handle re-resolves the element by path on each call, so it stays valid across further edits. In C++ values use the uniform helpers Float, Float2, Float3, Int, Bool, Str, while Python infers the type from native values (1.5, (1, 1, 1), "x", True). For the raw element, doc.element("/world/ball") returns it (or None/nullptr).

Iterate elements and connections directly; they are plain data you read straight off the Document:

for (const Element& p : doc.elements())  /* 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 = doc.eval(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. The value semantics give you undo directly, so the document needs no change-tracking machinery inside it.

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

What lives where

Document.h / Element.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.