/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! `` 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::Zero; use std::cmp::Ordering; /// A computed value. pub type Ratio = GenericRatio; impl PartialOrd for Ratio { fn partial_cmp(&self, other: &Self) -> Option { f64::partial_cmp( &((self.0).0 as f64 * (other.1).0 as f64), &((self.1).0 as f64 * (other.0).0 as f64), ) } } impl GenericRatio { /// 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 / self.1 } } /// https://drafts.csswg.org/css-values/#combine-ratio impl Animate for GenericRatio { fn animate(&self, other: &Self, procedure: Procedure) -> Result { // If either is degenerate, the values cannot be interpolated. if self.is_degenerate() || other.is_degenerate() { return Err(()); } // Addition of 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 is defined by converting each 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 by inverting the // logarithm, then interpreting the result as a 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(GenericRatio(result, 1.0)) } } impl ComputeSquaredDistance for GenericRatio { fn compute_squared_distance(&self, other: &Self) -> Result { 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()) } }