summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-28 23:13:11 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-28 23:15:03 +0100
commit76048a8ef45ac5892235f2e69cb7cb6c35a037e4 (patch)
treeb882d3d209ae50f8849a94b38e8945e0b8330f03 /library
parent406de22ee5cd74dc6f67743bad4710415bb50c41 (diff)
Overline, Underline, Overbracket, Underbracket
Diffstat (limited to 'library')
-rw-r--r--library/src/math/braced.rs123
-rw-r--r--library/src/math/matrix.rs47
-rw-r--r--library/src/math/mod.rs8
-rw-r--r--library/src/math/stack.rs315
-rw-r--r--library/src/text/symbols.rs2
5 files changed, 324 insertions, 171 deletions
diff --git a/library/src/math/braced.rs b/library/src/math/braced.rs
deleted file mode 100644
index e207cb92..00000000
--- a/library/src/math/braced.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-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(())
- }
-}
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index 27e1b282..45ebdda7 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -172,61 +172,20 @@ fn layout(
let mut frame = stack(ctx, rows, align, gap, 0);
let height = frame.height();
+ let target = height + VERTICAL_PADDING.of(height);
frame.set_baseline(frame.height() / 2.0 + axis);
if let Some(left) = left {
- ctx.push(GlyphFragment::new(ctx, left).stretch_vertical(ctx, height, short_fall));
+ ctx.push(GlyphFragment::new(ctx, left).stretch_vertical(ctx, target, short_fall));
}
ctx.push(frame);
if let Some(right) = right {
ctx.push(
- GlyphFragment::new(ctx, right).stretch_vertical(ctx, height, short_fall),
+ GlyphFragment::new(ctx, right).stretch_vertical(ctx, target, short_fall),
);
}
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<MathRow>,
- 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_line_frame(ctx, &points)).collect();
-
- for row in &rows {
- height += row.height();
- width.set_max(row.width());
- }
-
- let extra = VERTICAL_PADDING.of(height);
- height += extra;
-
- let mut y = extra / 2.0;
- 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
-}
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 4c002cea..2d914c31 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -5,7 +5,6 @@ mod ctx;
mod accent;
mod align;
mod attach;
-mod braced;
mod frac;
mod fragment;
mod lr;
@@ -14,6 +13,7 @@ mod op;
mod root;
mod row;
mod spacing;
+mod stack;
mod stretch;
mod style;
mod symbols;
@@ -21,12 +21,12 @@ mod symbols;
pub use self::accent::*;
pub use self::align::*;
pub use self::attach::*;
-pub use self::braced::*;
pub use self::frac::*;
pub use self::lr::*;
pub use self::matrix::*;
pub use self::op::*;
pub use self::root::*;
+pub use self::stack::*;
pub use self::style::*;
use ttf_parser::GlyphId;
@@ -65,8 +65,12 @@ pub fn module(sym: &Module) -> Module {
math.def_func::<ScriptsNode>("scripts");
math.def_func::<LimitsNode>("limits");
math.def_func::<AccentNode>("accent");
+ math.def_func::<UnderlineNode>("underline");
+ math.def_func::<OverlineNode>("overline");
math.def_func::<UnderbraceNode>("underbrace");
math.def_func::<OverbraceNode>("overbrace");
+ math.def_func::<UnderbracketNode>("underbracket");
+ math.def_func::<OverbracketNode>("overbracket");
// Fractions and matrix-likes.
math.def_func::<FracNode>("frac");
diff --git a/library/src/math/stack.rs b/library/src/math/stack.rs
new file mode 100644
index 00000000..c8a1252c
--- /dev/null
+++ b/library/src/math/stack.rs
@@ -0,0 +1,315 @@
+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<Content> {
+ 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<Content> {
+ 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<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<()> {
+ 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<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<()> {
+ 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<Content>,
+}
+
+#[node]
+impl UnderbracketNode {
+ 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 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<Content>,
+}
+
+#[node]
+impl OverbracketNode {
+ 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 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<Content>,
+ 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<MathRow>,
+ 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_line_frame(ctx, &points)).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
+}
diff --git a/library/src/text/symbols.rs b/library/src/text/symbols.rs
index 36e49849..8bc6222a 100644
--- a/library/src/text/symbols.rs
+++ b/library/src/text/symbols.rs
@@ -150,13 +150,11 @@ symbols! {
breve: '˘',
caret: '‸',
caron: 'ˇ',
- cedilla: '¸',
circum: '^',
diaer: '¨',
grave: '`',
macron: '¯',
tilde: '~',
- overline: '‾',
// Currency.
bitcoin: '₿',