diff options
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | library/src/lib.rs | 2 | ||||
| -rw-r--r-- | library/src/math/mod.rs | 97 | ||||
| -rw-r--r-- | library/src/math/tex.rs | 20 | ||||
| -rw-r--r-- | src/model/eval.rs | 4 | ||||
| -rw-r--r-- | tests/ref/math/matrix.png | bin | 0 -> 4854 bytes | |||
| -rw-r--r-- | tests/ref/math/shorthand.png | bin | 1230 -> 1231 bytes | |||
| -rw-r--r-- | tests/ref/math/simple.png | bin | 6555 -> 6560 bytes | |||
| -rw-r--r-- | tests/ref/math/syntax.png | bin | 53462 -> 53524 bytes | |||
| -rw-r--r-- | tests/typ/math/matrix.typ | 20 |
10 files changed, 126 insertions, 19 deletions
@@ -846,7 +846,7 @@ dependencies = [ [[package]] name = "rex" version = "0.1.2" -source = "git+https://github.com/laurmaedje/ReX#672c321a947f945e9ba936ae9fbd982c4e043f1c" +source = "git+https://github.com/laurmaedje/ReX#523b29bd39a4daf50f9bb3ee48689c66b209c4ff" dependencies = [ "itertools", "nom", diff --git a/library/src/lib.rs b/library/src/lib.rs index af5c252b..29a6cc94 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -53,6 +53,8 @@ fn scope() -> Scope { std.def_node::<math::AtomNode>("atom"); std.def_node::<math::FracNode>("frac"); std.def_node::<math::SqrtNode>("sqrt"); + std.def_node::<math::VecNode>("vec"); + std.def_node::<math::CasesNode>("cases"); // Layout. std.def_node::<layout::PageNode>("page"); diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index 1e8145cc..c613ea2a 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -54,7 +54,7 @@ impl Layout for MathNode { styles: StyleChain, _: &Regions, ) -> SourceResult<Fragment> { - let mut t = Texifier::new(); + let mut t = Texifier::new(styles); self.texify(&mut t)?; layout_tex(vt, &t.finish(), self.display, styles) } @@ -71,7 +71,7 @@ trait Texify { /// Texify the node, but trim parentheses.. fn texify_unparen(&self, t: &mut Texifier) -> SourceResult<()> { let s = { - let mut sub = Texifier::new(); + let mut sub = Texifier::new(t.styles); self.texify(&mut sub)?; sub.finish() }; @@ -88,19 +88,21 @@ trait Texify { } /// Builds the TeX representation of the formula. -struct Texifier { +struct Texifier<'a> { tex: EcoString, support: bool, space: bool, + styles: StyleChain<'a>, } -impl Texifier { +impl<'a> Texifier<'a> { /// Create a new texifier. - fn new() -> Self { + fn new(styles: StyleChain<'a>) -> Self { Self { tex: EcoString::new(), support: false, space: false, + styles, } } @@ -346,7 +348,7 @@ impl Texify for AlignNode { } } -/// A square root node. +/// A square root. #[derive(Debug, Hash)] pub struct SqrtNode(Content); @@ -365,3 +367,86 @@ impl Texify for SqrtNode { Ok(()) } } + +/// A column vector. +#[derive(Debug, Hash)] +pub struct VecNode(Vec<Content>); + +#[node(Texify)] +impl VecNode { + /// The kind of delimiter. + pub const DELIM: Delimiter = Delimiter::Paren; + + fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { + Ok(Self(args.all()?).pack()) + } +} + +impl Texify for VecNode { + fn texify(&self, t: &mut Texifier) -> SourceResult<()> { + let kind = match t.styles.get(Self::DELIM) { + Delimiter::Paren => "pmatrix", + Delimiter::Bracket => "bmatrix", + Delimiter::Brace => "Bmatrix", + Delimiter::Bar => "vmatrix", + }; + + t.push_str("\\begin{"); + t.push_str(kind); + t.push_str("}"); + + for component in &self.0 { + component.texify_unparen(t)?; + t.push_str("\\\\"); + } + t.push_str("\\end{"); + t.push_str(kind); + t.push_str("}"); + + Ok(()) + } +} + +/// A vector / matrix delimiter. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum Delimiter { + Paren, + Bracket, + Brace, + Bar, +} + +castable! { + Delimiter, + Expected: "type of bracket or bar", + Value::Str(s) => match s.as_str() { + "(" => Self::Paren, + "[" => Self::Bracket, + "{" => Self::Brace, + "|" => Self::Bar, + _ => Err("expected \"(\", \"[\", \"{\", or \"|\"")?, + }, +} + +/// A case distinction. +#[derive(Debug, Hash)] +pub struct CasesNode(Vec<Content>); + +#[node(Texify)] +impl CasesNode { + fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { + Ok(Self(args.all()?).pack()) + } +} + +impl Texify for CasesNode { + fn texify(&self, t: &mut Texifier) -> SourceResult<()> { + t.push_str("\\begin{cases}"); + for component in &self.0 { + component.texify_unparen(t)?; + t.push_str("\\\\"); + } + t.push_str("\\end{cases}"); + Ok(()) + } +} diff --git a/library/src/math/tex.rs b/library/src/math/tex.rs index da07f1d6..e8917f30 100644 --- a/library/src/math/tex.rs +++ b/library/src/math/tex.rs @@ -69,7 +69,7 @@ pub fn layout_tex( }, baseline: top, font: font.clone(), - fill: styles.get(TextNode::FILL), + paint: styles.get(TextNode::FILL), lang: styles.get(TextNode::LANG), colors: vec![], }; @@ -85,18 +85,18 @@ struct FrameBackend { frame: Frame, baseline: Abs, font: Font, - fill: Paint, + paint: Paint, lang: Lang, colors: Vec<RGBA>, } impl FrameBackend { - /// The currently active fill paint. - fn fill(&self) -> Paint { + /// The currently active paint. + fn paint(&self) -> Paint { self.colors .last() .map(|&RGBA(r, g, b, a)| RgbaColor::new(r, g, b, a).into()) - .unwrap_or(self.fill) + .unwrap_or(self.paint) } /// Convert a cursor to a point. @@ -112,7 +112,7 @@ impl Backend for FrameBackend { Element::Text(Text { font: self.font.clone(), size: Abs::pt(scale), - fill: self.fill(), + fill: self.paint(), lang: self.lang, glyphs: vec