summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-23 15:03:10 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-23 15:23:52 +0100
commit4653ffebb43d733a3cff873d0903c7d00aaeb499 (patch)
tree6a97b2e6a6903b3198547d6f3d0a7e3d2eb023cd /library
parent84c6c8b0e6b17996a603ec88b7490107154f38f3 (diff)
Math module
Diffstat (limited to 'library')
-rw-r--r--library/src/lib.rs230
-rw-r--r--library/src/math/accent.rs2
-rw-r--r--library/src/math/frac.rs5
-rw-r--r--library/src/math/lr.rs91
-rw-r--r--library/src/math/matrix.rs14
-rw-r--r--library/src/math/mod.rs77
-rw-r--r--library/src/math/op.rs131
-rw-r--r--library/src/math/root.rs2
-rw-r--r--library/src/math/spacing.rs18
9 files changed, 306 insertions, 264 deletions
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::<basics::HeadingNode>("heading");
- std.def_func::<basics::ListNode>("list");
- std.def_func::<basics::EnumNode>("enum");
- std.def_func::<basics::TermsNode>("terms");
- std.def_func::<basics::TableNode>("table");
+ global.def_func::<basics::HeadingNode>("heading");
+ global.def_func::<basics::ListNode>("list");
+ global.def_func::<basics::EnumNode>("enum");
+ global.def_func::<basics::TermsNode>("terms");
+ global.def_func::<basics::TableNode>("table");
// Text.
- std.def_func::<text::TextNode>("text");
- std.def_func::<text::LinebreakNode>("linebreak");
- std.def_func::<text::SymbolNode>("symbol");
- std.def_func::<text::SmartQuoteNode>("smartquote");
- std.def_func::<text::StrongNode>("strong");
- std.def_func::<text::EmphNode>("emph");
- std.def_func::<text::LowerFunc>("lower");
- std.def_func::<text::UpperFunc>("upper");
- std.def_func::<text::SmallcapsFunc>("smallcaps");
- std.def_func::<text::SubNode>("sub");
- std.def_func::<text::SuperNode>("super");
- std.def_func::<text::UnderlineNode>("underline");
- std.def_func::<text::StrikeNode>("strike");
- std.def_func::<text::OverlineNode>("overline");
- std.def_func::<text::RawNode>("raw");
+ global.def_func::<text::TextNode>("text");
+ global.def_func::<text::LinebreakNode>("linebreak");
+ global.def_func::<text::SymbolNode>("symbol");
+ global.def_func::<text::SmartQuoteNode>("smartquote");
+ global.def_func::<text::StrongNode>("strong");
+ global.def_func::<text::EmphNode>("emph");
+ global.def_func::<text::LowerFunc>("lower");
+ global.def_func::<text::UpperFunc>("upper");
+ global.def_func::<text::SmallcapsFunc>("smallcaps");
+ global.def_func::<text::SubNode>("sub");
+ global.def_func::<text::SuperNode>("super");
+ global.def_func::<text::UnderlineNode>("underline");
+ global.def_func::<text::StrikeNode>("strike");
+ global.def_func::<text::OverlineNode>("overline");
+ global.def_func::<text::RawNode>("raw");
// Math.
- math::define(&mut std);
+ global.define("math", math);
// Layout.
- std.def_func::<layout::PageNode>("page");
- std.def_func::<layout::PagebreakNode>("pagebreak");
- std.def_func::<layout::VNode>("v");
- std.def_func::<layout::ParNode>("par");
- std.def_func::<layout::ParbreakNode>("parbreak");
- std.def_func::<layout::HNode>("h");
- std.def_func::<layout::BoxNode>("box");
- std.def_func::<layout::BlockNode>("block");
- std.def_func::<layout::StackNode>("stack");
- std.def_func::<layout::GridNode>("grid");
- std.def_func::<layout::ColumnsNode>("columns");
- std.def_func::<layout::ColbreakNode>("colbreak");
- std.def_func::<layout::PlaceNode>("place");
- std.def_func::<layout::AlignNode>("align");
- std.def_func::<layout::PadNode>("pad");
- std.def_func::<layout::RepeatNode>("repeat");
- std.def_func::<layout::MoveNode>("move");
- std.def_func::<layout::ScaleNode>("scale");
- std.def_func::<layout::RotateNode>("rotate");
- std.def_func::<layout::HideNode>("hide");
+ global.def_func::<layout::PageNode>("page");
+ global.def_func::<layout::PagebreakNode>("pagebreak");
+ global.def_func::<layout::VNode>("v");
+ global.def_func::<layout::ParNode>("par");
+ global.def_func::<layout::ParbreakNode>("parbreak");
+ global.def_func::<layout::HNode>("h");
+ global.def_func::<layout::BoxNode>("box");
+ global.def_func::<layout::BlockNode>("block");
+ global.def_func::<layout::StackNode>("stack");
+ global.def_func::<layout::GridNode>("grid");
+ global.def_func::<layout::ColumnsNode>("columns");
+ global.def_func::<layout::ColbreakNode>("colbreak");
+ global.def_func::<layout::PlaceNode>("place");
+ global.def_func::<layout::AlignNode>("align");
+ global.def_func::<layout::PadNode>("pad");
+ global.def_func::<layout::RepeatNode>("repeat");
+ global.def_func::<layout::MoveNode>("move");
+ global.def_func::<layout::ScaleNode>("scale");
+ global.def_func::<layout::RotateNode>("rotate");
+ global.def_func::<layout::HideNode>("hide");
// Visualize.
- std.def_func::<visualize::ImageNode>("image");
- std.def_func::<visualize::LineNode>("line");
- std.def_func::<visualize::RectNode>("rect");
- std.def_func::<visualize::SquareNode>("square");
- std.def_func::<visualize::EllipseNode>("ellipse");
- std.def_func::<visualize::CircleNode>("circle");
+ global.def_func::<visualize::ImageNode>("image");
+ global.def_func::<visualize::LineNode>("line");
+ global.def_func::<visualize::RectNode>("rect");
+ global.def_func::<visualize::SquareNode>("square");
+ global.def_func::<visualize::EllipseNode>("ellipse");
+ global.def_func::<visualize::CircleNode>("circle");
// Meta.
- std.def_func::<meta::DocumentNode>("document");
- std.def_func::<meta::RefNode>("ref");
- std.def_func::<meta::LinkNode>("link");
- std.def_func::<meta::OutlineNode>("outline");
+ global.def_func::<meta::DocumentNode>("document");
+ global.def_func::<meta::RefNode>("ref");
+ global.def_func::<meta::LinkNode>("link");
+ global.def_func::<meta::OutlineNode>("outline");
// Compute.
- std.def_func::<compute::TypeFunc>("type");
- std.def_func::<compute::ReprFunc>("repr");
- std.def_func::<compute::AssertFunc>("assert");
- std.def_func::<compute::EvalFunc>("eval");
- std.def_func::<compute::IntFunc>("int");
- std.def_func::<compute::FloatFunc>("float");
- std.def_func::<compute::LumaFunc>("luma");
- std.def_func::<compute::RgbFunc>("rgb");
- std.def_func::<compute::CmykFunc>("cmyk");
- std.def_func::<compute::StrFunc>("str");
- std.def_func::<compute::LabelFunc>("label");
- std.def_func::<compute::RegexFunc>("regex");
- std.def_func::<compute::RangeFunc>("range");
- std.def_func::<compute::AbsFunc>("abs");
- std.def_func::<compute::MinFunc>("min");
- std.def_func::<compute::MaxFunc>("max");
- std.def_func::<compute::EvenFunc>("even");
- std.def_func::<compute::OddFunc>("odd");
- std.def_func::<compute::ModFunc>("mod");
- std.def_func::<compute::ReadFunc>("read");
- std.def_func::<compute::CsvFunc>("csv");
- std.def_func::<compute::JsonFunc>("json");
- std.def_func::<compute::XmlFunc>("xml");
- std.def_func::<compute::LoremFunc>("lorem");
- std.def_func::<compute::NumberingFunc>("numbering");
+ global.def_func::<compute::TypeFunc>("type");
+ global.def_func::<compute::ReprFunc>("repr");
+ global.def_func::<compute::AssertFunc>("assert");
+ global.def_func::<compute::EvalFunc>("eval");
+ global.def_func::<compute::IntFunc>("int");
+ global.def_func::<compute::FloatFunc>("float");
+ global.def_func::<compute::LumaFunc>("luma");
+ global.def_func::<compute::RgbFunc>("rgb");
+ global.def_func::<compute::CmykFunc>("cmyk");
+ global.def_func::<compute::StrFunc>("str");
+ global.def_func::<compute::LabelFunc>("label");
+ global.def_func::<compute::RegexFunc>("regex");
+ global.def_func::<compute::RangeFunc>("range");
+ global.def_func::<compute::AbsFunc>("abs");
+ global.def_func::<compute::MinFunc>("min");
+ global.def_func::<compute::MaxFunc>("max");
+ global.def_func::<compute::EvenFunc>("even");
+ global.def_func::<compute::OddFunc>("odd");
+ global.def_func::<compute::ModFunc>("mod");
+ global.def_func::<compute::ReadFunc>("read");
+ global.def_func::<compute::CsvFunc>("csv");
+ global.def_func::<compute::JsonFunc>("json");
+ global.def_func::<compute::XmlFunc>("xml");
+ global.def_func::<compute::LoremFunc>("lorem");
+ global.def_func::<compute::NumberingFunc>("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<char> {
- let atom = accent.to::<MathNode>()?.body.to::<AtomNode>()?;
+ let atom = accent.to::<FormulaNode>()?.body.to::<AtomNode>()?;
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<Value> {
+ 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<Value> {
+ 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<Value> {
+ 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<Value> {
+ delimited(args, '‖', '‖')
+}
+
+fn delimited(args: &mut Args, left: char, right: char) -> SourceResult<Value> {
+ Ok(Value::Content(
+ LrNode(Content::sequence(vec![
+ AtomNode(left.into()).pack(),
+ args.expect::<Content>("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::<MathNode>("math");
- scope.def_func::<LrNode>("lr");
- scope.def_func::<AccentNode>("accent");
- scope.def_func::<FracNode>("frac");
- scope.def_func::<BinomNode>("binom");
- scope.def_func::<ScriptNode>("script");
- scope.def_func::<SqrtNode>("sqrt");
- scope.def_func::<RootNode>("root");
- scope.def_func::<FloorNode>("floor");
- scope.def_func::<CeilNode>("ceil");
- scope.def_func::<VecNode>("vec");
- scope.def_func::<CasesNode>("cases");
- scope.def_func::<UnderbraceNode>("underbrace");
- scope.def_func::<OverbraceNode>("overbrace");
- scope.def_func::<BoldNode>("bold");
- scope.def_func::<ItalicNode>("italic");
- scope.def_func::<SerifNode>("serif");
- scope.def_func::<SansNode>("sans");
- scope.def_func::<CalNode>("cal");
- scope.def_func::<FrakNode>("frak");
- scope.def_func::<MonoNode>("mono");
- scope.def_func::<BbNode>("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::<FormulaNode>("formula");
+ math.def_func::<LrNode>("lr");
+ math.def_func::<FloorFunc>("floor");
+ math.def_func::<CeilFunc>("ceil");
+ math.def_func::<AbsFunc>("abs");
+ math.def_func::<AccentNode>("accent");
+ math.def_func::<FracNode>("frac");
+ math.def_func::<BinomNode>("binom");
+ math.def_func::<ScriptNode>("script");
+ math.def_func::<SqrtNode>("sqrt");
+ math.def_func::<RootNode>("root");
+ math.def_func::<VecNode>("vec");
+ math.def_func::<CasesNode>("cases");
+ math.def_func::<UnderbraceNode>("underbrace");
+ math.def_func::<OverbraceNode>("overbrace");
+ math.def_func::<BoldNode>("bold");
+ math.def_func::<ItalicNode>("italic");
+ math.def_func::<SerifNode>("serif");
+ math.def_func::<SansNode>("sans");
+ math.def_func::<CalNode>("cal");
+ math.def_func::<FrakNode>("frak");
+ math.def_func::<MonoNode>("mono");
+ math.def_func::<BbNode>("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<Content> {
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<Value> {
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<Content> {
let mut realized = self.clone().pack().guarded(Guard::Base(NodeId::of::<Self>()));
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<EcoString>, limits: bool) -> Self {
- Self { text: text.into(), limits }
- }
-}
-
#[node]
impl OpNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
@@ -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<Content> {
- 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<Content> {
- 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<Frame> {
- let node = index?.to::<MathNode>()?.body.to::<AtomNode>()?;
+ let node = index?.to::<FormulaNode>()?.body.to::<AtomNode>()?;
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 {