use super::*; const LINE_GAP: Em = Em::new(0.15); const BRACE_GAP: Em = Em::new(0.25); const BRACKET_GAP: Em = Em::new(0.25); /// # Underline /// A horizontal line under content. /// /// ## Example /// ``` /// $ underline(1 + 2 + ... + 5) $ /// ``` /// /// ## Parameters /// - body: Content (positional, required) /// The content above the line. /// /// ## Category /// math #[func] #[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct UnderlineNode(Content); #[node] impl UnderlineNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } } impl LayoutMath for UnderlineNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout(ctx, &self.0, &None, '\u{305}', LINE_GAP, false) } } /// # Overline /// A horizontal line over content. /// /// ## Example /// ``` /// $ overline(1 + 2 + ... + 5) $ /// ``` /// /// ## Parameters /// - body: Content (positional, required) /// The content below the line. /// /// ## Category /// math #[func] #[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct OverlineNode(Content); #[node] impl OverlineNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { Ok(Self(args.expect("body")?).pack()) } } impl LayoutMath for OverlineNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout(ctx, &self.0, &None, '\u{332}', LINE_GAP, true) } } /// # 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, } #[node] impl UnderbraceNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { 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<()> { layout(ctx, &self.body, &self.annotation, '⏟', BRACE_GAP, false) } } /// # 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, } #[node] impl OverbraceNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { 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<()> { layout(ctx, &self.body, &self.annotation, '⏞', BRACE_GAP, true) } } /// # Underbracket /// A horizontal bracket under content, with an optional annotation below. /// /// ## Example /// ``` /// $ underbracket(1 + 2 + ... + 5, "numbers") $ /// ``` /// /// ## Parameters /// - body: Content (positional, required) /// The content above the bracket. /// /// - annotation: Content (positional) /// The optional content below the bracket. /// /// ## Category /// math #[func] #[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct UnderbracketNode { /// The content above the bracket. pub body: Content, /// The optional content below the bracket. pub annotation: Option, } #[node] impl UnderbracketNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let body = args.expect("body")?; let annotation = args.eat()?; Ok(Self { body, annotation }.pack()) } } impl LayoutMath for UnderbracketNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout(ctx, &self.body, &self.annotation, '⎵', BRACKET_GAP, false) } } /// # Overbracket /// A horizontal bracket over content, with an optional annotation above. /// /// ## Example /// ``` /// $ overbracket(1 + 2 + ... + 5, "numbers") $ /// ``` /// /// ## Parameters /// - body: Content (positional, required) /// The content below the bracket. /// /// - annotation: Content (positional) /// The optional content above the bracket. /// /// ## Category /// math #[func] #[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct OverbracketNode { /// The content below the bracket. pub body: Content, /// The optional content above the bracket. pub annotation: Option, } #[node] impl OverbracketNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let body = args.expect("body")?; let annotation = args.eat()?; Ok(Self { body, annotation }.pack()) } } impl LayoutMath for OverbracketNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { layout(ctx, &self.body, &self.annotation, '⎴', BRACKET_GAP, true) } } /// Layout an over- or underthing. fn layout( ctx: &mut MathContext, body: &Content, annotation: &Option, c: char, gap: Em, reverse: bool, ) -> SourceResult<()> { let gap = gap.scaled(ctx); let body = ctx.layout_row(body)?; let glyph = GlyphFragment::new(ctx, c); let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); let mut rows = vec![body, stretched.into()]; ctx.style(if reverse { ctx.style.for_subscript() } else { ctx.style.for_superscript() }); rows.extend( annotation .as_ref() .map(|annotation| ctx.layout_row(annotation)) .transpose()?, ); ctx.unstyle(); let mut baseline = 0; if reverse { rows.reverse(); baseline = rows.len() - 1; } ctx.push(stack(ctx, rows, Align::Center, gap, baseline)); Ok(()) } /// Stack rows on top of each other. /// /// Add a `gap` between each row and uses the baseline of the `baseline`th /// row for the whole frame. pub(super) fn stack( ctx: &MathContext, rows: Vec, align: Align, gap: Abs, baseline: usize, ) -> Frame { let mut width = Abs::zero(); let mut height = rows.len().saturating_sub(1) as f64 * gap; let points = alignments(&rows); let rows: Vec<_> = rows .into_iter() .map(|row| row.to_aligned_frame(ctx, &points, align)) .collect(); for row in &rows { height += row.height(); width.set_max(row.width()); } let mut y = Abs::zero(); let mut frame = Frame::new(Size::new(width, height)); for (i, row) in rows.into_iter().enumerate() { let x = align.position(width - row.width()); let pos = Point::new(x, y); if i == baseline { frame.set_baseline(y + row.baseline()); } y += row.height() + gap; frame.push_frame(pos, row); } frame }