Skip to content

stdlib_graph.kt

src/jvmMain/kotlin/afield/document/graph/stdlib_graph.kt · 188 lines

package and imports — 81 lines, from line 1
@file:afield.runtime.completion.CompletionCategory("Workspace")
@file:Suppress("UNCHECKED_CAST")

package afield.document.graph

import afield.data.collections.alphas
import afield.data.collections.get
import afield.data.collections.invoke
import afield.data.collections.r
import afield.data.collections.times
import afield.graphics.render.rangeTo
import afield.graphics.vulkan.buffers.add
import afield.lang.stacksl.h
import afield.math.autodiff.compareTo
import afield.math.autodiff.div
import afield.math.autodiff.minus
import afield.math.autodiff.plus
import afield.math.autodiff.times
import afield.math.units.div
import afield.math.units.minus
import afield.math.units.plus
import afield.math.units.times

import afield.data.collections.isTrue
import afield.data.collections.m
import afield.data.collections.s
import afield.math.linalg.Vector2f
import afield.math.linalg.Vector3f
import afield.math.linalg.Vector4f
import afield.math.linalg.Matrix4f
import afield.math.linalg.Matrix3f
import afield.math.linalg.Quaternionf
import afield.ai.agent.*
import afield.ai.conversation.*
import afield.ai.tools.*
import afield.animation.gesture.*
import afield.animation.interpolation.*
import afield.animation.keyframes.*
import afield.app.diagnostics.*
import afield.app.notifications.*
import afield.app.protocol.*
import afield.app.server.*
import afield.app.settings.*
import afield.audio.synth.*
import afield.data.table.*
import afield.document.context.*
import afield.document.graph.*
import afield.document.history.*
import afield.document.persistence.*
import afield.document.properties.*
import afield.document.workspace.*
import afield.graphics.color.*
import afield.graphics.paint.*
import afield.graphics.vulkan.*
import afield.math.functions.*
import afield.media.capture.*
import afield.media.stream.*
import afield.runtime.completion.*
import afield.runtime.execution.*
import afield.runtime.kotlin.*
import afield.runtime.lifecycle.*
import afield.tools.automation.*
import afield.tools.docs.*
import afield.ui.controls.*
import afield.ui.layout.*
import afield.ui.notation.*
import afield.ui.output.*
import afield.ui.output.templates.*
import afield.ui.slides.*
import afield.ui.visualization.*
import afield.app.server.Server.Companion.stop
import afield.graphics.vulkan.util.FramePresentationLog
import afield.graphics.vulkan.util.Options
import afield.graphics.vulkan.util.floatToHalfBits
import kotlinx.coroutines.DelicateCoroutinesApi
import afield.math.linalg.*
import java.nio.BufferOverflowException
import java.nio.ByteBuffer
import java.nio.FloatBuffer
import java.nio.IntBuffer
import kotlin.reflect.KProperty

Finding boxes by name, connecting them, and hanging scratch state off one.

named is how one box gets hold of another, searching the whole document from the root and skipping the tombstoned boxes a delete leaves behind. parent += child and parent -= child connect and disconnect, and connection is the edge property inheritance walks. Marker is a named property bag attached to a box, obtained with __m and created on first use, for state that belongs to part of a box rather than to the box itself; it lives in memory only and is not saved with the document.

One of the stdlib files, which the intro preamble star-imports into every box, so everything here can be called unqualified, with Box as the implicit receiver.

fun Set<Box>.named(s: String, ignoreDeleted: Boolean = true)

The first box in this set called s. Throws NoSuchElementException when there is none.

Deleted boxes are skipped by default — a deleted box lingers in the graph carrying a _tombstone property rather than vanishing, and without this filter you can find one and wonder why your edits go nowhere. Pass ignoreDeleted = false to include them.

fun Set<Box>.named(s: String, ignoreDeleted: Boolean = true) =
    this.first { it.name == s && (!ignoreDeleted || !it.getPropertyRaw("_tombstone").isTrue()) }

fun Box.named(s: String, ignoreDeleted: Boolean = true)

The box called s in this document — the usual way for one box to get hold of another.

Searches root.children, so it finds any box in the same document regardless of where the call is made from, and it is the document, not this box's own children. Names are not unique, so with two matches you get an arbitrary one; throws NoSuchElementException with none. Deleted (_tombstone) boxes are skipped unless you ask for them.

fun Box.named(s: String, ignoreDeleted: Boolean = true) =
    this.root.children.first { it.name == s && (!ignoreDeleted || !it.getPropertyRaw("_tombstone").isTrue()) }

operator fun Box.plusAssign(child: Box)

parent += child — connects child to this box, making it a child in the box graph.

Connection is what property inheritance walks, so this is how a box comes to see its parent's exported values. The graph is a DAG, not a tree: a box can be connected to several parents, and += adds rather than moves.

operator fun Box.plusAssign(child: Box) {
    this.connect(child)
}

operator fun Box.minusAssign(child: Box)

parent -= child — breaks the connection to child, without deleting either box.

operator fun Box.minusAssign(child: Box) {
    this.disconnect(child)
}

data class Marker(val name: String, val box: Box) : HasProperties, HasHierarchy

A named property bag attached to a box — somewhere to hang state that belongs to part of a box rather than to the box itself.

Obtained by name with Box.__m, which creates it on first use and returns the same instance thereafter, so it survives across frames while the box is running. Its properties live in a plain in-memory map: they are not saved with the document and are not visible to the property inspector, unlike a real box property.

It presents itself as a leaf of the box graph — no children, and its single parent is the owning box — so inheritance-shaped code can walk into it without special-casing.

data class Marker(val name: String, val box: Box) : HasProperties, HasHierarchy {

val properties = mutableMapOf<String, Any>()

This marker's state, as a plain map. In memory only; nothing here is persisted with the document.

override fun setPropertyRaw(name: String, value: Any, doResync: Boolean)

Stores a value under name. The doResync flag is ignored — a marker has no client to notify.

    override fun setPropertyRaw(name: String, value: Any, doResync: Boolean) {
        properties[name] = value
    }

override fun getPropertyRaw(name: String, default: Any?): Any?

The value stored under name, or default if this marker has never been given one.

    override fun getPropertyRaw(name: String, default: Any?): Any? {
        return properties[name] ?: default
    }

override fun removePropertyRaw(name: String, doResync: Boolean)

Erases name. The doResync flag is ignored — a marker has no client to notify.

    override fun removePropertyRaw(name: String, doResync: Boolean) {
        properties.remove(name)
    }

override var children: MutableSet<Box>

Always empty: a marker is a leaf. Assigning to it does nothing.

    override var children: MutableSet<Box>
        get() = mutableSetOf<Box>()
        set(value) {}

override var parents: MutableSet<Box>

Just the owning box, so a parent walk reaches it. Assigning to it does nothing.

    override var parents: MutableSet<Box>
        get() = mutableSetOf(box)
        set(value) {}
}

fun Box.__m(s: String, onCreation: Marker.() -> Unit

The Marker called s on this box, created on first use — the get-or-create for per-box scratch state.

__m("trail") { properties["points"] = mutableListOf<Vec3>() }: onCreation runs only the first time, which is what makes this safe to call every frame from inside an animation loop. The marker and its contents outlive an individual frame but not the process, and are not saved with the document.

fun Box.__m(s: String, onCreation: Marker.() -> Unit = {}) = this.markers.computeIfAbsent(s) {
    val m = Marker(s, box = this)
    m.onCreation()
    m
}