Skip to content

Commit

Permalink
I decided to use the word shell instead of turtle or tortoise becau…
Browse files Browse the repository at this point in the history
…se I

think it is a bit clearer, the word reads nicer (“overshell” vs “overturtle”),
and there is no associated discussion (they are called “tortoise shells” in
Unicode):

> tortoises and turtles are not the same thing!
~ @Enivex

There will be a sibling PR to rename the symbols too.

Additionally, I refactored some code to make it more readable.
I also fixed a bug introduced in #3262 where the wrong script style was being used.

Closes #3856.
  • Loading branch information
frozolotl committed Apr 4, 2024
1 parent ffc9570 commit 6a8f1d2
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 33 deletions.
4 changes: 4 additions & 0 deletions crates/typst/src/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ pub fn module() -> Module {
math.define_elem::<OverlineElem>();
math.define_elem::<UnderbraceElem>();
math.define_elem::<OverbraceElem>();
math.define_elem::<UnderparenElem>();
math.define_elem::<OverparenElem>();
math.define_elem::<UndershellElem>();
math.define_elem::<OvershellElem>();
math.define_elem::<UnderbracketElem>();
math.define_elem::<OverbracketElem>();
math.define_elem::<CancelElem>();
Expand Down
202 changes: 169 additions & 33 deletions crates/typst/src/math/underover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use crate::diag::SourceResult;
use crate::foundations::{elem, Content, Packed, StyleChain};
use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use crate::math::{
alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult,
FrameFragment, GlyphFragment, LayoutMath, MathContext, MathRun, Scaled,
alignments, scaled_font_size, style_cramped, AlignmentResult, FrameFragment,
GlyphFragment, LayoutMath, MathContext, MathRun, Scaled,
};
use crate::syntax::Span;
use crate::text::TextElem;
use crate::visualize::{FixedStroke, Geometry};

const BRACE_GAP: Em = Em::new(0.25);
const BRACKET_GAP: Em = Em::new(0.25);
use super::{style_for_subscript, style_for_superscript};

const GAP: Em = Em::new(0.25);

/// A marker to distinguish under- vs. overlines.
enum LineKind {
Expand Down Expand Up @@ -143,8 +144,8 @@ impl LayoutMath for Packed<UnderbraceElem> {
self.body(),
&self.annotation(styles),
'⏟',
BRACE_GAP,
false,
GAP,
Position::Below,
self.span(),
)
}
Expand Down Expand Up @@ -175,8 +176,8 @@ impl LayoutMath for Packed<OverbraceElem> {
self.body(),
&self.annotation(styles),
'⏞',
BRACE_GAP,
true,
GAP,
Position::Above,
self.span(),
)
}
Expand Down Expand Up @@ -207,8 +208,8 @@ impl LayoutMath for Packed<UnderbracketElem> {
self.body(),
&self.annotation(styles),
'⎵',
BRACKET_GAP,
false,
GAP,
Position::Below,
self.span(),
)
}
Expand Down Expand Up @@ -239,13 +240,148 @@ impl LayoutMath for Packed<OverbracketElem> {
self.body(),
&self.annotation(styles),
'⎴',
BRACKET_GAP,
true,
GAP,
Position::Above,
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 paren.
#[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),
'⏝',
GAP,
Position::Below,
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 paren.
#[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),
'⏜',
GAP,
Position::Above,
self.span(),
)
}
}

/// A horizontal tortoise shell under content, with an optional annotation below.
///
/// ```example
/// $ undershell(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct UndershellElem {
/// The content above the shell.
#[required]
pub body: Content,

/// The optional content below the shell.
#[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),
'⏡',
GAP,
Position::Below,
self.span(),
)
}
}

/// A horizontal tortoise shell over content, with an optional annotation above.
///
/// ```example
/// $ overshell(1 + 2 + ... + 5, "numbers") $
/// ```
#[elem(LayoutMath)]
pub struct OvershellElem {
/// The content below the shell.
#[required]
pub body: Content,

/// The optional content above the shell.
#[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),
'⏠',
GAP,
Position::Above,
self.span(),
)
}
}

/// Where the annotation is placed at.
#[derive(Clone, Copy, PartialEq, Eq)]
enum Position {
Above,
Below,
}

/// Layout an over- or underbrace-like object.
#[allow(clippy::too_many_arguments)]
fn layout_underoverspreader(
Expand All @@ -255,7 +391,7 @@ fn layout_underoverspreader(
annotation: &Option<Content>,
c: char,
gap: Em,
reverse: bool,
position: Position,
span: Span,
) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles);
Expand All @@ -267,29 +403,29 @@ fn layout_underoverspreader(
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());

let mut rows = vec![MathRun::new(vec![body]), stretched.into()];
if let Some(annotation) = annotation {
let (above_style, below_style);
let annot_styles = match position {
Position::Above => {
above_style = style_for_subscript(styles);
styles.chain(&above_style)
}
Position::Below => {
below_style = style_for_superscript(styles);
styles.chain(&below_style)
}
};
rows.push(ctx.layout_into_run(annotation, annot_styles)?);
}

let (sup_style, sub_style);
let row_styles = if reverse {
sup_style = style_for_subscript(styles);
styles.chain(&sup_style)
} else {
sub_style = style_for_subscript(styles);
styles.chain(&sub_style)
let baseline = match position {
Position::Above => {
rows.reverse();
rows.len() - 1
}
Position::Below => 0,
};

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(rows, FixedAlignment::Center, gap, baseline);
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class));

Expand Down
4 changes: 4 additions & 0 deletions docs/reference/groups.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
"overbrace",
"underbracket",
"overbracket",
"underparen",
"overparen",
"undershell",
"overshell",
]
details: |
Delimiters above or below parts of an equation.
Expand Down
Binary file modified tests/ref/math/underover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions tests/typ/math/underover.typ
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ $ x = underbrace(
underbrace("numbers", x + y)
) $

---
// Test braces.
$ x = overbrace(1 + 2) $

---
// Test lines and brackets.
$ x = overbracket(
Expand All @@ -19,3 +23,15 @@ $ x = overbracket(
$ underbracket([1, 2/3], "relevant stuff")
arrow.l.r.double.long
overbracket([4/5,6], "irrelevant stuff") $

---
// Test parentheses.
$ underparen([1, 2/3], "relevant stuff")
arrow.l.r.double.long
overparen([4/5,6], "irrelevant stuff") $

---
// Test shells.
$ undershell([1, 2/3], "relevant stuff")
arrow.l.r.double.long
overshell([4/5,6], "irrelevant stuff") $

0 comments on commit 6a8f1d2

Please sign in to comment.