mirror of
https://github.com/typst/typst
synced 2025-05-19 03:25:27 +08:00
Handle compiler panics when handling infinite lengths (#2215)
This commit is contained in:
parent
ffcd951bc8
commit
553da642bd
@ -738,12 +738,12 @@ pub struct Paper {
|
|||||||
impl Paper {
|
impl Paper {
|
||||||
/// The width of the paper.
|
/// The width of the paper.
|
||||||
pub fn width(self) -> Abs {
|
pub fn width(self) -> Abs {
|
||||||
Abs::mm(self.width.0)
|
Abs::mm(self.width.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The height of the paper.
|
/// The height of the paper.
|
||||||
pub fn height(self) -> Abs {
|
pub fn height(self) -> Abs {
|
||||||
Abs::mm(self.height.0)
|
Abs::mm(self.height.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,8 +756,8 @@ macro_rules! papers {
|
|||||||
impl Paper {
|
impl Paper {
|
||||||
$(pub const $var: Self = Self {
|
$(pub const $var: Self = Self {
|
||||||
name: $name,
|
name: $name,
|
||||||
width: Scalar($width),
|
width: Scalar::new($width),
|
||||||
height: Scalar($height),
|
height: Scalar::new($height),
|
||||||
};)*
|
};)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,9 @@ pub(crate) fn field(value: &Value, field: &str) -> StrResult<Value> {
|
|||||||
"cap" => stroke.line_cap.into_value(),
|
"cap" => stroke.line_cap.into_value(),
|
||||||
"join" => stroke.line_join.into_value(),
|
"join" => stroke.line_join.into_value(),
|
||||||
"dash" => stroke.dash_pattern.clone().into_value(),
|
"dash" => stroke.dash_pattern.clone().into_value(),
|
||||||
"miter-limit" => stroke.miter_limit.map(|limit| limit.0).into_value(),
|
"miter-limit" => {
|
||||||
|
stroke.miter_limit.map(|limit| limit.get()).into_value()
|
||||||
|
}
|
||||||
_ => return missing(),
|
_ => return missing(),
|
||||||
}
|
}
|
||||||
} else if let Some(align) = dynamic.downcast::<Align>() {
|
} else if let Some(align) = dynamic.downcast::<Align>() {
|
||||||
|
@ -336,7 +336,7 @@ impl PageContext<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.state.stroke.as_ref().map(|s| &s.miter_limit) != Some(miter_limit) {
|
if self.state.stroke.as_ref().map(|s| &s.miter_limit) != Some(miter_limit) {
|
||||||
self.content.set_miter_limit(miter_limit.0 as f32);
|
self.content.set_miter_limit(miter_limit.get() as f32);
|
||||||
}
|
}
|
||||||
self.state.stroke = Some(stroke.clone());
|
self.state.stroke = Some(stroke.clone());
|
||||||
}
|
}
|
||||||
|
@ -496,7 +496,7 @@ fn render_shape(
|
|||||||
line_cap: line_cap.into(),
|
line_cap: line_cap.into(),
|
||||||
line_join: line_join.into(),
|
line_join: line_join.into(),
|
||||||
dash,
|
dash,
|
||||||
miter_limit: miter_limit.0 as f32,
|
miter_limit: miter_limit.get() as f32,
|
||||||
};
|
};
|
||||||
canvas.stroke_path(&path, &paint, &stroke, ts, mask);
|
canvas.stroke_path(&path, &paint, &stroke, ts, mask);
|
||||||
}
|
}
|
||||||
|
@ -323,7 +323,8 @@ impl SVGRenderer {
|
|||||||
LineJoin::Bevel => "bevel",
|
LineJoin::Bevel => "bevel",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.xml.write_attribute("stroke-miterlimit", &stroke.miter_limit.0);
|
self.xml
|
||||||
|
.write_attribute("stroke-miterlimit", &stroke.miter_limit.get());
|
||||||
if let Some(pattern) = &stroke.dash_pattern {
|
if let Some(pattern) = &stroke.dash_pattern {
|
||||||
self.xml.write_attribute("stroke-dashoffset", &pattern.phase.to_pt());
|
self.xml.write_attribute("stroke-dashoffset", &pattern.phase.to_pt());
|
||||||
self.xml.write_attribute(
|
self.xml.write_attribute(
|
||||||
|
@ -7,22 +7,22 @@ pub struct Abs(Scalar);
|
|||||||
impl Abs {
|
impl Abs {
|
||||||
/// The zero length.
|
/// The zero length.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self(Scalar(0.0))
|
Self(Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The infinite length.
|
/// The infinite length.
|
||||||
pub const fn inf() -> Self {
|
pub const fn inf() -> Self {
|
||||||
Self(Scalar(f64::INFINITY))
|
Self(Scalar::INFINITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an absolute length from a number of raw units.
|
/// Create an absolute length from a number of raw units.
|
||||||
pub const fn raw(raw: f64) -> Self {
|
pub const fn raw(raw: f64) -> Self {
|
||||||
Self(Scalar(raw))
|
Self(Scalar::new(raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an absolute length from a value in a unit.
|
/// Create an absolute length from a value in a unit.
|
||||||
pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
|
pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
|
||||||
Self(Scalar(val * unit.raw_scale()))
|
Self(Scalar::new(val * unit.raw_scale()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an absolute length from a number of points.
|
/// Create an absolute length from a number of points.
|
||||||
@ -47,7 +47,7 @@ impl Abs {
|
|||||||
|
|
||||||
/// Get the value of this absolute length in raw units.
|
/// Get the value of this absolute length in raw units.
|
||||||
pub const fn to_raw(self) -> f64 {
|
pub const fn to_raw(self) -> f64 {
|
||||||
(self.0).0
|
(self.0).get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of this absolute length in a unit.
|
/// Get the value of this absolute length in a unit.
|
||||||
|
@ -18,17 +18,17 @@ pub struct Angle(Scalar);
|
|||||||
impl Angle {
|
impl Angle {
|
||||||
/// The zero angle.
|
/// The zero angle.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self(Scalar(0.0))
|
Self(Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an angle from a number of raw units.
|
/// Create an angle from a number of raw units.
|
||||||
pub const fn raw(raw: f64) -> Self {
|
pub const fn raw(raw: f64) -> Self {
|
||||||
Self(Scalar(raw))
|
Self(Scalar::new(raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an angle from a value in a unit.
|
/// Create an angle from a value in a unit.
|
||||||
pub fn with_unit(val: f64, unit: AngleUnit) -> Self {
|
pub fn with_unit(val: f64, unit: AngleUnit) -> Self {
|
||||||
Self(Scalar(val * unit.raw_scale()))
|
Self(Scalar::new(val * unit.raw_scale()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an angle from a number of radians.
|
/// Create an angle from a number of radians.
|
||||||
@ -43,7 +43,7 @@ impl Angle {
|
|||||||
|
|
||||||
/// Get the value of this angle in raw units.
|
/// Get the value of this angle in raw units.
|
||||||
pub const fn to_raw(self) -> f64 {
|
pub const fn to_raw(self) -> f64 {
|
||||||
(self.0).0
|
(self.0).get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of this angle in a unit.
|
/// Get the value of this angle in a unit.
|
||||||
|
@ -9,29 +9,29 @@ pub struct Em(Scalar);
|
|||||||
impl Em {
|
impl Em {
|
||||||
/// The zero em length.
|
/// The zero em length.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self(Scalar(0.0))
|
Self(Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub const fn one() -> Self {
|
pub const fn one() -> Self {
|
||||||
Self(Scalar(1.0))
|
Self(Scalar::ONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a font-relative length.
|
/// Create a font-relative length.
|
||||||
pub const fn new(em: f64) -> Self {
|
pub const fn new(em: f64) -> Self {
|
||||||
Self(Scalar(em))
|
Self(Scalar::new(em))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an em length from font units at the given units per em.
|
/// Create an em length from font units at the given units per em.
|
||||||
pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
|
pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
|
||||||
Self(Scalar(units.into() / units_per_em))
|
Self(Scalar::new(units.into() / units_per_em))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an em length from a length at the given font size.
|
/// Create an em length from a length at the given font size.
|
||||||
pub fn from_length(length: Abs, font_size: Abs) -> Self {
|
pub fn from_length(length: Abs, font_size: Abs) -> Self {
|
||||||
let result = length / font_size;
|
let result = length / font_size;
|
||||||
if result.is_finite() {
|
if result.is_finite() {
|
||||||
Self(Scalar(result))
|
Self(Scalar::new(result))
|
||||||
} else {
|
} else {
|
||||||
Self::zero()
|
Self::zero()
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ impl Em {
|
|||||||
|
|
||||||
/// The number of em units.
|
/// The number of em units.
|
||||||
pub const fn get(self) -> f64 {
|
pub const fn get(self) -> f64 {
|
||||||
(self.0).0
|
(self.0).get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute value of this em length.
|
/// The absolute value of this em length.
|
||||||
|
@ -19,22 +19,22 @@ pub struct Fr(Scalar);
|
|||||||
impl Fr {
|
impl Fr {
|
||||||
/// Takes up zero space: `0fr`.
|
/// Takes up zero space: `0fr`.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self(Scalar(0.0))
|
Self(Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes up as much space as all other items with this fraction: `1fr`.
|
/// Takes up as much space as all other items with this fraction: `1fr`.
|
||||||
pub const fn one() -> Self {
|
pub const fn one() -> Self {
|
||||||
Self(Scalar(1.0))
|
Self(Scalar::ONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new fraction.
|
/// Create a new fraction.
|
||||||
pub const fn new(ratio: f64) -> Self {
|
pub const fn new(ratio: f64) -> Self {
|
||||||
Self(Scalar(ratio))
|
Self(Scalar::new(ratio))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the underlying number.
|
/// Get the underlying number.
|
||||||
pub const fn get(self) -> f64 {
|
pub const fn get(self) -> f64 {
|
||||||
(self.0).0
|
(self.0).get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute value of this fraction.
|
/// The absolute value of this fraction.
|
||||||
|
@ -18,22 +18,22 @@ pub struct Ratio(Scalar);
|
|||||||
impl Ratio {
|
impl Ratio {
|
||||||
/// A ratio of `0%` represented as `0.0`.
|
/// A ratio of `0%` represented as `0.0`.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self(Scalar(0.0))
|
Self(Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A ratio of `100%` represented as `1.0`.
|
/// A ratio of `100%` represented as `1.0`.
|
||||||
pub const fn one() -> Self {
|
pub const fn one() -> Self {
|
||||||
Self(Scalar(1.0))
|
Self(Scalar::ONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new ratio from a value, where `1.0` means `100%`.
|
/// Create a new ratio from a value, where `1.0` means `100%`.
|
||||||
pub const fn new(ratio: f64) -> Self {
|
pub const fn new(ratio: f64) -> Self {
|
||||||
Self(Scalar(ratio))
|
Self(Scalar::new(ratio))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the underlying ratio.
|
/// Get the underlying ratio.
|
||||||
pub const fn get(self) -> f64 {
|
pub const fn get(self) -> f64 {
|
||||||
(self.0).0
|
(self.0).get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the ratio is zero.
|
/// Whether the ratio is zero.
|
||||||
|
@ -4,7 +4,40 @@ use super::*;
|
|||||||
///
|
///
|
||||||
/// Panics if it's `NaN` during any of those operations.
|
/// Panics if it's `NaN` during any of those operations.
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct Scalar(pub f64);
|
pub struct Scalar(f64);
|
||||||
|
|
||||||
|
// We have to detect NaNs this way since `f64::is_nan` isn’t const
|
||||||
|
// on stable yet:
|
||||||
|
// ([tracking issue](https://github.com/rust-lang/rust/issues/57241))
|
||||||
|
#[allow(clippy::unusual_byte_groupings)]
|
||||||
|
const fn is_nan_const(x: f64) -> bool {
|
||||||
|
// Safety: all bit patterns are valid for u64, and f64 has no padding bits.
|
||||||
|
// We cannot use `f64::to_bits` because it is not const.
|
||||||
|
let x_bits = unsafe { std::mem::transmute::<f64, u64>(x) };
|
||||||
|
(x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scalar {
|
||||||
|
/// Creates a [`Scalar`] with the given value.
|
||||||
|
///
|
||||||
|
/// If the value is NaN, then it is set to `0.0` in the result.
|
||||||
|
pub const fn new(x: f64) -> Self {
|
||||||
|
Self(if is_nan_const(x) { 0.0 } else { x })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the value of this [`Scalar`].
|
||||||
|
#[inline]
|
||||||
|
pub const fn get(self) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scalar containing `0.0`.
|
||||||
|
pub const ZERO: Self = Self(0.0);
|
||||||
|
/// The scalar containing `1.0`.
|
||||||
|
pub const ONE: Self = Self(1.0);
|
||||||
|
/// The scalar containing `f64::INFINITY`.
|
||||||
|
pub const INFINITY: Self = Self(f64::INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
impl Numeric for Scalar {
|
impl Numeric for Scalar {
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
@ -18,7 +51,7 @@ impl Numeric for Scalar {
|
|||||||
|
|
||||||
impl From<f64> for Scalar {
|
impl From<f64> for Scalar {
|
||||||
fn from(float: f64) -> Self {
|
fn from(float: f64) -> Self {
|
||||||
Self(float)
|
Self::new(float)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +121,7 @@ impl Neg for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
Self(-self.0)
|
Self::new(-self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +129,13 @@ impl<T: Into<Self>> Add<T> for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(self, rhs: T) -> Self::Output {
|
fn add(self, rhs: T) -> Self::Output {
|
||||||
Self(self.0 + rhs.into().0)
|
Self::new(self.0 + rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Self>> AddAssign<T> for Scalar {
|
impl<T: Into<Self>> AddAssign<T> for Scalar {
|
||||||
fn add_assign(&mut self, rhs: T) {
|
fn add_assign(&mut self, rhs: T) {
|
||||||
self.0 += rhs.into().0;
|
*self = *self + rhs.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,13 +143,13 @@ impl<T: Into<Self>> Sub<T> for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn sub(self, rhs: T) -> Self::Output {
|
fn sub(self, rhs: T) -> Self::Output {
|
||||||
Self(self.0 - rhs.into().0)
|
Self::new(self.0 - rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Self>> SubAssign<T> for Scalar {
|
impl<T: Into<Self>> SubAssign<T> for Scalar {
|
||||||
fn sub_assign(&mut self, rhs: T) {
|
fn sub_assign(&mut self, rhs: T) {
|
||||||
self.0 -= rhs.into().0;
|
*self = *self - rhs.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,13 +157,13 @@ impl<T: Into<Self>> Mul<T> for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn mul(self, rhs: T) -> Self::Output {
|
fn mul(self, rhs: T) -> Self::Output {
|
||||||
Self(self.0 * rhs.into().0)
|
Self::new(self.0 * rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Self>> MulAssign<T> for Scalar {
|
impl<T: Into<Self>> MulAssign<T> for Scalar {
|
||||||
fn mul_assign(&mut self, rhs: T) {
|
fn mul_assign(&mut self, rhs: T) {
|
||||||
self.0 *= rhs.into().0;
|
*self = *self * rhs.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,13 +171,13 @@ impl<T: Into<Self>> Div<T> for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn div(self, rhs: T) -> Self::Output {
|
fn div(self, rhs: T) -> Self::Output {
|
||||||
Self(self.0 / rhs.into().0)
|
Self::new(self.0 / rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Self>> DivAssign<T> for Scalar {
|
impl<T: Into<Self>> DivAssign<T> for Scalar {
|
||||||
fn div_assign(&mut self, rhs: T) {
|
fn div_assign(&mut self, rhs: T) {
|
||||||
self.0 /= rhs.into().0;
|
*self = *self / rhs.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,24 +185,24 @@ impl<T: Into<Self>> Rem<T> for Scalar {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn rem(self, rhs: T) -> Self::Output {
|
fn rem(self, rhs: T) -> Self::Output {
|
||||||
Self(self.0 % rhs.into().0)
|
Self::new(self.0 % rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Self>> RemAssign<T> for Scalar {
|
impl<T: Into<Self>> RemAssign<T> for Scalar {
|
||||||
fn rem_assign(&mut self, rhs: T) {
|
fn rem_assign(&mut self, rhs: T) {
|
||||||
self.0 %= rhs.into().0;
|
*self = *self % rhs.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum for Scalar {
|
impl Sum for Scalar {
|
||||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
Self(iter.map(|s| s.0).sum())
|
Self::new(iter.map(|s| s.0).sum())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sum<&'a Self> for Scalar {
|
impl<'a> Sum<&'a Self> for Scalar {
|
||||||
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
Self(iter.map(|s| s.0).sum())
|
Self::new(iter.map(|s| s.0).sum())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ cast! {
|
|||||||
line_cap,
|
line_cap,
|
||||||
line_join,
|
line_join,
|
||||||
dash_pattern,
|
dash_pattern,
|
||||||
miter_limit: miter_limit.map(Scalar),
|
miter_limit: miter_limit.map(Scalar::new),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -460,7 +460,7 @@ impl Default for FixedStroke {
|
|||||||
line_cap: LineCap::Butt,
|
line_cap: LineCap::Butt,
|
||||||
line_join: LineJoin::Miter,
|
line_join: LineJoin::Miter,
|
||||||
dash_pattern: None,
|
dash_pattern: None,
|
||||||
miter_limit: Scalar(4.0),
|
miter_limit: Scalar::new(4.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,20 @@
|
|||||||
// Error: 2-8 invalid hexadecimal number: 0x123z
|
// Error: 2-8 invalid hexadecimal number: 0x123z
|
||||||
#0x123z
|
#0x123z
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test that multiplying infinite numbers by certain units does not crash.
|
||||||
|
#(float("inf") * 1pt)
|
||||||
|
#(float("inf") * 1em)
|
||||||
|
#(float("inf") * (1pt + 1em))
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test that trying to produce a NaN scalar (such as in lengths) does not crash.
|
||||||
|
#let infpt = float("inf") * 1pt
|
||||||
|
#test(infpt - infpt, 0pt)
|
||||||
|
#test(infpt + (-infpt), 0pt)
|
||||||
|
// TODO: this result is surprising
|
||||||
|
#test(infpt / float("inf"), 0pt)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test boolean operators.
|
// Test boolean operators.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user