Under- and overbraces
This commit is contained in:
parent
bcf20610fc
commit
c5ef350cce
123
library/src/math/braced.rs
Normal file
123
library/src/math/braced.rs
Normal file
@ -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(())
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user