Skip to content
Oakfield Operator Calculus Function Reference Site

Coupling Operators

sim_add_mask_operator(ctx, input, mask, out, opts)

Gate a field by a mask with optional feathering. Implements selective region processing, apertures, conditional field operations, and smooth spatial transitions.

sim_add_mask_operator(ctx, input, mask, output, [options]) -> operator

Returns: Operator handle (userdata)

Hard threshold (feather = 0):

outi={inputiif miTfillotherwise\text{out}_i = \begin{cases} \text{input}_i & \text{if } m_i \geq T \\ \text{fill} & \text{otherwise} \end{cases}

where mim_i is the mask value and TT is the threshold.

Soft feathering (feather > 0):

αi=smoothstep(mi(Tf)2f)\alpha_i = \text{smoothstep}\left(\frac{m_i - (T - f)}{2f}\right) outi=αiinputi+(1αi)fill\text{out}_i = \alpha_i \cdot \text{input}_i + (1 - \alpha_i) \cdot \text{fill}

where ff is the feather half-width and smoothstep provides a smooth transition:

smoothstep(t)=3t22t3for t[0,1]\text{smoothstep}(t) = 3t^2 - 2t^3 \quad \text{for } t \in [0, 1]

Inverted mode:

Swaps the active and inactive regions (mask < threshold becomes active).

ParameterTypeDefaultRangeDescription
modeenum "apply"apply, invertMask application mode
thresholddouble 0.5unboundedMask threshold for gating
featherdouble 0.0≥0Soft transition half-width
fill_valuedouble 0.0unboundedValue when mask is inactive (real part)
fill_value_imdouble 0.0unboundedImaginary fill value for complex output
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt
  • Operates elementwise; no boundary handling required
  • Input and mask fields must have compatible dimensions
  • Complex fields: fill value uses both fill_value and fill_value_im
  • Unconditionally stable (bounded output for bounded input)
  • Hard threshold (feather = 0) produces discontinuous results
  • Soft feathering (feather > 0) provides C¹ continuous transitions
  • Larger feather values produce smoother but wider transition zones
  • Hard threshold: simple comparison, very fast
  • Soft feathering: requires smoothstep computation per sample
  • Inverted mode has no additional cost (threshold comparison is reversed)
-- Hard threshold mask
ooc.sim_add_mask_operator(ctx, input, mask, out, {
threshold = 0.5
})
-- Soft feathered mask (smooth transitions)
ooc.sim_add_mask_operator(ctx, input, mask, out, {
threshold = 0.5,
feather = 0.1
})
-- Wide feather for gradient blending
ooc.sim_add_mask_operator(ctx, input, mask, out, {
threshold = 0.5,
feather = 0.25
})
-- Inverted mask (keep where mask < threshold)
ooc.sim_add_mask_operator(ctx, input, mask, out, {
mode = "invert",
threshold = 0.25
})
-- Custom fill value for inactive regions
ooc.sim_add_mask_operator(ctx, input, mask, out, {
threshold = 0.5,
fill_value = -1.0
})
-- Complex field with complex fill value
ooc.sim_add_mask_operator(ctx, complex_field, mask, out, {
threshold = 0.3,
feather = 0.05,
fill_value = 0.0,
fill_value_im = 0.0
})
-- Circular aperture (using radial mask)
-- First create radial coordinate mask, then apply
ooc.sim_add_coordinate_operator(ctx, radial_mask, {
mode = "coord",
coord_mode = "radial",
coord_center_x = 128,
coord_center_y = 128,
normalize = "unit"
})
ooc.sim_add_mask_operator(ctx, input, radial_mask, apertured, {
mode = "invert", -- keep center, block edges
threshold = 0.3,
feather = 0.05
})

sim_add_mixer_operator(ctx, lhs, rhs, out, opts)

Combine two input fields using various mixing formulas including linear blending, modulation, nonlinear operations, and feedback integration. Essential for signal processing, synthesis, and field coupling.

sim_add_mixer_operator(ctx, lhs, rhs, output, [options]) -> operator

Returns: Operator handle (userdata)

Let a=lhsgaa = \text{lhs} \cdot g_a and b=rhsgbb = \text{rhs} \cdot g_b be the gain-scaled inputs.

Linear Modes:

  • linear: out=a+b+bias\text{out} = a + b + \text{bias}
  • sum: out=a+b\text{out} = a + b
  • difference: out=ab\text{out} = a - b
  • abs_diff: out=ab\text{out} = |a - b|
  • average: out=a+b2\text{out} = \frac{a + b}{2}
  • crossfade: out=(1m)a+mb\text{out} = (1 - m) \cdot a + m \cdot b where mm is mix

Multiplicative Modes:

  • multiply: out=ab\text{out} = a \cdot b
  • power: out=ab\text{out} = a^b (complex power for complex fields)

Extrema:

  • max: out=max(a,b)\text{out} = \max(a, b)
  • min: out=min(a,b)\text{out} = \min(a, b)

Modulation Modes:

  • am (amplitude modulation): out=(1+b)a+bias\text{out} = (1 + b) \cdot a + \text{bias}
  • fm (frequency modulation): out=aeib\text{out} = a \cdot e^{i \cdot b}
  • pm (phase modulation): out=aei(arg(a)+b)\text{out} = |a| \cdot e^{i(\arg(a) + b)}
  • ring_mod: out=ab\text{out} = a \cdot b (removes DC component)

Feedback Mode:

Leaky integrator with configurable splitting:

outn+1=(1ε)outn+εinput\text{out}_{n+1} = (1 - \varepsilon) \cdot \text{out}_n + \varepsilon \cdot \text{input}

Split modes affect the order of decay and injection:

  • none: Combined update
  • lie: Decay, then inject
  • strang: Half decay, inject, half decay (symmetric)

Core Parameters:

ParameterTypeDefaultRangeDescription
modeenum "linear"see belowMixing formula
lhs_gaindouble 1.0unboundedGain applied to left input
rhs_gaindouble 1.0unboundedGain applied to right input
biasdouble 0.0unboundedConstant offset added after mixing
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Mode options: linear, multiply, crossfade, sum, power, am, fm, pm, ring_mod, max, min, average, difference, abs_diff, feedback

Crossfade Parameters:

ParameterTypeDefaultRangeDescription
mixdouble 0.5[0, 1]Crossfade weight (0 = lhs, 1 = rhs)

Feedback Parameters:

ParameterTypeDefaultRangeDescription
feedback_epsilondouble 0.1[0, 1]Feedback/injection strength
feedback_epsilon_modeenum "input"see belowHow epsilon is applied
feedback_splitenum "none"see belowOperator splitting strategy

Epsilon mode options: input (scales injection), feedback (scales decay)

Split options: none, lie, strang

  • Operates elementwise; no boundary handling required
  • Both input fields must have compatible dimensions
  • Feedback mode maintains internal state; initialize carefully
  • Linear modes: Unconditionally stable
  • Multiply/power: Can produce large values; consider clamping
  • FM/PM: Unitary for real modulation; preserves magnitude
  • Feedback: Stable for ε[0,1]\varepsilon \in [0, 1]; larger values respond faster but may oscillate
  • Strang splitting: Second-order accurate; preferred for feedback with varying inputs
  • Linear modes are highly optimized
  • Modulation modes (fm, pm) require transcendental functions
  • Power mode with complex fields uses cpow (expensive)
  • Feedback requires internal state storage
-- Linear combination with gains
ooc.sim_add_mixer_operator(ctx, a, b, out, {
mode = "linear",
lhs_gain = 0.7,
rhs_gain = 0.3,
bias = 0.0
})
-- Simple sum
ooc.sim_add_mixer_operator(ctx, a, b, sum, {
mode = "sum"
})
-- Element-wise product
ooc.sim_add_mixer_operator(ctx, a, b, product, {
mode = "multiply"
})
-- Crossfade (35% toward rhs)
ooc.sim_add_mixer_operator(ctx, a, b, blended, {
mode = "crossfade",
lhs_gain = 1.0,
rhs_gain = 1.0,
mix = 0.35
})
-- FM synthesis
ooc.sim_add_mixer_operator(ctx, carrier, modulator, out, {
mode = "fm",
lhs_gain = 1.0,
rhs_gain = 0.2 -- modulation depth
})
-- Amplitude modulation
ooc.sim_add_mixer_operator(ctx, carrier, envelope, am_signal, {
mode = "am",
lhs_gain = 1.0,
rhs_gain = 0.8
})
-- Phase modulation
ooc.sim_add_mixer_operator(ctx, signal, phase_mod, pm_signal, {
mode = "pm",
rhs_gain = 0.5 -- radians
})
-- Ring modulation
ooc.sim_add_mixer_operator(ctx, a, b, ring, {
mode = "ring_mod"
})
-- Element-wise maximum
ooc.sim_add_mixer_operator(ctx, a, b, max_field, {
mode = "max"
})
-- Signed difference
ooc.sim_add_mixer_operator(ctx, a, b, diff, {
mode = "difference"
})
-- Absolute difference
ooc.sim_add_mixer_operator(ctx, a, b, abs_diff, {
mode = "abs_diff"
})
-- Power (a^b)
ooc.sim_add_mixer_operator(ctx, base, exponent, power, {
mode = "power"
})
-- Feedback with input injection mode
ooc.sim_add_mixer_operator(ctx, input, state, state, {
mode = "feedback",
feedback_epsilon = 0.2,
feedback_epsilon_mode = "input"
})
-- Feedback with Strang splitting (higher accuracy)
ooc.sim_add_mixer_operator(ctx, input, state, state, {
mode = "feedback",
feedback_epsilon = 0.1,
feedback_epsilon_mode = "feedback",
feedback_split = "strang"
})
-- Accumulate average into running sum
ooc.sim_add_mixer_operator(ctx, a, b, running_avg, {
mode = "average",
accumulate = true,
scale_by_dt = true
})