From 7e295d84b55322e84695e793af8d64b6ec89e357 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 22 Jan 2023 13:32:58 +0100 Subject: Math delimiter grouping --- library/src/lib.rs | 1 + library/src/math/lr.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ library/src/math/mod.rs | 2 ++ 3 files changed, 81 insertions(+) create mode 100644 library/src/math/lr.rs (limited to 'library/src') diff --git a/library/src/lib.rs b/library/src/lib.rs index f714c72b..c2f51ee4 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -189,6 +189,7 @@ fn items() -> LangItems { }, math: |body, block| math::MathNode { 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(), math_frac: |num, denom| math::FracNode { num, denom }.pack(), math_align_point: || math::AlignPointNode.pack(), diff --git a/library/src/math/lr.rs b/library/src/math/lr.rs new file mode 100644 index 00000000..9cfc6e5f --- /dev/null +++ b/library/src/math/lr.rs @@ -0,0 +1,78 @@ +use super::*; + +/// How much less high scaled delimiters can be than what they wrap. +const DELIM_SHORT_FALL: Em = Em::new(0.1); + +/// # Left-Right +/// Scales delimiters. +/// +/// While matched delimiters scale by default, this can be used to scale +/// unmatched delimiters and to control the delimiter scaling more precisely. +/// +/// ## Example +/// ``` +/// $ lr(]a, b/2]) $ +/// ``` +/// +/// ## Parameters +/// - body: Content (positional, variadic) +/// The delimited content, including the delimiters. +/// +/// ## Category +/// math +#[func] +#[capable(LayoutMath)] +#[derive(Debug, Hash)] +pub struct LrNode(pub Content); + +#[node] +impl LrNode { + fn construct(_: &Vm, args: &mut Args) -> SourceResult { + let mut body = Content::empty(); + for (i, arg) in args.all::()?.into_iter().enumerate() { + if i > 0 { + body += AtomNode(','.into()).pack(); + } + body += arg; + } + Ok(Self(body).pack()) + } +} + +impl LayoutMath for LrNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + let mut row = ctx.layout_row(&self.0)?; + + let axis = scaled!(ctx, axis_height); + let max_extent = row + .0 + .iter() + .map(|fragment| (fragment.ascent() - axis).max(fragment.descent() + axis)) + .max() + .unwrap_or_default(); + + let height = 2.0 * max_extent; + if let [first, .., last] = row.0.as_mut_slice() { + for fragment in [first, last] { + if !matches!( + fragment.class(), + Some(MathClass::Opening | MathClass::Closing | MathClass::Fence) + ) { + continue; + } + + let MathFragment::Glyph(glyph) = *fragment else { continue }; + let short_fall = DELIM_SHORT_FALL.at(glyph.font_size); + *fragment = MathFragment::Variant( + glyph.stretch_vertical(ctx, height, short_fall), + ); + } + } + + for fragment in row.0 { + ctx.push(fragment); + } + + Ok(()) + } +} diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index ae660405..44b52e96 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -8,6 +8,7 @@ mod atom; mod braced; mod frac; mod fragment; +mod lr; mod matrix; mod op; mod root; @@ -50,6 +51,7 @@ use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode, Symbol /// 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"); -- cgit v1.2.3