style: Implement Animate trait and ComputeSquaredDistance trait for <ratio>.

I also update the wpt becasue it seems the original one lets <ratio>
support the addition. However, the spec says "Addition of <ratio>s is not
possible".

Differential Revision: https://phabricator.services.mozilla.com/D106219
This commit is contained in:
Boris Chiou 2021-02-25 01:50:55 +00:00 committed by Emilio Cobos Álvarez
parent 52d39fc1bc
commit 0ef2410ea0
4 changed files with 83 additions and 7 deletions

View file

@ -4,7 +4,9 @@
//! `<ratio>` computed values.
use crate::values::animated::{Animate, Procedure};
use crate::values::computed::NonNegativeNumber;
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::ratio::Ratio as GenericRatio;
use crate::{One, Zero};
use std::cmp::{Ordering, PartialOrd};
@ -21,8 +23,58 @@ impl PartialOrd for Ratio {
}
}
/// https://drafts.csswg.org/css-values/#combine-ratio
impl Animate for Ratio {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
// If either <ratio> is degenerate, the values cannot be interpolated.
if self.is_degenerate() || other.is_degenerate() {
return Err(());
}
// Addition of <ratio>s is not possible, and based on
// https://drafts.csswg.org/css-values-4/#not-additive,
// we simply use the first value as the result value.
// Besides, the procedure for accumulation should be identical to addition here.
if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
return Ok(self.clone());
}
// The interpolation of a <ratio> is defined by converting each <ratio> to a number by
// dividing the first value by the second (so a ratio of 3 / 2 would become 1.5), taking
// the logarithm of that result (so the 1.5 would become approximately 0.176), then
// interpolating those values.
//
// The result during the interpolation is converted back to a <ratio> by inverting the
// logarithm, then interpreting the result as a <ratio> with the result as the first value
// and 1 as the second value.
let start = self.to_f32().ln();
let end = other.to_f32().ln();
let e = std::f32::consts::E;
let result = e.powf(start.animate(&end, procedure)?);
// The range of the result is [0, inf), based on the easing function.
if result.is_zero() || result.is_infinite() {
return Err(());
}
Ok(Ratio::new(result, 1.0f32))
}
}
impl ComputeSquaredDistance for Ratio {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.is_degenerate() || other.is_degenerate() {
return Err(());
}
// Use the distance of their logarithm values. (This is used by testing, so don't need to
// care about the base. Here we use the same base as that in animate().)
self.to_f32()
.ln()
.compute_squared_distance(&other.to_f32().ln())
}
}
impl Ratio {
/// Returns a new Ratio.
#[inline]
pub fn new(a: f32, b: f32) -> Self {
GenericRatio(a.into(), b.into())
}
@ -36,4 +88,18 @@ impl Ratio {
self
}
}
/// Returns true if this is a degenerate ratio.
/// https://drafts.csswg.org/css-values/#degenerate-ratio
#[inline]
pub fn is_degenerate(&self) -> bool {
self.0.is_zero() || self.1.is_zero()
}
/// Returns the f32 value by dividing the first value by the second one.
#[inline]
fn to_f32(&self) -> f32 {
debug_assert!(!self.is_degenerate());
(self.0).0 / (self.1).0
}
}