ratio.rs (3470B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! `<ratio>` computed values. 6 7 use crate::values::animated::{Animate, Procedure}; 8 use crate::values::computed::NonNegativeNumber; 9 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; 10 use crate::values::generics::ratio::Ratio as GenericRatio; 11 use crate::values::generics::NonNegative; 12 use crate::Zero; 13 use std::cmp::Ordering; 14 15 /// A computed <ratio> value. 16 pub type Ratio = GenericRatio<NonNegativeNumber>; 17 18 impl PartialOrd for Ratio { 19 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 20 f64::partial_cmp( 21 &((self.0).0 as f64 * (other.1).0 as f64), 22 &((self.1).0 as f64 * (other.0).0 as f64), 23 ) 24 } 25 } 26 27 impl Ratio { 28 /// Returns the f32 value by dividing the first value by the second one. 29 #[inline] 30 fn to_f32(&self) -> f32 { 31 debug_assert!(!self.is_degenerate()); 32 (self.0).0 / (self.1).0 33 } 34 /// Returns a new Ratio. 35 #[inline] 36 pub fn new(a: f32, b: f32) -> Self { 37 GenericRatio(a.into(), b.into()) 38 } 39 } 40 41 /// https://drafts.csswg.org/css-values/#combine-ratio 42 impl Animate for Ratio { 43 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 44 // If either <ratio> is degenerate, the values cannot be interpolated. 45 if self.is_degenerate() || other.is_degenerate() { 46 return Err(()); 47 } 48 49 // Addition of <ratio>s is not possible, and based on 50 // https://drafts.csswg.org/css-values-4/#not-additive, 51 // we simply use the first value as the result value. 52 // Besides, the procedure for accumulation should be identical to addition here. 53 if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) { 54 return Ok(self.clone()); 55 } 56 57 // The interpolation of a <ratio> is defined by converting each <ratio> to a number by 58 // dividing the first value by the second (so a ratio of 3 / 2 would become 1.5), taking 59 // the logarithm of that result (so the 1.5 would become approximately 0.176), then 60 // interpolating those values. 61 // 62 // The result during the interpolation is converted back to a <ratio> by inverting the 63 // logarithm, then interpreting the result as a <ratio> with the result as the first value 64 // and 1 as the second value. 65 let start = self.to_f32().ln(); 66 let end = other.to_f32().ln(); 67 let e = std::f32::consts::E; 68 let result = e.powf(start.animate(&end, procedure)?); 69 // The range of the result is [0, inf), based on the easing function. 70 if result.is_zero() || result.is_infinite() { 71 return Err(()); 72 } 73 Ok(GenericRatio(NonNegative(result), NonNegative(1.0))) 74 } 75 } 76 77 impl ComputeSquaredDistance for Ratio { 78 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { 79 if self.is_degenerate() || other.is_degenerate() { 80 return Err(()); 81 } 82 // Use the distance of their logarithm values. (This is used by testing, so don't 83 // need to care about the base. Here we use the same base as that in animate().) 84 self.to_f32() 85 .ln() 86 .compute_squared_distance(&other.to_f32().ln()) 87 } 88 }