Skip to documentation
SLOP

tiny.rig.curve

Reference tiny.rig curve

Defined in tiny.rig.

Parameter curves: clips of plain values that drive shape and material parameters, with a mixer, a state machine, a blend space and notify tracks over them.

API (55)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.rigcurve
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: fun/rig/src/curve/machine.zig:39

zig
pub const ParameterMachine = struct {    states: []ParameterState,    state_count: usize,    state_capacity: usize,    transitions: []Transition,    transition_count: usize,    transition_capacity: usize,    mixer: *ParameterMixer,    current: ?usize = null,    current_layer: ?usize = null,    target: ?usize = null,    target_layer: ?usize = null,    blend_duration: f32 = 0,    blend_elapsed: f32 = 0,    pub fn init(        allocator: Allocator,        mixer: *ParameterMixer,        max_states: usize,        max_transitions: usize,    ) !ParameterMachine {        const states = try allocator.alloc(ParameterState, max_states);        const transitions = try allocator.alloc(Transition, max_transitions);        return .{            .states = states,            .state_count = 0,            .state_capacity = max_states,            .transitions = transitions,            .transition_count = 0,            .transition_capacity = max_transitions,            .mixer = mixer,        };    }    pub fn deinit(self: *ParameterMachine, allocator: Allocator) void {        allocator.free(self.states);        allocator.free(self.transitions);        self.* = undefined;    }    pub fn addState(self: *ParameterMachine, clip: *const ParameterClip, state_name: []const u8, opts: struct {        speed: f32 = 1.0,        loop: bool = true,    }) !usize {        if (self.state_count >= self.state_capacity) return error.TooManyStates;        const idx = self.state_count;        var state = ParameterState{            .clip = clip,            .speed = opts.speed,            .loop = opts.loop,        };        const copy_len = @min(state_name.len, 32);        @memcpy(state.name_buf[0..copy_len], state_name[0..copy_len]);        state.name_len = copy_len;        self.states[idx] = state;        self.state_count += 1;        return idx;    }    pub fn setInitialState(self: *ParameterMachine, state_idx: usize) !void {        if (state_idx >= self.state_count) return error.InvalidState;        self.resetRuntimeState();        const st = &self.states[state_idx];        const layer = self.mixer.addLayer(st.clip, .{            .speed = st.speed,            .loop = st.loop,        }) catch return error.MixerFull;        self.current = state_idx;        self.current_layer = layer;    }    pub fn addTransition(self: *ParameterMachine, tr: Transition) !void {        if (self.transition_count >= self.transition_capacity) return error.TooManyTransitions;        self.transitions[self.transition_count] = tr;        self.transition_count += 1;    }    pub fn isTransitioning(self: *const ParameterMachine) bool {        return self.target != null;    }    pub fn currentState(self: *const ParameterMachine) ?usize {        return self.current;    }    pub fn fireTrigger(self: *ParameterMachine, trigger_name: []const u8) void {        const cur = self.current orelse return;        if (self.isTransitioning()) return;        for (self.transitions[0..self.transition_count]) |*tr| {            if (tr.from_state != cur) continue;            const tr_trigger = tr.trigger() orelse continue;            if (!std.mem.eql(u8, tr_trigger, trigger_name)) continue;            if (tr.condition) |cond| {                if (!cond()) continue;            }            self.beginTransition(tr);            return;        }    }    pub fn update(self: *ParameterMachine, dt: f32) void {        if (self.current == null) return;        self.mixer.advance(dt);        if (self.isTransitioning()) {            self.advanceBlend(dt);        } else {            self.checkAutoTransitions();            self.checkConditionTransitions();        }    }    fn beginTransition(self: *ParameterMachine, tr: *const Transition) void {        const st = &self.states[tr.to_state];        const target_layer = self.mixer.addLayer(st.clip, .{            .weight = 0.0,            .speed = st.speed,            .loop = st.loop,        }) catch return;        self.target = tr.to_state;        self.blend_duration = tr.blend_duration;        self.blend_elapsed = 0;        self.target_layer = target_layer;    }    fn advanceBlend(self: *ParameterMachine, dt: f32) void {        self.blend_elapsed += dt;        if (self.blend_duration <= 0 or self.blend_elapsed >= self.blend_duration) {            if (self.current_layer) |cl| self.mixer.removeLayer(cl);            if (self.target_layer) |tl| {                self.mixer.layers[tl].weight = 1.0;            }            self.current = self.target;            self.current_layer = self.target_layer;            self.target = null;            self.target_layer = null;            self.blend_duration = 0;            self.blend_elapsed = 0;        } else {            const t = self.blend_elapsed / self.blend_duration;            if (self.current_layer) |cl| {                self.mixer.layers[cl].weight = 1.0 - t;            }            if (self.target_layer) |tl| {                self.mixer.layers[tl].weight = t;            }        }    }    fn resetRuntimeState(self: *ParameterMachine) void {        if (self.current_layer) |layer| {            self.mixer.removeLayer(layer);        }        if (self.target_layer) |layer| {            if (self.current_layer) |current_layer| {                if (current_layer != layer) {                    self.mixer.removeLayer(layer);                }            } else {                self.mixer.removeLayer(layer);            }        }        self.current = null;        self.current_layer = null;        self.target = null;        self.target_layer = null;        self.blend_duration = 0;        self.blend_elapsed = 0;    }    fn checkAutoTransitions(self: *ParameterMachine) void {        const cur = self.current orelse return;        const cl = self.current_layer orelse return;        const layer = &self.mixer.layers[cl];        if (layer.loop or layer.time < layer.clip.duration) return;        for (self.transitions[0..self.transition_count]) |*tr| {            if (tr.from_state != cur) continue;            if (tr.has_trigger or tr.condition != null) continue;            self.beginTransition(tr);            return;        }    }    fn checkConditionTransitions(self: *ParameterMachine) void {        const cur = self.current orelse return;        for (self.transitions[0..self.transition_count]) |*tr| {            if (tr.from_state != cur) continue;            if (tr.has_trigger) continue;            const cond = tr.condition orelse continue;            if (cond()) {                self.beginTransition(tr);                return;            }        }    }};

Source: fun/rig/src/curve/machine.zig:27

zig
pub const ParameterState = struct {    name_buf: [32]u8 = undefined,    name_len: usize = 0,    clip: *const ParameterClip,    speed: f32 = 1.0,    loop: bool = true,    pub fn name(self: *const ParameterState) []const u8 {        return self.name_buf[0..self.name_len];    }};

Source: fun/rig/src/curve/machine.zig:12

zig
pub const Transition = struct {    from_state: usize,    to_state: usize,    trigger_buf: [32]u8 = undefined,    trigger_len: usize = 0,    has_trigger: bool = false,    blend_duration: f32 = 0.2,    condition: ?ConditionFn = null,    pub fn trigger(self: *const Transition) ?[]const u8 {        if (self.has_trigger) return self.trigger_buf[0..self.trigger_len];        return null;    }};

Source: fun/rig/src/curve/mixer.zig:19

zig
pub const MixerLayer = struct {    clip: *const ParameterClip,    weight: f32 = 1.0,    time: f32 = 0.0,    speed: f32 = 1.0,    loop: bool = false,    active: bool = true,};

Source: fun/rig/src/curve/mixer.zig:28

zig
pub const ParameterMixer = struct {    layers: []MixerLayer,    layer_count: usize,    capacity: usize,    pub fn init(allocator: Allocator, max_layers: usize) !ParameterMixer {        const layers = try allocator.alloc(MixerLayer, max_layers);        return .{ .layers = layers, .layer_count = 0, .capacity = max_layers };    }    pub fn deinit(self: *ParameterMixer, allocator: Allocator) void {        allocator.free(self.layers);        self.* = undefined;    }    pub fn addLayer(self: *ParameterMixer, clip: *const ParameterClip, opts: struct {        weight: f32 = 1.0,        speed: f32 = 1.0,        loop: bool = false,    }) !usize {        for (0..self.layer_count) |i| {            if (!self.layers[i].active) {                self.layers[i] = .{                    .clip = clip,                    .weight = opts.weight,                    .speed = opts.speed,                    .loop = opts.loop,                };                return i;            }        }        if (self.layer_count >= self.capacity) return error.TooManyLayers;        const idx = self.layer_count;        self.layers[idx] = .{            .clip = clip,            .weight = opts.weight,            .speed = opts.speed,            .loop = opts.loop,        };        self.layer_count += 1;        return idx;    }    pub fn removeLayer(self: *ParameterMixer, index: usize) void {        if (index < self.layer_count) {            self.layers[index].active = false;        }    }    pub fn advance(self: *ParameterMixer, dt: f32) void {        for (self.layers[0..self.layer_count]) |*layer| {            if (!layer.active) continue;            layer.time += dt * layer.speed;            if (layer.loop and layer.clip.duration > 0) {                layer.time = @mod(layer.time, layer.clip.duration);            }        }    }    pub fn blendSlot(        self: *const ParameterMixer,        slot_idx: usize,        blend_mode: SlotBlendMode,        out: []f32,        tmp: []f32,    ) void {        const dim = out.len;        for (out) |*v| v.* = 0;        var weight_sum: f32 = 0;        var first = true;        for (self.layers[0..self.layer_count]) |*layer| {            if (!layer.active or layer.weight <= 0) continue;            if (slot_idx >= layer.clip.curve_count) continue;            layer.clip.evalSlot(slot_idx, layer.time, tmp[0..dim]);            weight_sum += layer.weight;            if (dim == 1 and blend_mode == .slerp) {                if (first) {                    out[0] = tmp[0];                    first = false;                } else {                    const t_blend = layer.weight / weight_sum;                    out[0] = angleSlerp(out[0], tmp[0], t_blend);                }            } else {                for (0..dim) |d| {                    out[d] += layer.weight * tmp[d];                }            }        }        if (blend_mode != .slerp or dim != 1) {            if (weight_sum > 0) {                for (0..dim) |d| {                    out[d] /= weight_sum;                }            }        }    }};

Source: fun/rig/src/curve/mixer.zig:9

zig
pub const SlotBlendMode = enum {    lerp,    slerp,};

Source: fun/rig/src/curve/parameter.zig:15

zig
pub const ParameterClip = struct {    name_buf: [64]u8 = undefined,    name_len: usize = 0,    duration: f32,    curves: []SlotCurve,    curve_count: usize,    capacity: usize,    pub fn init(allocator: Allocator, clip_name: []const u8, duration: f32, max_curves: usize) !ParameterClip {        const curves = try allocator.alloc(SlotCurve, max_curves);        var clip = ParameterClip{            .duration = duration,            .curves = curves,            .curve_count = 0,            .capacity = max_curves,        };        const copy_len = @min(clip_name.len, 64);        @memcpy(clip.name_buf[0..copy_len], clip_name[0..copy_len]);        clip.name_len = copy_len;        return clip;    }    pub fn deinit(self: *ParameterClip, allocator: Allocator) void {        for (self.curves[0..self.curve_count]) |*c| {            var spline = c.spline;            spline.deinit(allocator);        }        allocator.free(self.curves);        self.* = undefined;    }    pub fn name(self: *const ParameterClip) []const u8 {        return self.name_buf[0..self.name_len];    }    pub fn addCurve(        self: *ParameterClip,        allocator: Allocator,        times: []const f32,        values: []const f32,        dim: usize,    ) !usize {        if (self.curve_count >= self.capacity) return error.TooManyCurves;        const n_kf = times.len;        if (n_kf < 2) return error.TooFewKeyframes;        const spline = try CatmullRomSpline.init(allocator, values, dim);        const n_seg = n_kf - 1;        const t_span = times[n_kf - 1] - times[0];        const time_scale: f32 = if (t_span > 0) @as(f32, @floatFromInt(n_seg)) / t_span else 0.0;        const idx = self.curve_count;        self.curves[idx] = .{ .spline = spline, .time_scale = time_scale, .dim = dim };        self.curve_count += 1;        return idx;    }    pub fn evalSlot(self: *const ParameterClip, slot_idx: usize, t: f32, out: []f32) void {        const curve = &self.curves[slot_idx];        const clamped = clamp(t, 0.0, self.duration);        const spline_t = clamped * curve.time_scale;        curve.spline.eval(spline_t, out);    }};

Source: fun/rig/src/curve/parameter.zig:9

zig
pub const SlotCurve = struct {    spline: CatmullRomSpline,    time_scale: f32,    dim: usize,};

Source: fun/rig/src/curve/space.zig:11

zig
pub const BlendSample = struct {    clip: *const ParameterClip,    parameter: f32,    speed: f32 = 1.0,};

Source: fun/rig/src/curve/space.zig:27

zig
pub const BlendSpace1D = struct {    samples: std.ArrayListUnmanaged(BlendSample) = .empty,    capacity: usize,    pub fn init(capacity: usize) BlendSpace1D {        return .{ .capacity = capacity };    }    pub fn deinit(self: *BlendSpace1D, allocator: Allocator) void {        self.samples.deinit(allocator);        self.* = .{            .capacity = 0,        };    }    pub fn sampleCount(self: *const BlendSpace1D) u32 {        return @intCast(self.samples.items.len);    }    pub fn addSample(        self: *BlendSpace1D,        allocator: Allocator,        clip: *const ParameterClip,        parameter: f32,        speed: f32,    ) !void {        if (self.samples.items.len >= self.capacity) return error.TooManySamples;        try self.samples.append(allocator, .{            .clip = clip,            .parameter = parameter,            .speed = speed,        });        std.mem.sort(BlendSample, self.samples.items, {}, blendSampleLessThan);    }    pub fn evaluateSlot(        self: *const BlendSpace1D,        parameter: f32,        slot_idx: usize,        time: f32,        blend_mode: SlotBlendMode,        out: []f32,        tmp: []f32,    ) !void {        if (self.samples.items.len == 0) return error.EmptyBlendSpace;        if (out.len == 0 or tmp.len < out.len) return error.BufferTooSmall;        const sample_pair = self.findPair(parameter);        const left = sample_pair.left;        const right = sample_pair.right;        if (slot_idx >= left.clip.curve_count) return error.InvalidSlot;        left.clip.evalSlot(slot_idx, time * left.speed, out);        if (left.clip == right.clip and sample_pair.alpha == 0.0) return;        if (slot_idx >= right.clip.curve_count) return error.InvalidSlot;        right.clip.evalSlot(slot_idx, time * right.speed, tmp[0..out.len]);        if (out.len == 1 and blend_mode == .slerp) {            out[0] = angleSlerp(out[0], tmp[0], sample_pair.alpha);            return;        }        for (out, tmp[0..out.len]) |*dst, src| {            dst.* = (1.0 - sample_pair.alpha) * dst.* + sample_pair.alpha * src;        }    }    fn findPair(self: *const BlendSpace1D, parameter: f32) BlendPair {        const items = self.samples.items;        if (items.len == 1 or parameter <= items[0].parameter) {            return .{ .left = items[0], .right = items[0], .alpha = 0.0 };        }        if (parameter >= items[items.len - 1].parameter) {            const tail = items[items.len - 1];            return .{ .left = tail, .right = tail, .alpha = 0.0 };        }        for (items[0 .. items.len - 1], items[1..]) |left, right| {            if (parameter < left.parameter or parameter > right.parameter) continue;            const span = right.parameter - left.parameter;            const alpha = if (@abs(span) <= 1e-6)                0.0            else                clamp01((parameter - left.parameter) / span);            return .{                .left = left,                .right = right,                .alpha = alpha,            };        }        const tail = items[items.len - 1];        return .{ .left = tail, .right = tail, .alpha = 0.0 };    }};

Source: fun/rig/src/curve/space.zig:123

zig
pub const NotifyEvent = struct {    id: u32,    time: f32,};

Source: fun/rig/src/curve/space.zig:132

zig
pub const NotifyTrack = struct {    events: std.ArrayListUnmanaged(NotifyEvent) = .empty,    capacity: usize,    pub fn init(capacity: usize) NotifyTrack {        return .{ .capacity = capacity };    }    pub fn deinit(self: *NotifyTrack, allocator: Allocator) void {        self.events.deinit(allocator);        self.* = .{            .capacity = 0,        };    }    pub fn addNotify(        self: *NotifyTrack,        allocator: Allocator,        event_id: u32,        time: f32,    ) !void {        if (self.events.items.len >= self.capacity) return error.TooManyEvents;        try self.events.append(allocator, .{            .id = event_id,            .time = time,        });        std.mem.sort(NotifyEvent, self.events.items, {}, notifyEventLessThan);    }    pub fn queryRange(        self: *const NotifyTrack,        start_time: f32,        end_time: f32,        duration: f32,        loop: bool,        out_ids: []u32,        out_times: []f32,    ) !u32 {        if (out_ids.len != out_times.len) return error.BufferTooSmall;        if (loop and duration <= 0.0) return error.InvalidDuration;        var count: usize = 0;        if (!loop or end_time >= start_time) {            try collectRange(self.events.items, start_time, end_time, out_ids, out_times, &count);            return @intCast(count);        }        try collectRange(self.events.items, start_time, duration, out_ids, out_times, &count);        try collectRange(self.events.items, 0.0, end_time, out_ids, out_times, &count);        return @intCast(count);    }};

Source: fun/rig/src/curve/spline.zig:91

zig
pub const CatmullRomSpline = struct {    data: []f32,    dim: usize,    n_points: usize,    pub fn init(allocator: Allocator, points: []const f32, dim: usize) !CatmullRomSpline {        const n = points.len / dim;        if (n < 2) return error.TooFewPoints;        const total = n * dim * 2;        const data = try allocator.alloc(f32, total);        @memcpy(data[0 .. n * dim], points);        const tangent_base = n * dim;        for (0..n) |i| {            const dst = data[tangent_base + i * dim ..][0..dim];            if (i == 0) {                const p0 = data[0..dim];                const p1 = data[dim .. 2 * dim];                for (0..dim) |d| dst[d] = p1[d] - p0[d];            } else if (i == n - 1) {                const pm = data[(n - 2) * dim ..][0..dim];                const pl = data[(n - 1) * dim ..][0..dim];                for (0..dim) |d| dst[d] = pl[d] - pm[d];            } else {                const pp = data[(i - 1) * dim ..][0..dim];                const pn = data[(i + 1) * dim ..][0..dim];                for (0..dim) |d| dst[d] = 0.5 * (pn[d] - pp[d]);            }        }        return .{ .data = data, .dim = dim, .n_points = n };    }    pub fn deinit(self: *CatmullRomSpline, allocator: Allocator) void {        allocator.free(self.data);        self.* = undefined;    }    fn pointSlice(self: CatmullRomSpline, i: usize) []const f32 {        return self.data[i * self.dim ..][0..self.dim];    }    fn tangentSlice(self: CatmullRomSpline, i: usize) []const f32 {        const base = self.n_points * self.dim;        return self.data[base + i * self.dim ..][0..self.dim];    }    fn segment(self: CatmullRomSpline, i: usize) CubicHermiteSegment {        return .{            .p0 = self.pointSlice(i),            .m0 = self.tangentSlice(i),            .p1 = self.pointSlice(i + 1),            .m1 = self.tangentSlice(i + 1),            .dim = self.dim,        };    }    pub fn eval(self: CatmullRomSpline, t: f32, out: []f32) void {        const n_seg = self.n_points - 1;        const n_seg_f: f32 = @floatFromInt(n_seg);        if (t <= 0.0) {            @memcpy(out[0..self.dim], self.pointSlice(0));            return;        }        if (t >= n_seg_f) {            @memcpy(out[0..self.dim], self.pointSlice(self.n_points - 1));            return;        }        var seg_idx: usize = @intFromFloat(t);        if (seg_idx >= n_seg) seg_idx = n_seg - 1;        const local_t = t - @as(f32, @floatFromInt(seg_idx));        self.segment(seg_idx).eval(local_t, out);    }};

Source: fun/rig/src/curve/spline.zig:71

zig
pub const CubicHermiteSegment = struct {    p0: []const f32,    m0: []const f32,    p1: []const f32,    m1: []const f32,    dim: usize,    pub fn eval(self: CubicHermiteSegment, t: f32, out: []f32) void {        const t2 = t * t;        const t3 = t2 * t;        const h00 = 2.0 * t3 - 3.0 * t2 + 1.0;        const h10 = t3 - 2.0 * t2 + t;        const h01 = -2.0 * t3 + 3.0 * t2;        const h11 = t3 - t2;        for (0..self.dim) |i| {            out[i] = h00 * self.p0[i] + h10 * self.m0[i] + h01 * self.p1[i] + h11 * self.m1[i];        }    }};

Source: fun/rig/src/curve/machine.zig:10

zig
pub const ConditionFn = *const fn () bool;
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.sdfii.src.animation.emissiontest: FSM slot appends proposal throu...curve.ParameterMachineaddState
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...curve.ParameterMachineaddTransition
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...curve.ParameterMachinecurrentState
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.sdfii.src.animation.emissiontest: FSM slot appends proposal throu...curve.ParameterMachinedeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...private; no linkfun.rig.src.curve.machine.ParameterMachinebeginTransitioncurve.ParameterMachineisTransitioningcurve.ParameterMachinefireTrigger
Static calls · unresolved targets: 1 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...private; no linkfun.sdfii.src.animation.abisdfii anim fsm createtest; no linkfun.sdfii.src.animation.emissiontest: FSM slot appends proposal throu...curve.ParameterMachineinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callscurve.ParameterMachinefireTriggercurve.ParameterMachineupdatetest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggercurve.ParameterMachineisTransitioning
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.sdfii.src.animation.emissiontest: FSM slot appends proposal throu...private; no linkfun.rig.src.curve.machine.ParameterMachineresetRuntimeStatecurve.ParameterMixeraddLayercurve.ParameterMachinesetInitialState
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...private; no linkfun.rig.src.curve.machine.ParameterMachineadvanceBlendprivate; no linkfun.rig.src.curve.machine.ParameterMachinecheckAutoTransitionsprivate; no linkfun.rig.src.curve.machine.ParameterMachinecheckConditionTransitionscurve.ParameterMachineisTransitioningcurve.ParameterMixeradvancecurve.ParameterMachineupdate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate; no linkfun.rig.src.curve.machine.ParameterMachinebeginTransitioncurve.ParameterMachinesetInitialStatetest; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.sdfii.src.animation.emissiontest: mixer slot proposal matches dir...curve.ParameterMixeraddLayer
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callscurve.ParameterMachineupdatecurve.ParameterMixeradvance
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.sdfii.src.animation.emissiontest: mixer slot proposal matches dir...curveangleSlerpcurve.ParameterMixerblendSlot
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.sdfii.src.animation.emissiontest: FSM slot appends proposal throu...test; no linkfun.sdfii.src.animation.emissiontest: mixer slot proposal matches dir...curve.ParameterMixerdeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50private; no linkfun.sdfii.src.animation.abisdfii anim mixer create+2 morecurve.ParameterMixerinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsprivate; no linkfun.rig.src.curve.machine.ParameterMachineadvanceBlendprivate; no linkfun.rig.src.curve.machine.ParameterMachineresetRuntimeStatecurve.ParameterMixerremoveLayer
Static calls · unresolved targets: 0 · external targets: 0.

Source: fun/rig/src/curve/mixer.zig:14

zig
pub fn inferBlendMode(param_name: []const u8) SlotBlendMode {    if (std.mem.eql(u8, param_name, "angle")) return .slerp;    return .lerp;}
Called byCallstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.rig.src.curve.parametertest: cubic curve evaluation at midpo...+5 morecurve.CatmullRomSplineinitcurve.ParameterClipaddCurve
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.rig.src.curve.parametertest: cubic curve evaluation at midpo...+5 morecurve.ParameterClipdeinit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest; no linkfun.rig.src.curve.parametertest: cubic curve evaluation at midpo...test; no linkfun.rig.src.curve.parametertest: linear interpolation between tw...private; no linkfun.rig.src.curve.parameterclampcurve.ParameterClipevalSlot
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.machinetest: FSM condition-based transitiontest; no linkfun.rig.src.curve.machinetest: FSM state transition on triggertest; no linkfun.rig.src.curve.machinetest: mixer reuses inactive slots acr...test; no linkfun.rig.src.curve.mixertest: mixer blending two clips 50/50test; no linkfun.rig.src.curve.parametertest: cubic curve evaluation at midpo...+6 morecurve.ParameterClipinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: blend space 1d interpolates nei...curve.BlendSpace1DaddSample
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: blend space 1d interpolates nei...curve.BlendSpace1Ddeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest; no linkfun.rig.src.curve.spacetest: blend space 1d interpolates nei...private; no linkfun.rig.src.curve.space.BlendSpace1DfindPaircurveangleSlerpcurve.BlendSpace1DevaluateSlot
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: blend space 1d interpolates nei...private; no linkfun.sdfii.src.animation.abisdfii anim blend space1d createcurve.BlendSpace1Dinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: notify track wraps looping quer...curve.NotifyTrackaddNotify
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: notify track wraps looping quer...curve.NotifyTrackdeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest; no linkfun.rig.src.curve.spacetest: notify track wraps looping quer...private; no linkfun.sdfii.src.animation.abisdfii anim notify track createcurve.NotifyTrackinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest; no linkfun.rig.src.curve.spacetest: notify track wraps looping quer...private; no linkfun.rig.src.curve.spacecollectRangecurve.NotifyTrackqueryRange
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate; no linkfun.rig.src.curve.spline.CatmullRomSplinepointSliceprivate; no linkfun.rig.src.curve.spline.CatmullRomSplinesegmentcurve.CatmullRomSplineeval
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callscurve.ParameterClipaddCurvecurve.CatmullRomSplineinit
Static calls · unresolved targets: 0 · external targets: 1.

Source: fun/rig/src/curve/spline.zig:30

zig
pub fn PeriodicSpline(comptime point_count: usize, comptime dimension: usize) type {    if (point_count < 4) @compileError("periodic splines require at least four points");    if (dimension == 0) @compileError("periodic splines require at least one dimension");    return struct {        points: [point_count][dimension]f32,        pub fn sample(self: @This(), phase: f32) [dimension]f32 {            std.debug.assert(std.math.isFinite(phase));            const wrapped = phase - @floor(phase);            const scaled = wrapped * @as(f32, @floatFromInt(point_count));            const segment: usize = @intFromFloat(scaled);            const local = scaled - @as(f32, @floatFromInt(segment));            const p0 = self.points[(segment + point_count - 1) % point_count];            const p1 = self.points[segment];            const p2 = self.points[(segment + 1) % point_count];            const p3 = self.points[(segment + 2) % point_count];            const local2 = local * local;            const local3 = local2 * local;            var result: [dimension]f32 = undefined;            for (0..dimension) |axis| {                result[axis] = 0.5 * (2.0 * p1[axis] +                    (-p0[axis] + p2[axis]) * local +                    (2.0 * p0[axis] - 5.0 * p1[axis] + 4.0 * p2[axis] - p3[axis]) * local2 +                    (-p0[axis] + 3.0 * p1[axis] - 3.0 * p2[axis] + p3[axis]) * local3);            }            return result;        }    };}
Called byCallsNo direct callstest; no linkfun.rig.src.curve.splinetest: periodic spline passes through ...curvePeriodicSpline
Static calls · unresolved targets: 0 · external targets: 0.

Source: fun/rig/src/curve/spline.zig:13

zig
pub fn angleSlerp(a: f32, b: f32, t: f32) f32 {    const two_pi: f32 = 2.0 * math.pi;    var diff = @mod(b - a, two_pi);    if (diff > math.pi) {        diff -= two_pi;    }    return a + diff * t;}
Called byCallsNo direct callscurve.ParameterMixerblendSlotcurve.BlendSpace1DevaluateSlotcurveangleSlerp
Static calls · unresolved targets: 0 · external targets: 0.

Source: fun/rig/src/curve/spline.zig:9

zig
pub fn scalarLerp(a: f32, b: f32, t: f32) f32 {    return a + (b - a) * t;}

Source: fun/rig/src/curve/spline.zig:22

zig
pub fn vec3Lerp(a: [3]f32, b: [3]f32, t: f32) [3]f32 {    return .{        a[0] + (b[0] - a[0]) * t,        a[1] + (b[1] - a[1]) * t,        a[2] + (b[2] - a[2]) * t,    };}

Source: fun/rig/src/curve/root.zig

zig
//! Parameter curves: clips of plain values that drive shape and material//! parameters, with a mixer, a state machine, a blend space and notify//! tracks over them. Joint poses are `rig.JointClip`'s; these clips carry//! no joints.const machine = @import("machine.zig");const mixer = @import("mixer.zig");const parameter = @import("parameter.zig");const space = @import("space.zig");const spline = @import("spline.zig");pub const scalarLerp = spline.scalarLerp;pub const angleSlerp = spline.angleSlerp;pub const vec3Lerp = spline.vec3Lerp;pub const PeriodicSpline = spline.PeriodicSpline;pub const CubicHermiteSegment = spline.CubicHermiteSegment;pub const CatmullRomSpline = spline.CatmullRomSpline;pub const SlotCurve = parameter.SlotCurve;pub const ParameterClip = parameter.ParameterClip;pub const SlotBlendMode = mixer.SlotBlendMode;pub const inferBlendMode = mixer.inferBlendMode;pub const MixerLayer = mixer.MixerLayer;pub const ParameterMixer = mixer.ParameterMixer;pub const ConditionFn = machine.ConditionFn;pub const Transition = machine.Transition;pub const ParameterState = machine.ParameterState;pub const ParameterMachine = machine.ParameterMachine;pub const BlendSample = space.BlendSample;pub const BlendSpace1D = space.BlendSpace1D;pub const NotifyEvent = space.NotifyEvent;pub const NotifyTrack = space.NotifyTrack;

Source: fun/rig/src/root.zig:23

zig
pub const curve = @import("curve/root.zig");

Complete caller list for curve.ParameterMixer.init

7 direct callers.

Complete caller list for curve.ParameterClip.addCurve

10 direct callers.

Complete caller list for curve.ParameterClip.deinit

10 direct callers.

Complete caller list for curve.ParameterClip.init

11 direct callers.

Audit

Definitions56
Public names56
Members62
Version26.7.0
Revisiondaab053ee433