Skip to content

Commit 75bd003

Browse files
committed
Implement hypot()
1 parent 06c5f81 commit 75bd003

File tree

2 files changed

+101
-16
lines changed

2 files changed

+101
-16
lines changed

src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6076,6 +6076,28 @@ mod tests {
60766076
minify_test(".foo { rotate: atan2(1px, -1vw)", ".foo{rotate:atan2(1px,-1vw)}");
60776077
}
60786078

6079+
#[test]
6080+
fn test_exp() {
6081+
minify_test(".foo { width: hypot()", ".foo{width:hypot()}");
6082+
minify_test(".foo { width: hypot(1px)", ".foo{width:1px}");
6083+
minify_test(".foo { width: hypot(1px, 2px)", ".foo{width:2.23607px}");
6084+
minify_test(".foo { width: hypot(1px, 2px, 3px)", ".foo{width:3.74166px}");
6085+
minify_test(".foo { width: hypot(1px, 2vw)", ".foo{width:hypot(1px,2vw)}");
6086+
minify_test(".foo { width: hypot(1px, 2px, 3vw)", ".foo{width:hypot(1px,2px,3vw)}");
6087+
minify_test(".foo { width: calc(100px * hypot(3, 4))", ".foo{width:500px}");
6088+
minify_test(".foo { width: calc(1px * pow(2, sqrt(100))", ".foo{width:1024px}");
6089+
minify_test(".foo { width: calc(100px * pow(2, pow(2, 2)", ".foo{width:1600px}");
6090+
minify_test(".foo { width: calc(1px * log(1))", ".foo{width:0}");
6091+
minify_test(".foo { width: calc(1px * log(10, 10))", ".foo{width:1px}");
6092+
minify_test(".foo { width: calc(1px * exp(0))", ".foo{width:1px}");
6093+
minify_test(".foo { width: calc(1px * log(e))", ".foo{width:1px}");
6094+
minify_test(".foo { width: calc(1px * (e - exp(1)))", ".foo{width:0}");
6095+
minify_test(
6096+
".foo { width: calc(1px * (exp(log(1) + exp(0)*2))",
6097+
".foo{width:7.38906px}",
6098+
);
6099+
}
6100+
60796101
#[test]
60806102
fn test_sign() {
60816103
minify_test(".foo { width: abs(1px)", ".foo{width:1px}");

src/values/calc.rs

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::error::{ParserError, PrinterError};
55
use crate::macros::enum_property;
66
use crate::printer::Printer;
77
use crate::traits::private::AddInternal;
8-
use crate::traits::{Parse, Sign, ToCss, TryMap, TryOp, TrySign};
8+
use crate::traits::{Parse, Sign, ToCss, TryMap, TryOp, TrySign, Zero};
99
use cssparser::*;
1010

1111
use super::angle::Angle;
@@ -43,6 +43,8 @@ pub enum MathFunction<V> {
4343
Abs(Calc<V>),
4444
/// The [`sign()`](https://drafts.csswg.org/css-values-4/#funcdef-sign) function.
4545
Sign(Calc<V>),
46+
/// The [`hypot()`](https://drafts.csswg.org/css-values-4/#funcdef-hypot) function.
47+
Hypot(Vec<Calc<V>>),
4648
}
4749

4850
enum_property! {
@@ -176,6 +178,19 @@ impl<V: ToCss + std::ops::Mul<f32, Output = V> + TrySign + Clone + std::fmt::Deb
176178
v.to_css(dest)?;
177179
dest.write_char(')')
178180
}
181+
MathFunction::Hypot(args) => {
182+
dest.write_str("hypot(")?;
183+
let mut first = true;
184+
for arg in args {
185+
if first {
186+
first = false;
187+
} else {
188+
dest.delim(',', false)?;
189+
}
190+
arg.to_css(dest)?;
191+
}
192+
dest.write_char(')')
193+
}
179194
}
180195
}
181196
}
@@ -394,20 +409,24 @@ impl<
394409
},
395410
"sqrt" => Self::parse_numeric_fn(input, f32::sqrt),
396411
"exp" => Self::parse_numeric_fn(input, f32::exp),
412+
"hypot" => {
413+
input.parse_nested_block(|input| {
414+
let args: Vec<Self> = input.parse_comma_separated(Self::parse_sum)?;
415+
Self::parse_hypot(&args)?
416+
.map_or_else(
417+
|| Ok(Calc::Function(Box::new(MathFunction::Hypot(args)))),
418+
|v| Ok(v)
419+
)
420+
})
421+
},
397422
"abs" => {
398423
input.parse_nested_block(|input| {
399424
let v: Calc<V> = Self::parse_sum(input)?;
400-
match &v {
401-
Calc::Number(n) => return Ok(Calc::Number(n.abs())),
402-
Calc::Value(v) => {
403-
if let Some(v) = v.try_map(f32::abs) {
404-
return Ok(Calc::Value(Box::new(v)));
405-
}
406-
}
407-
_ => {}
408-
}
409-
410-
Ok(Calc::Function(Box::new(MathFunction::Abs(v))))
425+
Self::apply_map(&v, f32::abs)
426+
.map_or_else(
427+
|| Ok(Calc::Function(Box::new(MathFunction::Abs(v)))),
428+
|v| Ok(v)
429+
)
411430
})
412431
},
413432
"sign" => {
@@ -599,17 +618,35 @@ impl<
599618
input.expect_comma()?;
600619
let b: Calc<V> = Calc::parse_sum(input)?;
601620

602-
match (&a, &b) {
621+
Ok(Self::apply_op(&a, &b, op).unwrap_or_else(|| Calc::Function(Box::new(fallback(a, b)))))
622+
}
623+
624+
fn apply_op<'t, O: FnOnce(f32, f32) -> f32>(a: &Calc<V>, b: &Calc<V>, op: O) -> Option<Self> {
625+
match (a, b) {
603626
(Calc::Value(a), Calc::Value(b)) => {
604627
if let Some(v) = a.try_op(&**b, op) {
605-
return Ok(Calc::Value(Box::new(v)));
628+
return Some(Calc::Value(Box::new(v)));
629+
}
630+
}
631+
(Calc::Number(a), Calc::Number(b)) => return Some(Calc::Number(op(*a, *b))),
632+
_ => {}
633+
}
634+
635+
None
636+
}
637+
638+
fn apply_map<'t, O: FnOnce(f32) -> f32>(v: &Calc<V>, op: O) -> Option<Self> {
639+
match v {
640+
Calc::Number(n) => return Some(Calc::Number(op(*n))),
641+
Calc::Value(v) => {
642+
if let Some(v) = v.try_map(op) {
643+
return Some(Calc::Value(Box::new(v)));
606644
}
607645
}
608-
(Calc::Number(a), Calc::Number(b)) => return Ok(Calc::Number(op(*a, *b))),
609646
_ => {}
610647
}
611648

612-
Ok(Calc::Function(Box::new(fallback(a, b))))
649+
None
613650
}
614651

615652
fn parse_trig<'t, F: FnOnce(f32) -> f32>(
@@ -698,6 +735,32 @@ impl<
698735
// This will fall back to an unparsed property, leaving the atan2() function intact.
699736
Err(input.new_custom_error(ParserError::InvalidValue))
700737
}
738+
739+
fn parse_hypot<'t>(args: &Vec<Self>) -> Result<Option<Self>, ParseError<'i, ParserError<'i>>> {
740+
if args.len() == 1 {
741+
return Ok(Some(args[0].clone()));
742+
}
743+
744+
if args.len() == 2 {
745+
return Ok(Self::apply_op(&args[0], &args[1], f32::hypot));
746+
}
747+
748+
let mut iter = args.iter();
749+
let first = match Self::apply_map(&iter.next().unwrap(), |v| v.powi(2)) {
750+
Some(v) => v,
751+
None => return Ok(None),
752+
};
753+
let sum = iter.try_fold(first, |acc, arg| {
754+
Self::apply_op(&acc, &arg, |a, b| a + b.powi(2)).map_or_else(|| Err(()), |v| Ok(v))
755+
});
756+
757+
let sum = match sum {
758+
Ok(s) => s,
759+
Err(_) => return Ok(None),
760+
};
761+
762+
Ok(Self::apply_map(&sum, f32::sqrt))
763+
}
701764
}
702765

703766
impl<V: std::ops::Mul<f32, Output = V>> std::ops::Mul<f32> for Calc<V> {

0 commit comments

Comments
 (0)