From 4653ffebb43d733a3cff873d0903c7d00aaeb499 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 23 Jan 2023 15:03:10 +0100 Subject: Math module --- library/src/lib.rs | 230 ++++++++++++++++++++++---------------------- library/src/math/accent.rs | 2 +- library/src/math/frac.rs | 5 +- library/src/math/lr.rs | 91 +++++++++++++++++- library/src/math/matrix.rs | 14 ++- library/src/math/mod.rs | 77 +++++++-------- library/src/math/op.rs | 131 +++++++------------------ library/src/math/root.rs | 2 +- library/src/math/spacing.rs | 18 +++- 9 files changed, 306 insertions(+), 264 deletions(-) (limited to 'library/src') diff --git a/library/src/lib.rs b/library/src/lib.rs index c2f51ee4..c4b6710f 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -11,144 +11,146 @@ pub mod text; pub mod visualize; use typst::geom::{Align, Color, Dir, GenAlign}; -use typst::model::{LangItems, Library, Node, NodeId, Scope, StyleMap}; +use typst::model::{LangItems, Library, Module, Node, NodeId, Scope, StyleMap}; use self::layout::LayoutRoot; /// Construct the standard library. pub fn build() -> Library { - Library { scope: scope(), styles: styles(), items: items() } + let math = math::module(); + let global = global(math.clone()); + Library { global, math, styles: styles(), items: items() } } -/// Construct the standard scope. -fn scope() -> Scope { - let mut std = Scope::new(); +/// Construct the module with global definitions. +fn global(math: Module) -> Module { + let mut global = Scope::deduplicating(); // Basics. - std.def_func::("heading"); - std.def_func::("list"); - std.def_func::("enum"); - std.def_func::("terms"); - std.def_func::("table"); + global.def_func::("heading"); + global.def_func::("list"); + global.def_func::("enum"); + global.def_func::("terms"); + global.def_func::("table"); // Text. - std.def_func::("text"); - std.def_func::("linebreak"); - std.def_func::("symbol"); - std.def_func::("smartquote"); - std.def_func::("strong"); - std.def_func::("emph"); - std.def_func::("lower"); - std.def_func::("upper"); - std.def_func::("smallcaps"); - std.def_func::("sub"); - std.def_func::("super"); - std.def_func::("underline"); - std.def_func::("strike"); - std.def_func::("overline"); - std.def_func::("raw"); + global.def_func::("text"); + global.def_func::("linebreak"); + global.def_func::("symbol"); + global.def_func::("smartquote"); + global.def_func::("strong"); + global.def_func::("emph"); + global.def_func::("lower"); + global.def_func::("upper"); + global.def_func::("smallcaps"); + global.def_func::("sub"); + global.def_func::("super"); + global.def_func::("underline"); + global.def_func::("strike"); + global.def_func::("overline"); + global.def_func::("raw"); // Math. - math::define(&mut std); + global.define("math", math); // Layout. - std.def_func::("page"); - std.def_func::("pagebreak"); - std.def_func::("v"); - std.def_func::("par"); - std.def_func::("parbreak"); - std.def_func::("h"); - std.def_func::("box"); - std.def_func::("block"); - std.def_func::("stack"); - std.def_func::("grid"); - std.def_func::("columns"); - std.def_func::("colbreak"); - std.def_func::("place"); - std.def_func::("align"); - std.def_func::("pad"); - std.def_func::("repeat"); - std.def_func::("move"); - std.def_func::("scale"); - std.def_func::("rotate"); - std.def_func::("hide"); + global.def_func::("page"); + global.def_func::("pagebreak"); + global.def_func::("v"); + global.def_func::("par"); + global.def_func::("parbreak"); + global.def_func::("h"); + global.def_func::("box"); + global.def_func::("block"); + global.def_func::("stack"); + global.def_func::("grid"); + global.def_func::("columns"); + global.def_func::("colbreak"); + global.def_func::("place"); + global.def_func::("align"); + global.def_func::("pad"); + global.def_func::("repeat"); + global.def_func::("move"); + global.def_func::("scale"); + global.def_func::("rotate"); + global.def_func::("hide"); // Visualize. - std.def_func::("image"); - std.def_func::("line"); - std.def_func::("rect"); - std.def_func::("square"); - std.def_func::("ellipse"); - std.def_func::("circle"); + global.def_func::("image"); + global.def_func::("line"); + global.def_func::("rect"); + global.def_func::("square"); + global.def_func::("ellipse"); + global.def_func::("circle"); // Meta. - std.def_func::("document"); - std.def_func::("ref"); - std.def_func::("link"); - std.def_func::("outline"); + global.def_func::("document"); + global.def_func::("ref"); + global.def_func::("link"); + global.def_func::("outline"); // Compute. - std.def_func::("type"); - std.def_func::("repr"); - std.def_func::("assert"); - std.def_func::("eval"); - std.def_func::("int"); - std.def_func::("float"); - std.def_func::("luma"); - std.def_func::("rgb"); - std.def_func::("cmyk"); - std.def_func::("str"); - std.def_func::("label"); - std.def_func::("regex"); - std.def_func::("range"); - std.def_func::("abs"); - std.def_func::("min"); - std.def_func::("max"); - std.def_func::("even"); - std.def_func::("odd"); - std.def_func::("mod"); - std.def_func::("read"); - std.def_func::("csv"); - std.def_func::("json"); - std.def_func::("xml"); - std.def_func::("lorem"); - std.def_func::("numbering"); + global.def_func::("type"); + global.def_func::("repr"); + global.def_func::("assert"); + global.def_func::("eval"); + global.def_func::("int"); + global.def_func::("float"); + global.def_func::("luma"); + global.def_func::("rgb"); + global.def_func::("cmyk"); + global.def_func::("str"); + global.def_func::("label"); + global.def_func::("regex"); + global.def_func::("range"); + global.def_func::("abs"); + global.def_func::("min"); + global.def_func::("max"); + global.def_func::("even"); + global.def_func::("odd"); + global.def_func::("mod"); + global.def_func::("read"); + global.def_func::("csv"); + global.def_func::("json"); + global.def_func::("xml"); + global.def_func::("lorem"); + global.def_func::("numbering"); // Colors. - std.define("black", Color::BLACK); - std.define("gray", Color::GRAY); - std.define("silver", Color::SILVER); - std.define("white", Color::WHITE); - std.define("navy", Color::NAVY); - std.define("blue", Color::BLUE); - std.define("aqua", Color::AQUA); - std.define("teal", Color::TEAL); - std.define("eastern", Color::EASTERN); - std.define("purple", Color::PURPLE); - std.define("fuchsia", Color::FUCHSIA); - std.define("maroon", Color::MAROON); - std.define("red", Color::RED); - std.define("orange", Color::ORANGE); - std.define("yellow", Color::YELLOW); - std.define("olive", Color::OLIVE); - std.define("green", Color::GREEN); - std.define("lime", Color::LIME); + global.define("black", Color::BLACK); + global.define("gray", Color::GRAY); + global.define("silver", Color::SILVER); + global.define("white", Color::WHITE); + global.define("navy", Color::NAVY); + global.define("blue", Color::BLUE); + global.define("aqua", Color::AQUA); + global.define("teal", Color::TEAL); + global.define("eastern", Color::EASTERN); + global.define("purple", Color::PURPLE); + global.define("fuchsia", Color::FUCHSIA); + global.define("maroon", Color::MAROON); + global.define("red", Color::RED); + global.define("orange", Color::ORANGE); + global.define("yellow", Color::YELLOW); + global.define("olive", Color::OLIVE); + global.define("green", Color::GREEN); + global.define("lime", Color::LIME); // Other constants. - std.define("ltr", Dir::LTR); - std.define("rtl", Dir::RTL); - std.define("ttb", Dir::TTB); - std.define("btt", Dir::BTT); - std.define("start", GenAlign::Start); - std.define("end", GenAlign::End); - std.define("left", GenAlign::Specific(Align::Left)); - std.define("center", GenAlign::Specific(Align::Center)); - std.define("right", GenAlign::Specific(Align::Right)); - std.define("top", GenAlign::Specific(Align::Top)); - std.define("horizon", GenAlign::Specific(Align::Horizon)); - std.define("bottom", GenAlign::Specific(Align::Bottom)); - - std + global.define("ltr", Dir::LTR); + global.define("rtl", Dir::RTL); + global.define("ttb", Dir::TTB); + global.define("btt", Dir::BTT); + global.define("start", GenAlign::Start); + global.define("end", GenAlign::End); + global.define("left", GenAlign::Specific(Align::Left)); + global.define("center", GenAlign::Specific(Align::Center)); + global.define("right", GenAlign::Specific(Align::Right)); + global.define("top", GenAlign::Specific(Align::Top)); + global.define("horizon", GenAlign::Specific(Align::Horizon)); + global.define("bottom", GenAlign::Specific(Align::Bottom)); + + Module::new("global").with_scope(global) } /// Construct the standard style map. @@ -187,7 +189,7 @@ fn items() -> LangItems { term_item: |term, description| { layout::ListItem::Term(basics::TermItem { term, description }).pack() }, - math: |body, block| math::MathNode { body, block }.pack(), + math_formula: |body, block| math::FormulaNode { body, block }.pack(), math_atom: |atom| math::AtomNode(atom).pack(), math_delimited: |body| math::LrNode(body).pack(), math_script: |base, sub, sup| math::ScriptNode { base, sub, sup }.pack(), diff --git a/library/src/math/accent.rs b/library/src/math/accent.rs index b8c31c19..a15a6020 100644 --- a/library/src/math/accent.rs +++ b/library/src/math/accent.rs @@ -133,7 +133,7 @@ fn attachment(ctx: &MathContext, id: GlyphId, italics_correction: Abs) -> Abs { /// Extract a single character from content. fn extract(accent: &Content) -> Option { - let atom = accent.to::()?.body.to::()?; + let atom = accent.to::()?.body.to::()?; let mut chars = atom.0.chars(); let c = chars.next().filter(|_| chars.next().is_none())?; Some(combining(c)) diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs index 50a68fea..e945473c 100644 --- a/library/src/math/frac.rs +++ b/library/src/math/frac.rs @@ -98,6 +98,7 @@ fn layout( denom: &Content, binom: bool, ) -> SourceResult<()> { + let short_fall = DELIM_SHORT_FALL.scaled(ctx); let axis = scaled!(ctx, axis_height); let thickness = scaled!(ctx, fraction_rule_thickness); let shift_up = scaled!( @@ -149,9 +150,9 @@ fn layout( frame.push_frame(denom_pos, denom); if binom { - ctx.push(GlyphFragment::new(ctx, '(')); + ctx.push(GlyphFragment::new(ctx, '(').stretch_vertical(ctx, height, short_fall)); ctx.push(frame); - ctx.push(GlyphFragment::new(ctx, ')')); + ctx.push(GlyphFragment::new(ctx, ')').stretch_vertical(ctx, height, short_fall)); } else { frame.push( line_pos, diff --git a/library/src/math/lr.rs b/library/src/math/lr.rs index 9cfc6e5f..89d12380 100644 --- a/library/src/math/lr.rs +++ b/library/src/math/lr.rs @@ -1,7 +1,7 @@ use super::*; /// How much less high scaled delimiters can be than what they wrap. -const DELIM_SHORT_FALL: Em = Em::new(0.1); +pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1); /// # Left-Right /// Scales delimiters. @@ -62,7 +62,7 @@ impl LayoutMath for LrNode { } let MathFragment::Glyph(glyph) = *fragment else { continue }; - let short_fall = DELIM_SHORT_FALL.at(glyph.font_size); + let short_fall = DELIM_SHORT_FALL.scaled(ctx); *fragment = MathFragment::Variant( glyph.stretch_vertical(ctx, height, short_fall), ); @@ -76,3 +76,90 @@ impl LayoutMath for LrNode { Ok(()) } } + +/// # Floor +/// Floor an expression. +/// +/// ## Example +/// ``` +/// $ floor(x/2) $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The expression to floor. +/// +/// ## Category +/// math +#[func] +pub fn floor(args: &mut Args) -> SourceResult { + delimited(args, '⌊', '⌋') +} + +/// # Ceil +/// Ceil an expression. +/// +/// ## Example +/// ``` +/// $ ceil(x/2) $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The expression to ceil. +/// +/// ## Category +/// math +#[func] +pub fn ceil(args: &mut Args) -> SourceResult { + delimited(args, '⌈', '⌉') +} + +/// # Abs +/// Take the absolute value of an expression. +/// +/// ## Example +/// ``` +/// $ abs(x/2) $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The expression to take the absolute value of. +/// +/// ## Category +/// math +#[func] +pub fn abs(args: &mut Args) -> SourceResult { + delimited(args, '|', '|') +} + +/// # Norm +/// Take the norm of an expression. +/// +/// ## Example +/// ``` +/// $ norm(x/2) $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The expression to take the norm of. +/// +/// ## Category +/// math +#[func] +pub fn norm(args: &mut Args) -> SourceResult { + delimited(args, '‖', '‖') +} + +fn delimited(args: &mut Args, left: char, right: char) -> SourceResult { + Ok(Value::Content( + LrNode(Content::sequence(vec![ + AtomNode(left.into()).pack(), + args.expect::("body")?, + AtomNode(right.into()).pack(), + ])) + .pack(), + )) +} diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs index aa21e9cf..9472e989 100644 --- a/library/src/math/matrix.rs +++ b/library/src/math/matrix.rs @@ -161,6 +161,7 @@ fn layout( ) -> SourceResult<()> { let axis = scaled!(ctx, axis_height); let gap = ROW_GAP.scaled(ctx); + let short_fall = DELIM_SHORT_FALL.scaled(ctx); ctx.style(ctx.style.for_denominator()); let mut rows = vec![]; @@ -169,17 +170,20 @@ fn layout( } ctx.unstyle(); - if let Some(left) = left { - ctx.push(GlyphFragment::new(ctx, left)); - } - let mut frame = stack(ctx, rows, align, gap, 0); + let height = frame.height(); frame.set_baseline(frame.height() / 2.0 + axis); + if let Some(left) = left { + ctx.push(GlyphFragment::new(ctx, left).stretch_vertical(ctx, height, short_fall)); + } + ctx.push(frame); if let Some(right) = right { - ctx.push(GlyphFragment::new(ctx, right)); + ctx.push( + GlyphFragment::new(ctx, right).stretch_vertical(ctx, height, short_fall), + ); } Ok(()) diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index 44b52e96..0de440be 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -33,7 +33,7 @@ pub use self::style::*; use ttf_parser::GlyphId; use ttf_parser::Rect; use typst::font::Font; -use typst::model::{Guard, Scope, SequenceNode}; +use typst::model::{Guard, Module, Scope, SequenceNode}; use unicode_math_class::MathClass; use self::ctx::*; @@ -48,38 +48,38 @@ use crate::text::TextNode; use crate::text::TextSize; use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode, SymbolNode}; -/// Hook up all math definitions. -pub fn define(scope: &mut Scope) { - scope.def_func::("math"); - scope.def_func::("lr"); - scope.def_func::("accent"); - scope.def_func::("frac"); - scope.def_func::("binom"); - scope.def_func::("script"); - scope.def_func::("sqrt"); - scope.def_func::("root"); - scope.def_func::("floor"); - scope.def_func::("ceil"); - scope.def_func::("vec"); - scope.def_func::("cases"); - scope.def_func::("underbrace"); - scope.def_func::("overbrace"); - scope.def_func::("bold"); - scope.def_func::("italic"); - scope.def_func::("serif"); - scope.def_func::("sans"); - scope.def_func::("cal"); - scope.def_func::("frak"); - scope.def_func::("mono"); - scope.def_func::("bb"); - scope.define("thin", HNode::strong(THIN).pack()); - scope.define("med", HNode::strong(MEDIUM).pack()); - scope.define("thick", HNode::strong(THICK).pack()); - scope.define("quad", HNode::strong(QUAD).pack()); - define_operators(scope); +/// Create a module with all math definitions. +pub fn module() -> Module { + let mut math = Scope::deduplicating(); + math.def_func::("formula"); + math.def_func::("lr"); + math.def_func::("floor"); + math.def_func::("ceil"); + math.def_func::("abs"); + math.def_func::("accent"); + math.def_func::("frac"); + math.def_func::("binom"); + math.def_func::("script"); + math.def_func::("sqrt"); + math.def_func::("root"); + math.def_func::("vec"); + math.def_func::("cases"); + math.def_func::("underbrace"); + math.def_func::("overbrace"); + math.def_func::("bold"); + math.def_func::("italic"); + math.def_func::("serif"); + math.def_func::("sans"); + math.def_func::("cal"); + math.def_func::("frak"); + math.def_func::("mono"); + math.def_func::("bb"); + define_spacings(&mut math); + define_operators(&mut math); + Module::new("math").with_scope(math) } -/// # Math +/// # Formula /// A mathematical formula. /// /// ## Syntax @@ -131,7 +131,7 @@ pub fn define(scope: &mut Scope) { #[func] #[capable(Show, Finalize, Layout, Inline, LayoutMath)] #[derive(Debug, Clone, Hash)] -pub struct MathNode { +pub struct FormulaNode { /// Whether the formula is displayed as a separate block. pub block: bool, /// The content of the formula. @@ -139,7 +139,7 @@ pub struct MathNode { } #[node] -impl MathNode { +impl FormulaNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let body = args.expect("body")?; let block = args.named("block")?.unwrap_or(false); @@ -148,13 +148,14 @@ impl MathNode { fn field(&self, name: &str) -> Option { match name { + "body" => Some(Value::Content(self.body.clone())), "block" => Some(Value::Bool(self.block)), _ => None, } } } -impl Show for MathNode { +impl Show for FormulaNode { fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult { let mut realized = self.clone().pack().guarded(Guard::Base(NodeId::of::())); if self.block { @@ -164,7 +165,7 @@ impl Show for MathNode { } } -impl Finalize for MathNode { +impl Finalize for FormulaNode { fn finalize(&self, realized: Content) -> Content { realized.styled( TextNode::FAMILY, @@ -173,7 +174,7 @@ impl Finalize for MathNode { } } -impl Layout for MathNode { +impl Layout for FormulaNode { fn layout( &self, vt: &mut Vt, @@ -200,14 +201,14 @@ impl Layout for MathNode { } } -impl Inline for MathNode {} +impl Inline for FormulaNode {} #[capability] trait LayoutMath { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()>; } -impl LayoutMath for MathNode { +impl LayoutMath for FormulaNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { self.body.layout_math(ctx) } diff --git a/library/src/math/op.rs b/library/src/math/op.rs index aef0d41b..a7d29166 100644 --- a/library/src/math/op.rs +++ b/library/src/math/op.rs @@ -25,12 +25,6 @@ pub struct OpNode { pub limits: bool, } -impl OpNode { - fn new(text: impl Into, limits: bool) -> Self { - Self { text: text.into(), limits } - } -} - #[node] impl OpNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { @@ -55,96 +49,41 @@ impl LayoutMath for OpNode { } /// Hook up all operators. -pub fn define_operators(scope: &mut Scope) { - let mut define = |name: &str, limits| { - scope.define(name, OpNode { text: name.into(), limits }.pack()); - }; - - // These have the same name in code and display. - define("arccos", false); - define("arcsin", false); - define("arctan", false); - define("arg", false); - define("cos", false); - define("cosh", false); - define("cot", false); - define("coth", false); - define("csc", false); - define("deg", false); - define("det", true); - define("dim", false); - define("exp", false); - define("gcd", true); - define("hom", false); - define("inf", true); - define("ker", false); - define("lg", false); - define("lim", true); - define("ln", false); - define("log", false); - define("max", true); - define("min", true); - define("Pr", true); - define("sec", false); - define("sin", false); - define("sinh", false); - define("sup", true); - define("tan", false); - define("tanh", false); - - // These have an extra thin space. - scope.define("liminf", OpNode::new("lim inf", true).pack()); - scope.define("limsup", OpNode::new("lim sup", true).pack()); -} - -/// # Floor -/// A floored expression. -/// -/// ## Example -/// ``` -/// $ floor(x/2) $ -/// ``` -/// -/// ## Parameters -/// - body: Content (positional, required) -/// The expression to floor. -/// -/// ## Category -/// math -#[func] -#[capable] -#[derive(Debug, Hash)] -pub struct FloorNode(pub Content); - -#[node] -impl FloorNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult { - Ok(Self(args.expect("body")?).pack()) - } +pub(super) fn define_operators(math: &mut Scope) { + math.define("arccos", op("arccos", false)); + math.define("arcsin", op("arcsin", false)); + math.define("arctan", op("arctan", false)); + math.define("arg", op("arg", false)); + math.define("cos", op("cos", false)); + math.define("cosh", op("cosh", false)); + math.define("cot", op("cot", false)); + math.define("coth", op("coth", false)); + math.define("csc", op("csc", false)); + math.define("deg", op("deg", false)); + math.define("det", op("det", true)); + math.define("dim", op("dim", false)); + math.define("exp", op("exp", false)); + math.define("gcd", op("gcd", true)); + math.define("hom", op("hom", false)); + math.define("inf", op("inf", true)); + math.define("ker", op("ker", false)); + math.define("lg", op("lg", false)); + math.define("lim", op("lim", true)); + math.define("ln", op("ln", false)); + math.define("log", op("log", false)); + math.define("max", op("max", true)); + math.define("min", op("min", true)); + math.define("Pr", op("Pr", true)); + math.define("sec", op("sec", false)); + math.define("sin", op("sin", false)); + math.define("sinh", op("sinh", false)); + math.define("sup", op("sup", true)); + math.define("tan", op("tan", false)); + math.define("tanh", op("tanh", false)); + math.define("liminf", op("lim inf", true)); + math.define("limsup", op("lim sup", true)); } -/// # Ceil -/// A ceiled expression. -/// -/// ## Example -/// ``` -/// $ ceil(x/2) $ -/// ``` -/// -/// ## Parameters -/// - body: Content (positional, required) -/// The expression to ceil. -/// -/// ## Category -/// math -#[func] -#[capable] -#[derive(Debug, Hash)] -pub struct CeilNode(pub Content); - -#[node] -impl CeilNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult { - Ok(Self(args.expect("body")?).pack()) - } +fn op(name: &str, limits: bool) -> Content { + OpNode { text: name.into(), limits }.pack() } diff --git a/library/src/math/root.rs b/library/src/math/root.rs index f5c5b2b8..2efe4e07 100644 --- a/library/src/math/root.rs +++ b/library/src/math/root.rs @@ -155,7 +155,7 @@ fn layout( /// Select a precomposed radical, if the font has it. fn precomposed(ctx: &MathContext, index: Option<&Content>, target: Abs) -> Option { - let node = index?.to::()?.body.to::()?; + let node = index?.to::()?.body.to::()?; let c = match node.0.as_str() { "3" => '∛', "4" => '∜', diff --git a/library/src/math/spacing.rs b/library/src/math/spacing.rs index 0f613309..d5a7603d 100644 --- a/library/src/math/spacing.rs +++ b/library/src/math/spacing.rs @@ -1,10 +1,18 @@ use super::*; -pub(super) const ZERO: Em = Em::zero(); -pub(super) const THIN: Em = Em::new(1.0 / 6.0); -pub(super) const MEDIUM: Em = Em::new(2.0 / 9.0); -pub(super) const THICK: Em = Em::new(5.0 / 18.0); -pub(super) const QUAD: Em = Em::new(1.0); +const ZERO: Em = Em::zero(); +const THIN: Em = Em::new(1.0 / 6.0); +const MEDIUM: Em = Em::new(2.0 / 9.0); +const THICK: Em = Em::new(5.0 / 18.0); +const QUAD: Em = Em::new(1.0); + +/// Hook up all spacings. +pub(super) fn define_spacings(math: &mut Scope) { + math.define("thin", HNode::strong(THIN).pack()); + math.define("med", HNode::strong(MEDIUM).pack()); + math.define("thick", HNode::strong(THICK).pack()); + math.define("quad", HNode::strong(QUAD).pack()); +} /// Determine the spacing between two fragments in a given style. pub(super) fn spacing(left: MathClass, right: MathClass, style: MathStyle) -> Em { -- cgit v1.2.3