Add missing under/over-delimiters (#4637)

This commit is contained in:
mkorje 2024-08-05 11:28:17 +00:00 committed by GitHub
parent 211b546e4e
commit 810491c9d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 191 additions and 39 deletions

View File

@ -176,6 +176,10 @@ pub fn module() -> Module {
math.define_elem::<OverbraceElem>(); math.define_elem::<OverbraceElem>();
math.define_elem::<UnderbracketElem>(); math.define_elem::<UnderbracketElem>();
math.define_elem::<OverbracketElem>(); math.define_elem::<OverbracketElem>();
math.define_elem::<UnderparenElem>();
math.define_elem::<OverparenElem>();
math.define_elem::<UndershellElem>();
math.define_elem::<OvershellElem>();
math.define_elem::<CancelElem>(); math.define_elem::<CancelElem>();
math.define_elem::<FracElem>(); math.define_elem::<FracElem>();
math.define_elem::<BinomElem>(); math.define_elem::<BinomElem>();

View File

@ -2,9 +2,9 @@ use crate::diag::SourceResult;
use crate::foundations::{elem, Content, Packed, StyleChain}; use crate::foundations::{elem, Content, Packed, StyleChain};
use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size}; use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use crate::math::{ use crate::math::{
alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult, alignments, scaled_font_size, style_cramped, style_for_subscript,
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, MathRun, style_for_superscript, AlignmentResult, FrameFragment, GlyphFragment, LayoutMath,
Scaled, LeftRightAlternator, MathContext, MathRun, Scaled,
}; };
use crate::syntax::Span; use crate::syntax::Span;
use crate::text::TextElem; use crate::text::TextElem;
@ -12,11 +12,13 @@ use crate::visualize::{FixedStroke, Geometry};
const BRACE_GAP: Em = Em::new(0.25); const BRACE_GAP: Em = Em::new(0.25);
const BRACKET_GAP: Em = Em::new(0.25); const BRACKET_GAP: Em = Em::new(0.25);
const PAREN_GAP: Em = Em::new(0.25);
const SHELL_GAP: Em = Em::new(0.25);
/// A marker to distinguish under- vs. overlines. /// A marker to distinguish under- vs. overlines.
enum LineKind { enum Position {
Over,
Under, Under,
Over,
} }
/// A horizontal line under content. /// A horizontal line under content.
@ -34,7 +36,7 @@ pub struct UnderlineElem {
impl LayoutMath for Packed<UnderlineElem> { impl LayoutMath for Packed<UnderlineElem> {
#[typst_macros::time(name = "math.underline", span = self.span())] #[typst_macros::time(name = "math.underline", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> { fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverline(ctx, styles, self.body(), self.span(), LineKind::Under) layout_underoverline(ctx, styles, self.body(), self.span(), Position::Under)
} }
} }
@ -53,7 +55,7 @@ pub struct OverlineElem {
impl LayoutMath for Packed<OverlineElem> { impl LayoutMath for Packed<OverlineElem> {
#[typst_macros::time(name = "math.overline", span = self.span())] #[typst_macros::time(name = "math.overline", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> { fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverline(ctx, styles, self.body(), self.span(), LineKind::Over) layout_underoverline(ctx, styles, self.body(), self.span(), Position::Over)
} }
} }
@ -63,11 +65,11 @@ fn layout_underoverline(
styles: StyleChain, styles: StyleChain,
body: &Content, body: &Content,
span: Span, span: Span,
line: LineKind, position: Position,
) -> SourceResult<()> { ) -> SourceResult<()> {
let (extra_height, content, line_pos, content_pos, baseline, bar_height); let (extra_height, content, line_pos, content_pos, baseline, bar_height);
match line { match position {
LineKind::Under => { Position::Under => {
let sep = scaled!(ctx, styles, underbar_extra_descender); let sep = scaled!(ctx, styles, underbar_extra_descender);
bar_height = scaled!(ctx, styles, underbar_rule_thickness); bar_height = scaled!(ctx, styles, underbar_rule_thickness);
let gap = scaled!(ctx, styles, underbar_vertical_gap); let gap = scaled!(ctx, styles, underbar_vertical_gap);
@ -79,7 +81,7 @@ fn layout_underoverline(
content_pos = Point::zero(); content_pos = Point::zero();
baseline = content.ascent() baseline = content.ascent()
} }
LineKind::Over => { Position::Over => {
let sep = scaled!(ctx, styles, overbar_extra_ascender); let sep = scaled!(ctx, styles, overbar_extra_ascender);
bar_height = scaled!(ctx, styles, overbar_rule_thickness); bar_height = scaled!(ctx, styles, overbar_rule_thickness);
let gap = scaled!(ctx, styles, overbar_vertical_gap); let gap = scaled!(ctx, styles, overbar_vertical_gap);
@ -145,7 +147,7 @@ impl LayoutMath for Packed<UnderbraceElem> {
&self.annotation(styles), &self.annotation(styles),
'⏟', '⏟',
BRACE_GAP, BRACE_GAP,
false, Position::Under,
self.span(), self.span(),
) )
} }
@ -177,7 +179,7 @@ impl LayoutMath for Packed<OverbraceElem> {
&self.annotation(styles), &self.annotation(styles),
'⏞', '⏞',
BRACE_GAP, BRACE_GAP,
true, Position::Over,
self.span(), self.span(),
) )
} }
@ -200,7 +202,7 @@ pub struct UnderbracketElem {
} }
impl LayoutMath for Packed<UnderbracketElem> { impl LayoutMath for Packed<UnderbracketElem> {
#[typst_macros::time(name = "math.underbrace", span = self.span())] #[typst_macros::time(name = "math.underbracket", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> { fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader( layout_underoverspreader(
ctx, ctx,
@ -209,7 +211,7 @@ impl LayoutMath for Packed<UnderbracketElem> {
&self.annotation(styles), &self.annotation(styles),
'⎵', '⎵',
BRACKET_GAP, BRACKET_GAP,
false, Position::Under,
self.span(), self.span(),
) )
} }
@ -241,7 +243,135 @@ impl LayoutMath for Packed<OverbracketElem> {
&self.annotation(styles), &self.annotation(styles),
'⎴', '⎴',
BRACKET_GAP, BRACKET_GAP,
true, Position::Over,
self.span(),
)
}
}
/// A horizontal parenthesis under content, with an optional annotation below.
///
/// ```example
/// $ underparen(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct UnderparenElem {
/// The content above the parenthesis.
#[required]
pub body: Content,
/// The optional content below the parenthesis.
#[positional]
pub annotation: Option<Content>,
}
impl LayoutMath for Packed<UnderparenElem> {
#[typst_macros::time(name = "math.underparen", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader(
ctx,
styles,
self.body(),
&self.annotation(styles),
'⏝',
PAREN_GAP,
Position::Under,
self.span(),
)
}
}
/// A horizontal parenthesis over content, with an optional annotation above.
///
/// ```example
/// $ overparen(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct OverparenElem {
/// The content below the parenthesis.
#[required]
pub body: Content,
/// The optional content above the parenthesis.
#[positional]
pub annotation: Option<Content>,
}
impl LayoutMath for Packed<OverparenElem> {
#[typst_macros::time(name = "math.overparen", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader(
ctx,
styles,
self.body(),
&self.annotation(styles),
'⏜',
PAREN_GAP,
Position::Over,
self.span(),
)
}
}
/// A horizontal tortoise shell bracket under content, with an optional annotation below.
///
/// ```example
/// $ undershell(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct UndershellElem {
/// The content above the tortoise shell bracket.
#[required]
pub body: Content,
/// The optional content below the tortoise shell bracket.
#[positional]
pub annotation: Option<Content>,
}
impl LayoutMath for Packed<UndershellElem> {
#[typst_macros::time(name = "math.undershell", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader(
ctx,
styles,
self.body(),
&self.annotation(styles),
'⏡',
SHELL_GAP,
Position::Under,
self.span(),
)
}
}
/// A horizontal tortoise shell bracket over content, with an optional annotation above.
///
/// ```example
/// $ overshell(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct OvershellElem {
/// The content below the tortoise shell bracket.
#[required]
pub body: Content,
/// The optional content above the tortoise shell bracket.
#[positional]
pub annotation: Option<Content>,
}
impl LayoutMath for Packed<OvershellElem> {
#[typst_macros::time(name = "math.overshell", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader(
ctx,
styles,
self.body(),
&self.annotation(styles),
'⏠',
SHELL_GAP,
Position::Over,
self.span(), self.span(),
) )
} }
@ -256,7 +386,7 @@ fn layout_underoverspreader(
annotation: &Option<Content>, annotation: &Option<Content>,
c: char, c: char,
gap: Em, gap: Em,
reverse: bool, position: Position,
span: Span, span: Span,
) -> SourceResult<()> { ) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles); let font_size = scaled_font_size(ctx, styles);
@ -267,30 +397,30 @@ fn layout_underoverspreader(
let glyph = GlyphFragment::new(ctx, styles, c, span); let glyph = GlyphFragment::new(ctx, styles, c, span);
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero()); let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());
let mut rows = vec![MathRun::new(vec![body]), stretched.into()]; let mut rows = vec![];
let baseline = match position {
let (sup_style, sub_style); Position::Under => {
let row_styles = if reverse { rows.push(MathRun::new(vec![body]));
sup_style = style_for_subscript(styles); rows.push(stretched.into());
styles.chain(&sup_style) if let Some(annotation) = annotation {
} else { let under_style = style_for_subscript(styles);
sub_style = style_for_subscript(styles); let annotation_styles = styles.chain(&under_style);
styles.chain(&sub_style) rows.push(ctx.layout_into_run(annotation, annotation_styles)?);
}
0
}
Position::Over => {
if let Some(annotation) = annotation {
let over_style = style_for_superscript(styles);
let annotation_styles = styles.chain(&over_style);
rows.push(ctx.layout_into_run(annotation, annotation_styles)?);
}
rows.push(stretched.into());
rows.push(MathRun::new(vec![body]));
rows.len() - 1
}
}; };
rows.extend(
annotation
.as_ref()
.map(|annotation| ctx.layout_into_run(annotation, row_styles))
.transpose()?,
);
let mut baseline = 0;
if reverse {
rows.reverse();
baseline = rows.len() - 1;
}
let frame = stack( let frame = stack(
rows, rows,
FixedAlignment::Center, FixedAlignment::Center,

View File

@ -46,6 +46,10 @@
"overbrace", "overbrace",
"underbracket", "underbracket",
"overbracket", "overbracket",
"underparen",
"overparen",
"undershell",
"overshell",
] ]
details: | details: |
Delimiters above or below parts of an equation. Delimiters above or below parts of an equation.

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

View File

@ -19,3 +19,17 @@ $ x = overbracket(
$ underbracket([1, 2/3], "relevant stuff") $ underbracket([1, 2/3], "relevant stuff")
arrow.l.r.double.long arrow.l.r.double.long
overbracket([4/5,6], "irrelevant stuff") $ overbracket([4/5,6], "irrelevant stuff") $
--- math-underover-parens ---
// Test parentheses.
$ overparen(
underparen(x + y, "long comment"),
1 + 2 + ... + 5
) $
--- math-underover-shells ---
// Test tortoise shell brackets.
$ undershell(
1 + overshell(2 + ..., x + y),
"all stuff"
) $