implement builtin function math.atan2
This commit is contained in:
parent
28701b2707
commit
a9e4d5cba5
@ -464,7 +464,125 @@ fn atan(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
|
||||
fn atan2(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
todo!()
|
||||
let (y_num, y_unit) = match args.get_err(0, "y")? {
|
||||
Value::Dimension(n, u, ..) => (n, u),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$y: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
let (x_num, x_unit) = match args.get_err(1, "x")? {
|
||||
Value::Dimension(n, u, ..) => (n, u),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$x: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
let (x_num, y_num) = if x_unit == Unit::None && y_unit == Unit::None {
|
||||
let x = match x_num {
|
||||
Some(n) => n,
|
||||
None => return Ok(Value::Dimension(None, Unit::Deg, true)),
|
||||
};
|
||||
|
||||
let y = match y_num {
|
||||
Some(n) => n,
|
||||
None => return Ok(Value::Dimension(None, Unit::Deg, true)),
|
||||
};
|
||||
|
||||
(x, y)
|
||||
} else if y_unit == Unit::None {
|
||||
return Err((
|
||||
format!(
|
||||
"$y is unitless but $x has unit {}. \
|
||||
Arguments must all have units or all be unitless.",
|
||||
x_unit
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into());
|
||||
} else if x_unit == Unit::None {
|
||||
return Err((
|
||||
format!(
|
||||
"$y has unit {} but $x is unitless. \
|
||||
Arguments must all have units or all be unitless.",
|
||||
y_unit
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into());
|
||||
} else if x_unit.comparable(&y_unit) {
|
||||
let x = match x_num {
|
||||
Some(n) => n,
|
||||
None => return Ok(Value::Dimension(None, Unit::Deg, true)),
|
||||
};
|
||||
|
||||
let y = match y_num {
|
||||
Some(n) => n,
|
||||
None => return Ok(Value::Dimension(None, Unit::Deg, true)),
|
||||
};
|
||||
|
||||
(x, y.convert(&y_unit, &x_unit))
|
||||
} else {
|
||||
return Err((
|
||||
format!("Incompatible units {} and {}.", y_unit, x_unit),
|
||||
args.span(),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
|
||||
Ok(
|
||||
match (
|
||||
NumberState::from_number(&x_num),
|
||||
NumberState::from_number(&y_num),
|
||||
) {
|
||||
(NumberState::Zero, NumberState::FiniteNegative) => {
|
||||
Value::Dimension(Some(Number::from(-90)), Unit::Deg, true)
|
||||
}
|
||||
(NumberState::Zero, NumberState::Zero) | (NumberState::Finite, NumberState::Zero) => {
|
||||
Value::Dimension(Some(Number::zero()), Unit::Deg, true)
|
||||
}
|
||||
(NumberState::Zero, NumberState::Finite) => {
|
||||
Value::Dimension(Some(Number::from(90)), Unit::Deg, true)
|
||||
}
|
||||
(NumberState::Finite, NumberState::Finite)
|
||||
| (NumberState::FiniteNegative, NumberState::Finite)
|
||||
| (NumberState::Finite, NumberState::FiniteNegative)
|
||||
| (NumberState::FiniteNegative, NumberState::FiniteNegative) => Value::Dimension(
|
||||
y_num
|
||||
.atan2(x_num)
|
||||
.map(|n| (n * Number::from(180)) / Number::pi()),
|
||||
Unit::Deg,
|
||||
true,
|
||||
),
|
||||
(NumberState::FiniteNegative, NumberState::Zero) => {
|
||||
Value::Dimension(Some(Number::from(180)), Unit::Deg, true)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
enum NumberState {
|
||||
Zero,
|
||||
Finite,
|
||||
FiniteNegative,
|
||||
}
|
||||
|
||||
impl NumberState {
|
||||
fn from_number(num: &Number) -> Self {
|
||||
match (num.is_zero(), num.is_positive()) {
|
||||
(true, _) => NumberState::Zero,
|
||||
(false, true) => NumberState::Finite,
|
||||
(false, false) => NumberState::FiniteNegative,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn declare(f: &mut Module) {
|
||||
@ -489,6 +607,7 @@ pub(crate) fn declare(f: &mut Module) {
|
||||
f.insert_builtin("log", log);
|
||||
f.insert_builtin("pow", pow);
|
||||
f.insert_builtin("hypot", hypot);
|
||||
f.insert_builtin("atan2", atan2);
|
||||
#[cfg(feature = "random")]
|
||||
f.insert_builtin("random", random);
|
||||
|
||||
|
@ -137,6 +137,16 @@ impl Number {
|
||||
)?)))
|
||||
}
|
||||
|
||||
pub fn pi() -> Self {
|
||||
Number::from(std::f64::consts::PI)
|
||||
}
|
||||
|
||||
pub fn atan2(self, other: Self) -> Option<Self> {
|
||||
Some(Number::Big(Box::new(BigRational::from_float(
|
||||
self.as_float()?.atan2(other.as_float()?),
|
||||
)?)))
|
||||
}
|
||||
|
||||
/// Invariants: `from.comparable(&to)` must be true
|
||||
pub fn convert(self, from: &Unit, to: &Unit) -> Self {
|
||||
self * UNIT_CONVERSION_TABLE[to][from].clone()
|
||||
|
@ -495,3 +495,98 @@ error!(
|
||||
"@use 'sass:math';\na {\n color: math.hypot(1deg, 2deg, (0 / 0));\n}\n",
|
||||
"Error: Argument 1 has unit deg but argument 3 is unitless. Arguments must all have units or all be unitless."
|
||||
);
|
||||
test!(
|
||||
atan2_both_positive,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3, 4);\n}\n",
|
||||
"a {\n color: 36.8698976458deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_first_negative,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(-3, 4);\n}\n",
|
||||
"a {\n color: -36.8698976458deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_second_negative,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3, -4);\n}\n",
|
||||
"a {\n color: 143.1301023542deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_both_negative,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(-3, -4);\n}\n",
|
||||
"a {\n color: -143.1301023542deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_first_positive_second_zero,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3, 0);\n}\n",
|
||||
"a {\n color: 90deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_first_negative_second_zero,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(-3, 0);\n}\n",
|
||||
"a {\n color: -90deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_first_zero_second_positive,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(0, 4);\n}\n",
|
||||
"a {\n color: 0deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_first_zero_second_negative,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(0, -4);\n}\n",
|
||||
"a {\n color: 180deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_both_zero,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(0, 0);\n}\n",
|
||||
"a {\n color: 0deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_both_same_unit,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3px, 4px);\n}\n",
|
||||
"a {\n color: 36.8698976458deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_both_different_but_comparable_unit,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3px, 4in);\n}\n",
|
||||
"a {\n color: 0.4476141709deg;\n}\n"
|
||||
);
|
||||
error!(
|
||||
atan2_first_unitless_second_unit,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3, 4rem);\n}\n",
|
||||
"Error: $y is unitless but $x has unit rem. Arguments must all have units or all be unitless."
|
||||
);
|
||||
error!(
|
||||
atan2_first_unit_second_unitless,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3px, 4);\n}\n",
|
||||
"Error: $y has unit px but $x is unitless. Arguments must all have units or all be unitless."
|
||||
);
|
||||
error!(
|
||||
atan2_incompatible_units,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(3px, 4rem);\n}\n",
|
||||
"Error: Incompatible units px and rem."
|
||||
);
|
||||
error!(
|
||||
atan2_nan_incompatible_units,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(math.acos(2), 3);\n}\n",
|
||||
"Error: $y has unit deg but $x is unitless. Arguments must all have units or all be unitless."
|
||||
);
|
||||
test!(
|
||||
atan2_first_nan,
|
||||
"@use 'sass:math';\na {\n color: math.atan2((0/0), 0);\n}\n",
|
||||
"a {\n color: NaNdeg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_second_nan,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(0, (0/0));\n}\n",
|
||||
"a {\n color: NaNdeg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_both_nan,
|
||||
"@use 'sass:math';\na {\n color: math.atan2((0/0), (0/0));\n}\n",
|
||||
"a {\n color: NaNdeg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
atan2_nan_with_same_units,
|
||||
"@use 'sass:math';\na {\n color: math.atan2(math.acos(2), 3deg);\n}\n",
|
||||
"a {\n color: NaNdeg;\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user