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 subtreeimport kinogaki as kg
doc = kg.Document()
doc.append("/world", "group")
doc.append("/world/mat", "material").set("out", (0.8, 0.1, 0.1))
doc.append("/world/ball", "object") \
.set("radius", 1.5) \
.set("albedo", (1, 1, 1)) \
.set_meta("note", "hero object") # string metadata, separate from typed properties
doc.connect("/world/mat.out", "/world/ball.albedo") # source output drives target input
doc.remove("/world/old") # removes the subtreeThe 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/mistypedr = doc.edit("/world/ball").get_float("radius", 1.0) # permissive, with a default
rgb = doc.edit("/world/ball").require_float3("albedo") # raises a located error if absent/mistypedTo 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);v = doc.eval("/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.