summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-22 13:32:58 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-22 13:32:58 +0100
commit7e295d84b55322e84695e793af8d64b6ec89e357 (patch)
tree4570ee01286e69846ed1be382e30d1d3b0ed9bed /library/src
parent953bdc1859f7acdbecbb7b819bc5b113a50849d0 (diff)
Math delimiter grouping
Diffstat (limited to 'library/src')
-rw-r--r--library/src/lib.rs1
-rw-r--r--library/src/math/lr.rs78
-rw-r--r--library/src/math/mod.rs2
3 files changed, 81 insertions, 0 deletions
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<Content> {
+ let mut body = Content::empty();
+ for (i, arg) in args.all::<Content>()?.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::<MathNode>("math");
+ scope.def_func::<LrNode>("lr");
scope.def_func::<AccentNode>("accent");
scope.def_func::<FracNode>("frac");
scope.def_func::<BinomNode>("binom");