diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-01-22 13:31:44 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-01-22 13:31:44 +0100 |
| commit | c5ef350cce93ef2813a5aaec12df604e1be4d146 (patch) | |
| tree | 14d43da3a49c7cf945ddf1e1020a20da1e5477b0 /library | |
| parent | bcf20610fc4d5dcac43bf076c1ea0c9b433de0a0 (diff) | |
Under- and overbraces
Diffstat (limited to 'library')
| -rw-r--r-- | library/src/math/braced.rs | 123 | ||||
| -rw-r--r-- | library/src/math/mod.rs | 3 |
2 files changed, 126 insertions, 0 deletions
diff --git a/library/src/math/braced.rs b/library/src/math/braced.rs new file mode 100644 index 00000000..e207cb92 --- /dev/null +++ b/library/src/math/braced.rs @@ -0,0 +1,123 @@ +use super::*; + +const BRACED_GAP: Em = Em::new(0.3); + +/// # Underbrace +/// A horizontal brace under content, with an optional annotation below. +/// +/// ## Example +/// ``` +/// $ underbrace(1 + 2 + ... + 5, "numbers") $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The content above the brace. +/// +/// - annotation: Content (positional) +/// The optional content below the brace. +/// +/// ## Category +/// math +#[func] +#[capable(LayoutMath)] +#[derive(Debug, Hash)] +pub struct UnderbraceNode { + /// The content above the brace. + pub body: Content, + /// The optional content below the brace. + pub annotation: Option<Content>, +} + +#[node] +impl UnderbraceNode { + fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { + let body = args.expect("body")?; + let annotation = args.eat()?; + Ok(Self { body, annotation }.pack()) + } +} + +impl LayoutMath for UnderbraceNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + let gap = BRACED_GAP.scaled(ctx); + let body = ctx.layout_row(&self.body)?; + let glyph = GlyphFragment::new(ctx, '⏟'); + let brace = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); + + let mut rows = vec![body, brace.into()]; + ctx.style(ctx.style.for_subscript()); + rows.extend( + self.annotation + .as_ref() + .map(|annotation| ctx.layout_row(annotation)) + .transpose()?, + ); + ctx.unstyle(); + ctx.push(stack(ctx, rows, Align::Center, gap, 0)); + + Ok(()) + } +} + +/// # Overbrace +/// A horizontal brace over content, with an optional annotation above. +/// +/// ## Example +/// ``` +/// $ overbrace(1 + 2 + ... + 5, "numbers") $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, required) +/// The content below the brace. +/// +/// - annotation: Content (positional) +/// The optional content above the brace. +/// +/// ## Category +/// math +#[func] +#[capable(LayoutMath)] +#[derive(Debug, Hash)] +pub struct OverbraceNode { + /// The content below the brace. + pub body: Content, + /// The optional content above the brace. + pub annotation: Option<Content>, +} + +#[node] +impl OverbraceNode { + fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { + let body = args.expect("body")?; + let annotation = args.eat()?; + Ok(Self { body, annotation }.pack()) + } +} + +impl LayoutMath for OverbraceNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + let gap = BRACED_GAP.scaled(ctx); + let body = ctx.layout_row(&self.body)?; + let glyph = GlyphFragment::new(ctx, '⏞'); + let brace = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); + + let mut rows = vec![]; + ctx.style(ctx.style.for_superscript()); + rows.extend( + self.annotation + .as_ref() + .map(|annotation| ctx.layout_row(annotation)) + .transpose()?, + ); + ctx.unstyle(); + rows.push(brace.into()); + rows.push(body); + + let last = rows.len() - 1; + ctx.push(stack(ctx, rows, Align::Center, gap, last)); + + Ok(()) + } +} diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index c7b25db3..34eace04 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -5,6 +5,7 @@ mod ctx; mod accent; mod align; mod atom; +mod braced; mod frac; mod group; mod matrix; @@ -55,6 +56,8 @@ pub fn define(scope: &mut Scope) { scope.def_func::<RootNode>("root"); 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"); |
