Skip to content

Animation

10 source file(s), 178 public symbol(s), 41 of them carrying KDoc. Everything below is the documentation that lives beside the code — the same text the completion documentation panel shows in the editor. A blank summary means the declaration has no KDoc yet, not that this page dropped it.

Motion.kt

src/jvmMain/kotlin/afield/animation/mocap/Motion.kt — read the source

Top-level functions

Declaration Summary
fun MotionPose.bones(skeleton: MotionSkeleton): FLine Parent-child segments in 3D; pass these to the existing Field line renderer and camera.
fun FLine.motionRoute(startTime: Double, duration: Double, count: Int = 41, headings: Boolean = false, distanceFraction: (Double) -> Double = { it }): List<TimedRootPoint> Existing FLine arc-length cursor; distanceFraction supplies timing separately from geometry.

MotionCoordinates

class MotionCoordinates(val sourceUnits: String = "m", val sourceHandedness: String = "right", val sourceUp: String = "+Y", val sourceForward: String = "+Z", sourceToField: List<Double> = listOf(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0), val metresPerUnit: Double = 1.0)

Motion uses metres, seconds, right handed Y-up coordinates and xyzw quaternions. This matches Field's glTF scene coordinates. Cameras and projection remain the caller's. sourceToField is a row-major orthogonal basis, also used for forces; moments are axial vectors.

Declaration Summary
val sourceUnits: String
val sourceHandedness: String
val sourceUp: String
val sourceForward: String
val metresPerUnit: Double
val sourceToField
val determinant: Float
fun position(p: Vec3): Vec3
fun direction(p: Vec3): Vec3
fun moment(p: Vec3): Vec3
fun rotation(q: Quat): Quat
fun json(): JSONObject

MotionBody

data class MotionBody(val name: String, val joint: String, val massKg: Double? = null)

A rigid body is explicitly named and mapped; a joint index is never a force-application body.

Declaration Summary
fun json()

MotionSkeleton

class MotionSkeleton(val id: String, val version: String, names: List<String>, parents: List<Int>, restTranslations: List<Double>, restRotations: List<Double>, val coordinates: MotionCoordinates = MotionCoordinates(), bodies: List<MotionBody> = emptyList())

Runtime joint order and rest transforms. Reuses Field's scene hierarchy rather than a new FK engine.

Declaration Summary
val id: String
val version: String
val coordinates: MotionCoordinates
val names
val parents
val restTranslations
val restRotations
val bodies
val hash: String
fun joint(name: String): Int
fun body(name: String): MotionBody
fun sceneSkeleton(): GltfSkinning.Skeleton
fun json()

MotionPose

class MotionPose(root: List<Double>, localRotations: List<Double>, worldPositions: List<Double>)

Lists are copied on construction. Display code may create mutable Field vectors without sharing worker state.

Declaration Summary
val root
val localRotations
val worldPositions
val size: Int
val hasRotations: Boolean
fun position(index: Int): Vec3
fun rotation(index: Int): Quat
fun json()

MotionFrame

class MotionFrame(val time: Double, val index: Long, val episode: Long, val revision: Long, val executed: MotionPose, val intended: MotionPose? = null, contacts: List<Double> = emptyList(), val contactProvenance: String = "unavailable", diagnostics: Map<String, Double> = emptyMap())

Declaration Summary
val time: Double, val index: Long, val episode: Long, val revision: Long
val executed: MotionPose, val intended: MotionPose?
val contacts
val diagnostics
fun json()

TimedRootPoint

data class TimedRootPoint(val time: Double, val x: Double, val z: Double, val heading: Double? = null)

Declaration Summary
fun json()

MotionAuthoring.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionAuthoring.kt — read the source

Used in: Generative Mocap — ARDY and CLoSD

Top-level functions

Declaration Summary
suspend fun <T> CompletableFuture<T>.awaitMotion(): T Await an optional worker without blocking Field's frame thread. Cancellation cancels opening too.
fun MotionSession.prompt(text: String, at: Double = editableTime) Change intention at the next editable time, or explicitly schedule at in session seconds.
fun MotionSession.route(path: FLine, duration: Duration, at: Double = editableTime, headings: Boolean = true, distanceFraction: (Double) -> Double = { it }): CompletableFuture<MotionApplied?> Sample Field's arc-length cursor, with geometry, duration, heading and speed envelope independent.
fun MotionSession.body(name: String): MotionBodyHandle A runtime physical body; resolving it checks the explicit body mapping, never a display-joint index.
fun MotionSession.joint(name: String) An animation joint. Holding it is kinematic conditioning, distinct from a physical tether.
suspend fun afield.runtime.lifecycle.Frames.playMotion(session: MotionSession, duration: Duration, draw: (MotionSnapshot) -> Unit) Present a bounded, wall-paced stretch of motion from a Frames coroutine. Inference stays on the session worker; draw sees interpolated snapshots and explicit lag. Duration rounds up to the backend's next complete token/control step. Late output holds; it never extrapolates a body.

Top-level values

Declaration Summary
val MotionSession.editableTime: Double First uncommitted session time. A currently running advance may move it further before an edit applies.

TetherSettings

class TetherSettings internal constructor(previous: MotionAttachment? = null)

Mutable only while authoring a command; vectors are copied into the immutable wire payload. SI units.

Declaration Summary
var point
var anchor: Vec3?
var velocity
var stiffness
var damping
var maxForce
var ramp: Duration
var breakForce
var breakDistance
var breakDuration: Duration
fun breakAbove(force: Double, forAtLeast: Duration = 100.milliseconds) Break on sustained pre-cap demand, not merely because the spring hits its force cap.
fun unbreakable() Keep the connection when its applied force saturates.

MotionBodyHandle

class MotionBodyHandle internal constructor(private val session: MotionSession, val mapping: MotionBody)

Declaration Summary
fun position(localPoint: Vec3 = Vec3()): Vec3 Current executed body-local point in world metres; localPoint is not a joint offset.
fun tether(id: String = "tether-${tetherIds.incrementAndGet()}", at: Double = session.editableTime, block: TetherSettings.() -> Unit = {}): MotionTether Capture the current point as anchor unless the block supplies TetherSettings.anchor.
fun impulse(impulse: Vec3, point: Vec3 = Vec3(), at: Double = session.editableTime) World impulse in N s, applied once at a body-local point.

MotionTether

class MotionTether internal constructor(private val session: MotionSession, private val body: MotionBodyHandle, parameters: MotionAttachment, val accepted: CompletableFuture<MotionApplied?>)

A named connection. accepted acknowledges scheduling; observation reports actual physics/reset events.

Declaration Summary
var parameters
fun tune(at: Double = session.editableTime, block: TetherSettings.() -> Unit): CompletableFuture<MotionApplied?>
fun moveTo(anchor: Vec3, velocity: Vec3 = Vec3(), at: Double = session.editableTime)
fun release(at: Double = session.editableTime): CompletableFuture<MotionApplied?>

MotionJointHandle

class MotionJointHandle internal constructor(private val session: MotionSession, val index: Int)

Declaration Summary
fun position(): Vec3
fun hold(duration: Duration, orientation: Boolean = false, at: Double = session.editableTime): CompletableFuture<MotionApplied?> Repeated future targets; ARDY requires an explicit root plan covering the same interval.
fun release(at: Double = session.editableTime)

TetherObservation

data class TetherObservation(val id: String, val body: String, val active: Boolean, val reason: String, val time: Double, val forceN: Double = 0.0, val rawForceN: Double = 0.0, val stretchMetres: Double = 0.0, val powerWatts: Double = 0.0, val maxForce: Double = 0.0, val point: List<Double> = emptyList(), val anchor: List<Double> = emptyList(), val force: List<Double> = emptyList())

Actual submitted point force and raw anchor residual, not inferred muscular effort.

Declaration Summary
val time: Double, val forceN: Double
val powerWatts: Double
val point: List<Double>

MotionTelemetry

class MotionTelemetry internal constructor()

Bounded observations of actual backend events. Reset and break invalidate every affected connection.

Declaration Summary
var lastEvent
fun tether(id: String)
fun tethers(): List<TetherObservation>

MotionCommands.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionCommands.kt — read the source

Top-level functions

Declaration Summary
fun pointTether(point: Vec3, centreOfMass: Vec3, comVelocity: Vec3, angularVelocity: Vec3, anchor: Vec3, anchorVelocity: Vec3 = Vec3(), stiffness: Double, damping: Double, maximumForce: Double, engagement: Double = 1.0): PointWrench Inspectable point-force math, independent of a simulator. Inputs are in one world frame, SI. point is measured from the actual COM, not the display joint or rigid-body origin. The simulator adapter must add this wrench to other forces and apply it for the documented step.

MotionOperation

enum class MotionOperation

Constants: PROMPT, ROOT_PLAN, JOINT_TARGETS, ATTACH, UPDATE_ATTACHMENT, DETACH, IMPULSE, ADVANCE, RESET.

MotionCapabilities

data class MotionCapabilities(val backend: String, val operations: Set<MotionOperation>, val details: JSONObject)

Declaration Summary
fun require(operation: MotionOperation)

JointTarget

data class JointTarget(val joint: String, val time: Double, val position: List<Double>, val orientation: List<Double>? = null)

Kinematic conditioning, in world coordinates; orientation is optional and separately recorded.

Declaration Summary
fun json()

MotionAttachment

data class MotionAttachment(val id: String, val body: String, val localPoint: List<Double>, val anchor: List<Double>, val stiffness: Double = 30.0, val damping: Double = 5.0, val maximumForce: Double = 50.0, val engagementSeconds: Double = .2, val breakForce: Double? = null, val breakError: Double? = null, val breakDuration: Double = .1, val anchorVelocity: List<Double> = listOf(0.0, 0.0, 0.0))

Force capped spring/damper. A break requires a sustained criterion; saturation alone never detaches.

Declaration Summary
val id: String, val body: String, val localPoint: List<Double>, val anchor: List<Double>
val stiffness: Double
val engagementSeconds: Double
val breakDuration: Double
val anchorVelocity: List<Double>
fun json()

PointWrench

data class PointWrench(val force: Vec3, val torque: Vec3, val velocity: Vec3, val rawMagnitude: Double, val powerWatts: Double)

MotionExamples.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionExamples.kt — read the source

MotionExamples

object MotionExamples

A diagnostic two-joint clip, explicitly unrelated to learned motion or simulated humanoids.

Declaration Summary
fun contractClip(): MotionClip
fun recordContractClip(directory: Path)

MotionPlayback.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionPlayback.kt — read the source

MotionSnapshot

data class MotionSnapshot(val frame: MotionFrame, val lagSeconds: Double, val held: Boolean)

MotionBuffer

class MotionBuffer(val capacity: Int = 8, val maxSpanSeconds: Double = .4)

Bounded live display buffer. Missing data holds the last pose and reports lag; no extrapolation. Reset/episode edges hold the earlier pose until the new frame's time. Reader and producer may differ.

Declaration Summary
var dropped: Long
fun clear()
fun add(frame: MotionFrame)
fun sample(time: Double): MotionSnapshot?
fun size(): Int

MotionClip

class MotionClip(val skeleton: MotionSkeleton, frames: List<MotionFrame>)

Immutable recorded poses, independent of model installations and seeds.

Declaration Summary
val frames
fun sample(time: Double): MotionSnapshot

MotionProcess.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionProcess.kt — read the source

Used in: Generative Mocap — ARDY and CLoSD

MotionProcessConfig

data class MotionProcessConfig(val backend: String = "closd", val home: Path = Path.of(System.getenv("FIELD_MOTION_HOME") ?: "${System.getProperty("user.home")}/.field4/motion"), val python: Path = home.resolve("environments/$backend/bin/python"), val source: Path = home.resolve(if (backend == "closd") "upstream/CLoSD" else "upstream/ardy"), val worker: Path = Path.of("optional/motion/worker.py").toAbsolutePath(), val overrides: List<String> = emptyList(), val startupTimeoutMs: Int = 180_000, val requestTimeoutMs: Int = 120_000, val offlineAssets: Boolean = true, val embeddingCache: Path = home.resolve("embeddings"), val encoderUrl: String = System.getenv("FIELD_MOTION_ENCODER_URL") ?: "http://127.0.0.1:9550/", val seed: Int = 0, val denoisingSteps: Int = 4)

Declaration Summary
val backend: String
val home: Path
val python: Path
val source: Path
val worker: Path
val overrides: List<String>
val startupTimeoutMs: Int
val requestTimeoutMs: Int
val offlineAssets: Boolean
val embeddingCache: Path
val encoderUrl: String
val seed: Int
val denoisingSteps: Int

CLoSD

object CLoSD

Starts a process, authenticates its capability/skeleton reply, and owns it through box stop/re-run.

Declaration Summary
fun openAsync(config: MotionProcessConfig = MotionProcessConfig()): CompletableFuture<MotionSession>

ARDY

object ARDY

Declaration Summary
fun openAsync(config: MotionProcessConfig = MotionProcessConfig(backend = "ardy")): CompletableFuture<MotionSession>

MotionRecording.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionRecording.kt — read the source

MotionRecorder

class MotionRecorder(val directory: Path, val skeleton: MotionSkeleton, configuration: JSONObject, val chunkFrames: Int = 256) : AutoCloseable

Portable .f4motion directory: JSON metadata/events and chunked little-endian float32 poses. No native Python objects or pickle. The manifest appears only after a clean close. Requested/applied/superseded commands and reset/termination diagnostics belong in event.

Declaration Summary
val chunkFrames: Int
fun event(event: JSONObject)
fun append(frame: MotionFrame)
override fun close()

MotionRecording

class MotionRecording(val directory: Path)

Streaming read keeps long recordings off the live buffer; clip is an explicit in-memory load.

Declaration Summary
val metadata: JSONObject
val skeleton: MotionSkeleton
fun frames(): Sequence<MotionFrame>
fun clip()
fun events(): Sequence<JSONObject>

MotionScoreSmoke.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionScoreSmoke.kt — read the source

MotionScoreSmoke

object MotionScoreSmoke

Real physical-body authoring, moving anchor, observation, reset and reused-handle ordering.

Declaration Summary
fun main(args: Array<String>)

MotionSession.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionSession.kt — read the source

MotionCommand

data class MotionCommand(val revision: Long, val operation: MotionOperation, val requestedTime: Double, val payload: JSONObject)

MotionApplied

data class MotionApplied(val appliedTime: Double?, val frameIndex: Long?, val details: JSONObject = JSONObject())

Null time/index means accepted for scheduling; a later update carries the actual applied event.

MotionUpdate

data class MotionUpdate(val frames: List<MotionFrame>, val events: List<JSONObject> = emptyList())

MotionBackend

interface MotionBackend : AutoCloseable

Blocking backend boundary, invoked only by MotionSession's owned daemon thread. Implementations must return bounded batches, honor explicit session times, and close promptly. An installed Python package alone must never advertise a working capability.

Declaration Summary
val skeleton: MotionSkeleton
val capabilities: MotionCapabilities
fun command(command: MotionCommand): MotionApplied
fun advance(seconds: Double): MotionUpdate

MotionSession

class MotionSession(private val backend: MotionBackend, val queueCapacity: Int = 32, val buffer: MotionBuffer = MotionBuffer()) : AutoCloseable

One small optional motion facade. No inference or simulator work runs on Field's frame thread. Commands are bounded; path/target replacements coalesce only before the next ordered event. Subscriptions run on the worker thread and must be short; rendering reads buffer snapshots. Construction in a box registers with its existing RunScope (stop/re-run/document close). Live ARDY/CLoSD drivers require separately validated model environments; replay needs neither.

Declaration Summary
val buffer: MotionBuffer
val telemetry
var latest: MotionFrame?
var failure: Throwable?
var closed
fun subscribe(listener: (MotionFrame) -> Unit): AutoCloseable
fun record(directory: Path, configuration: JSONObject): MotionRecorder
fun setPrompt(text: String, effectiveTime: Double)
fun setRootPlan(points: List<TimedRootPoint>, effectiveTime: Double): CompletableFuture<MotionApplied?>
fun setJointTargets(targets: List<JointTarget>, effectiveTime: Double): CompletableFuture<MotionApplied?>
fun attach(attachment: MotionAttachment, effectiveTime: Double): CompletableFuture<MotionApplied?>
fun updateAttachment(attachment: MotionAttachment, effectiveTime: Double): CompletableFuture<MotionApplied?>
fun detach(id: String, effectiveTime: Double)
fun applyImpulse(body: String, localPoint: List<Double>, impulse: List<Double>, effectiveTime: Double): CompletableFuture<MotionApplied?> Point is body-local metres; impulse is a world vector in N s.
fun reset(effectiveTime: Double)
fun advance(seconds: Double): CompletableFuture<MotionUpdate>
fun markVisible(frame: MotionFrame): Double? Call when a renderer actually displays a revision; timings do not treat receipt as visibility.
fun visibleDelayPercentile(fraction: Double): Double?
fun queued(): Int
override fun close() Nonblocking on Field's runloop; the backend must make its close unblock pending I/O.

MotionView.kt

src/jvmMain/kotlin/afield/animation/mocap/MotionView.kt — read the source

Used in: Generative Mocap — ARDY and CLoSD

Top-level functions

Declaration Summary
fun Box.showMotion(frame: MotionFrame, skeleton: MotionSkeleton, channel: String = "motion", reference: Boolean = true, contacts: Boolean = true, overlays: List<FLine> = emptyList(), height: Int = 420, follow: Boolean = false, followHalfLife: Double = .4, followVertical: Boolean = false, followEpoch: String = frame.episode.toString()) Existing Field 3D viewer/camera, streamed FLine geometry; repeated draws preserve the user's orbit.
fun TetherObservation.overlays(forceScale: Double = .00015): List<FLine> Red spring/anchor and an amber submitted-force vector, scaled in metres per Newton for drawing.