///|
/// A color stop whose curve controls the transition to the next stop.
pub struct GradientStop {
position : Double
color : Rgba
curve : Curve
} derive(Debug)
///|
pub fn gradient_stop(
position : Double,
color : Rgba,
curve? : Curve = Curve::builtin(Linear),
) -> GradientStop raise MotionError {
ensure_finite(position)
if position < 0.0 || position > 1.0 {
raise MotionError::InvalidTime(position)
}
{ position, color, curve }
}
///|
pub fn GradientStop::position(self : GradientStop) -> Double {
self.position
}
///|
pub fn GradientStop::color(self : GradientStop) -> Rgba {
self.color
}
///|
pub fn GradientStop::curve(self : GradientStop) -> Curve {
self.curve
}
///|
/// A validated one-dimensional color gradient for UI and media rendering.
pub struct ColorGradient {
stops : Array[GradientStop]
premultiplied : Bool
} derive(Debug)
///|
pub fn ColorGradient::new(
stops : Array[GradientStop],
premultiplied? : Bool = false,
) -> ColorGradient raise MotionError {
if stops.length() == 0 {
raise MotionError::EmptyTrack
}
for i in 1.. Array[GradientStop] {
self.stops.copy()
}
///|
pub fn ColorGradient::length(self : ColorGradient) -> Int {
self.stops.length()
}
///|
pub fn ColorGradient::premultiplied(self : ColorGradient) -> Bool {
self.premultiplied
}
///|
pub fn ColorGradient::start(self : ColorGradient) -> Double {
self.stops[0].position
}
///|
pub fn ColorGradient::end(self : ColorGradient) -> Double {
self.stops[self.stops.length() - 1].position
}
///|
fn ColorGradient::segment(self : ColorGradient, position : Double) -> Int {
let last = self.stops.length() - 1
if position <= self.stops[0].position {
return 0
}
if position >= self.stops[last].position {
return last - 1
}
for index in 0.. Rgba {
let first = self.stops[0]
let last = self.stops[self.stops.length() - 1]
if self.stops.length() == 1 || position <= first.position {
return first.color
}
if position >= last.position {
return last.color
}
let index = self.segment(position)
let left = self.stops[index]
let right = self.stops[index + 1]
let ratio = (position - left.position) / (right.position - left.position)
interpolate_rgba(
left.color,
right.color,
left.curve.apply(ratio),
premultiplied=self.premultiplied,
)
}
///|
pub fn ColorGradient::sample_many(
self : ColorGradient,
count : Int,
) -> Array[Rgba] raise MotionError {
if count < 2 {
raise MotionError::InvalidSampleCount(count)
}
let values : Array[Rgba] = []
for index in 0.. ColorGradient {
let reversed : Array[GradientStop] = []
for index in 0.. Curve::builtin(reverse_easing_id(id))
CubicBezier(bezier) => Curve::bezier(bezier.reversed())
}
reversed.push({ position, color: source.color, curve })
}
{ stops: reversed, premultiplied: self.premultiplied }
}
///|
fn reverse_easing_id(id : EasingId) -> EasingId {
match id {
Linear => Linear
QuadIn => QuadOut
QuadOut => QuadIn
QuadInOut => QuadInOut
CubicIn => CubicOut
CubicOut => CubicIn
CubicInOut => CubicInOut
QuartIn => QuartOut
QuartOut => QuartIn
QuartInOut => QuartInOut
QuintIn => QuintOut
QuintOut => QuintIn
QuintInOut => QuintInOut
SineIn => SineOut
SineOut => SineIn
SineInOut => SineInOut
ExpoIn => ExpoOut
ExpoOut => ExpoIn
ExpoInOut => ExpoInOut
CircIn => CircOut
CircOut => CircIn
CircInOut => CircInOut
BackIn => BackOut
BackOut => BackIn
BackInOut => BackInOut
ElasticIn => ElasticOut
ElasticOut => ElasticIn
ElasticInOut => ElasticInOut
BounceIn => BounceOut
BounceOut => BounceIn
BounceInOut => BounceInOut
}
}
///|
/// A builder for gradients assembled by an editor or an importer.
pub struct GradientBuilder {
stops : Array[GradientStop]
premultiplied : Bool
}
///|
pub fn GradientBuilder::new(premultiplied? : Bool = false) -> GradientBuilder {
{ stops: [], premultiplied }
}
///|
pub fn GradientBuilder::add(
self : GradientBuilder,
stop : GradientStop,
) -> Unit {
self.stops.push(stop)
self.stops.sort_by(fn(left, right) { left.position.compare(right.position) })
}
///|
pub fn GradientBuilder::remove_at(
self : GradientBuilder,
index : Int,
) -> GradientStop? {
if index < 0 || index >= self.stops.length() {
None
} else {
Some(self.stops.remove(index))
}
}
///|
pub fn GradientBuilder::build(
self : GradientBuilder,
) -> ColorGradient raise MotionError {
ColorGradient::new(self.stops, premultiplied=self.premultiplied)
}