///|
/// Layer policy for combining animation sources at a common timestamp.
pub enum LayerBlendMode {
OverrideLayer
AdditiveLayer
MultiplyLayer
DifferenceLayer
} derive(Debug)
///|
pub fn layer_blend_modes() -> Array[LayerBlendMode] {
[OverrideLayer, AdditiveLayer, MultiplyLayer, DifferenceLayer]
}
///|
pub struct ScalarBlendLayer {
value : Double
weight : Double
mode : LayerBlendMode
priority : Int
} derive(Debug)
///|
pub fn scalar_blend_layer(
value : Double,
weight : Double,
mode? : LayerBlendMode = OverrideLayer,
priority? : Int = 0,
) -> ScalarBlendLayer {
{ value, weight: weight.max(0.0), mode, priority }
}
///|
pub fn ScalarBlendLayer::value(self : ScalarBlendLayer) -> Double {
self.value
}
///|
pub fn ScalarBlendLayer::weight(self : ScalarBlendLayer) -> Double {
self.weight
}
///|
pub fn ScalarBlendLayer::mode(self : ScalarBlendLayer) -> LayerBlendMode {
self.mode
}
///|
pub fn ScalarBlendLayer::priority(self : ScalarBlendLayer) -> Int {
self.priority
}
///|
pub fn normalize_weights(weights : Array[Double]) -> Array[Double] {
let total = weights.fold(init=0.0, fn(sum, value) { sum + value.max(0.0) })
let values : Array[Double] = []
if total <= 0.0 {
for _ in weights {
values.push(0.0)
}
} else {
for weight in weights {
values.push(weight.max(0.0) / total)
}
}
values
}
///|
fn blend_scalar_value(
mode : LayerBlendMode,
base : Double,
layer : Double,
) -> Double {
match mode {
OverrideLayer => layer
AdditiveLayer => base + layer
MultiplyLayer => base * layer
DifferenceLayer => base - layer
}
}
///|
pub fn blend_scalar_layers(
layers : Array[ScalarBlendLayer],
fallback : Double,
) -> Double {
if layers.length() == 0 {
return fallback
}
let sorted = layers.copy()
sorted.sort_by(fn(left, right) { right.priority.compare(left.priority) })
let weights : Array[Double] = []
for layer in sorted {
weights.push(layer.weight)
}
let normalized = normalize_weights(weights)
let mut result = fallback
for index in 0.. Point2BlendLayer {
{ value, weight: weight.max(0.0), mode, priority }
}
///|
pub fn Point2BlendLayer::value(self : Point2BlendLayer) -> Point2 {
self.value
}
///|
pub fn Point2BlendLayer::weight(self : Point2BlendLayer) -> Double {
self.weight
}
///|
pub fn Point2BlendLayer::mode(self : Point2BlendLayer) -> LayerBlendMode {
self.mode
}
///|
pub fn Point2BlendLayer::priority(self : Point2BlendLayer) -> Int {
self.priority
}
///|
pub fn blend_point2_layers(
layers : Array[Point2BlendLayer],
fallback : Point2,
) -> Point2 {
if layers.length() == 0 {
return fallback
}
let sorted = layers.copy()
sorted.sort_by(fn(left, right) { right.priority.compare(left.priority) })
let weights : Array[Double] = []
for layer in sorted {
weights.push(layer.weight)
}
let normalized = normalize_weights(weights)
let mut result = fallback
for index in 0.. layer.value
AdditiveLayer =>
point2(x=result.x() + layer.value.x(), y=result.y() + layer.value.y())
MultiplyLayer =>
point2(x=result.x() * layer.value.x(), y=result.y() * layer.value.y())
DifferenceLayer =>
point2(x=result.x() - layer.value.x(), y=result.y() - layer.value.y())
}
result = result.lerp(value, normalized[index])
}
result
}
///|
pub struct Point3BlendLayer {
value : Point3
weight : Double
mode : LayerBlendMode
priority : Int
} derive(Debug)
///|
pub fn point3_blend_layer(
value : Point3,
weight : Double,
mode? : LayerBlendMode = OverrideLayer,
priority? : Int = 0,
) -> Point3BlendLayer {
{ value, weight: weight.max(0.0), mode, priority }
}
///|
pub fn Point3BlendLayer::value(self : Point3BlendLayer) -> Point3 {
self.value
}
///|
pub fn Point3BlendLayer::weight(self : Point3BlendLayer) -> Double {
self.weight
}
///|
pub fn Point3BlendLayer::mode(self : Point3BlendLayer) -> LayerBlendMode {
self.mode
}
///|
pub fn Point3BlendLayer::priority(self : Point3BlendLayer) -> Int {
self.priority
}
///|
pub fn blend_point3_layers(
layers : Array[Point3BlendLayer],
fallback : Point3,
) -> Point3 {
if layers.length() == 0 {
return fallback
}
let sorted = layers.copy()
sorted.sort_by(fn(left, right) { right.priority.compare(left.priority) })
let weights : Array[Double] = []
for layer in sorted {
weights.push(layer.weight)
}
let normalized = normalize_weights(weights)
let mut result = fallback
for index in 0.. layer.value
AdditiveLayer =>
point3(
x=result.x() + layer.value.x(),
y=result.y() + layer.value.y(),
z=result.z() + layer.value.z(),
)
MultiplyLayer =>
point3(
x=result.x() * layer.value.x(),
y=result.y() * layer.value.y(),
z=result.z() * layer.value.z(),
)
DifferenceLayer =>
point3(
x=result.x() - layer.value.x(),
y=result.y() - layer.value.y(),
z=result.z() - layer.value.z(),
)
}
result = result.lerp(value, normalized[index])
}
result
}
///|
pub struct ColorBlendLayer {
value : Rgba
weight : Double
mode : BlendMode
priority : Int
} derive(Debug)
///|
pub fn color_blend_layer(
value : Rgba,
weight : Double,
mode? : BlendMode = Normal,
priority? : Int = 0,
) -> ColorBlendLayer {
{ value, weight: weight.max(0.0), mode, priority }
}
///|
pub fn ColorBlendLayer::value(self : ColorBlendLayer) -> Rgba {
self.value
}
///|
pub fn ColorBlendLayer::weight(self : ColorBlendLayer) -> Double {
self.weight
}
///|
pub fn ColorBlendLayer::mode(self : ColorBlendLayer) -> BlendMode {
self.mode
}
///|
pub fn blend_color_layers(
layers : Array[ColorBlendLayer],
fallback : Rgba,
) -> Rgba {
if layers.length() == 0 {
return fallback
}
let sorted = layers.copy()
sorted.sort_by(fn(left, right) { right.priority.compare(left.priority) })
let weights : Array[Double] = []
for layer in sorted {
weights.push(layer.weight)
}
let normalized = normalize_weights(weights)
let mut result = fallback
for index in 0.. Transform3BlendLayer {
{ value, weight: weight.max(0.0), priority }
}
///|
pub fn Transform3BlendLayer::value(self : Transform3BlendLayer) -> Transform3D {
self.value
}
///|
pub fn Transform3BlendLayer::weight(self : Transform3BlendLayer) -> Double {
self.weight
}
///|
pub fn Transform3BlendLayer::priority(self : Transform3BlendLayer) -> Int {
self.priority
}
///|
pub fn blend_transform3_layers(
layers : Array[Transform3BlendLayer],
fallback : Transform3D,
) -> Transform3D {
if layers.length() == 0 {
return fallback
}
let sorted = layers.copy()
sorted.sort_by(fn(left, right) { right.priority.compare(left.priority) })
let weights : Array[Double] = []
for layer in sorted {
weights.push(layer.weight)
}
let normalized = normalize_weights(weights)
let mut result = fallback
for index in 0.. BlendMask2D {
{ values: values.copy(), default_weight: clamp01(default_weight) }
}
///|
pub fn BlendMask2D::length(self : BlendMask2D) -> Int {
self.values.length()
}
///|
pub fn BlendMask2D::sample(self : BlendMask2D, index : Int) -> Double {
if index < 0 || index >= self.values.length() {
self.default_weight
} else {
clamp01(self.values[index])
}
}
///|
pub fn BlendMask2D::multiply(
self : BlendMask2D,
other : BlendMask2D,
) -> BlendMask2D {
let count = self.values.length().max(other.values.length())
let values : Array[Double] = []
for index in 0.. BlendMask2D {
if radius <= 0 || self.values.length() == 0 {
return self
}
let values : Array[Double] = []
for index in 0..= 0 && cursor < self.values.length() {
sum = sum + self.values[cursor]
count = count + 1
}
}
values.push(sum / count.to_double())
}
blend_mask2d(values, default_weight=self.default_weight)
}