Add delim-size parameter to mat, vec, and cases

Takes either a function or a relative length, just like with `lr`,
`stretch`, and `accent` which was changed in the previous two commits.
The short fall was changed in the first commit, so no test updates here.
The default is now much clearer to the user: `x => x * 1.1 - 0.1em`.
This commit is contained in:
mkorje 2025-04-27 21:04:39 +10:00
parent 92d06fbd9a
commit b30a38046c
No known key found for this signature in database
11 changed files with 98 additions and 27 deletions

View File

@ -1,14 +1,13 @@
use typst_library::diag::SourceResult; use typst_library::diag::SourceResult;
use typst_library::foundations::{Content, Packed, Resolve, StyleChain, SymbolElem}; use typst_library::foundations::{Content, Packed, Resolve, StyleChain, SymbolElem};
use typst_library::layout::{Em, Frame, FrameItem, Point, Size}; use typst_library::layout::{Em, Frame, FrameItem, Point, Size};
use typst_library::math::{BinomElem, FracElem}; use typst_library::math::{BinomElem, FracElem, DELIM_SHORT_FALL};
use typst_library::text::TextElem; use typst_library::text::TextElem;
use typst_library::visualize::{FixedStroke, Geometry}; use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span; use typst_syntax::Span;
use super::{ use super::{
style_for_denominator, style_for_numerator, FrameFragment, GlyphFragment, style_for_denominator, style_for_numerator, FrameFragment, GlyphFragment, MathContext,
MathContext, DELIM_SHORT_FALL,
}; };
const FRAC_AROUND: Em = Em::new(0.1); const FRAC_AROUND: Em = Em::new(0.1);
@ -49,7 +48,7 @@ fn layout_frac_like(
binom: bool, binom: bool,
span: Span, span: Span,
) -> SourceResult<()> { ) -> SourceResult<()> {
let short_fall = DELIM_SHORT_FALL.resolve(styles); let short_fall = DELIM_SHORT_FALL.abs().resolve(styles);
let axis = scaled!(ctx, styles, axis_height); let axis = scaled!(ctx, styles, axis_height);
let thickness = scaled!(ctx, styles, fraction_rule_thickness); let thickness = scaled!(ctx, styles, fraction_rule_thickness);
let shift_up = scaled!( let shift_up = scaled!(

View File

@ -1,19 +1,20 @@
use typst_library::diag::{bail, warning, SourceResult}; use typst_library::diag::{bail, warning, SourceResult};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain}; use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::layout::{ use typst_library::layout::{
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Point, Ratio, Rel, Size, Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Point, Rel, Size,
};
use typst_library::math::{
Augment, AugmentOffsets, CasesElem, MatElem, StretchSize, VecElem,
}; };
use typst_library::math::{Augment, AugmentOffsets, CasesElem, MatElem, VecElem};
use typst_library::text::TextElem; use typst_library::text::TextElem;
use typst_library::visualize::{FillRule, FixedStroke, Geometry, LineCap, Shape}; use typst_library::visualize::{FillRule, FixedStroke, Geometry, LineCap, Shape};
use typst_syntax::Span; use typst_syntax::Span;
use super::{ use super::{
alignments, delimiter_alignment, style_for_denominator, AlignmentResult, alignments, delimiter_alignment, style_for_denominator, AlignmentResult,
FrameFragment, GlyphFragment, LeftRightAlternator, MathContext, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, LeftRightAlternator, MathContext,
}; };
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
const DEFAULT_STROKE_THICKNESS: Em = Em::new(0.05); const DEFAULT_STROKE_THICKNESS: Em = Em::new(0.05);
/// Lays out a [`VecElem`]. /// Lays out a [`VecElem`].
@ -39,7 +40,15 @@ pub fn layout_vec(
)?; )?;
let delim = elem.delim(styles); let delim = elem.delim(styles);
layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), span) layout_delimiters(
ctx,
styles,
frame,
elem.delim_size(styles),
delim.open(),
delim.close(),
span,
)
} }
/// Lays out a [`CasesElem`]. /// Lays out a [`CasesElem`].
@ -67,7 +76,7 @@ pub fn layout_cases(
let delim = elem.delim(styles); let delim = elem.delim(styles);
let (open, close) = let (open, close) =
if elem.reverse(styles) { (None, delim.close()) } else { (delim.open(), None) }; if elem.reverse(styles) { (None, delim.close()) } else { (delim.open(), None) };
layout_delimiters(ctx, styles, frame, open, close, span) layout_delimiters(ctx, styles, frame, elem.delim_size(styles), open, close, span)
} }
/// Lays out a [`MatElem`]. /// Lays out a [`MatElem`].
@ -125,7 +134,15 @@ pub fn layout_mat(
)?; )?;
let delim = elem.delim(styles); let delim = elem.delim(styles);
layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), span) layout_delimiters(
ctx,
styles,
frame,
elem.delim_size(styles),
delim.open(),
delim.close(),
span,
)
} }
/// Layout the inner contents of a matrix, vector, or cases. /// Layout the inner contents of a matrix, vector, or cases.
@ -302,19 +319,23 @@ fn layout_delimiters(
ctx: &mut MathContext, ctx: &mut MathContext,
styles: StyleChain, styles: StyleChain,
mut frame: Frame, mut frame: Frame,
size: StretchSize,
left: Option<char>, left: Option<char>,
right: Option<char>, right: Option<char>,
span: Span, span: Span,
) -> SourceResult<()> { ) -> SourceResult<()> {
let short_fall = DELIM_SHORT_FALL.resolve(styles);
let axis = scaled!(ctx, styles, axis_height); let axis = scaled!(ctx, styles, axis_height);
let height = frame.height(); let height = frame.height();
let target = height + VERTICAL_PADDING.of(height);
frame.set_baseline(height / 2.0 + axis); frame.set_baseline(height / 2.0 + axis);
let target = size.resolve(ctx.engine, styles, height)?;
if let Some(left) = left { if let Some(left) = left {
let mut left = GlyphFragment::new(ctx, styles, left, span) let mut left = GlyphFragment::new(ctx, styles, left, span).stretch_vertical(
.stretch_vertical(ctx, target, short_fall); ctx,
target,
Abs::zero(),
);
left.align_on_axis(ctx, delimiter_alignment(left.c)); left.align_on_axis(ctx, delimiter_alignment(left.c));
ctx.push(left); ctx.push(left);
} }
@ -322,8 +343,11 @@ fn layout_delimiters(
ctx.push(FrameFragment::new(styles, frame)); ctx.push(FrameFragment::new(styles, frame));
if let Some(right) = right { if let Some(right) = right {
let mut right = GlyphFragment::new(ctx, styles, right, span) let mut right = GlyphFragment::new(ctx, styles, right, span).stretch_vertical(
.stretch_vertical(ctx, target, short_fall); ctx,
target,
Abs::zero(),
);
right.align_on_axis(ctx, delimiter_alignment(right.c)); right.align_on_axis(ctx, delimiter_alignment(right.c));
ctx.push(right); ctx.push(right);
} }

View File

@ -1,6 +1,6 @@
use ttf_parser::math::MathValue; use ttf_parser::math::MathValue;
use typst_library::foundations::{Style, StyleChain}; use typst_library::foundations::{Style, StyleChain};
use typst_library::layout::{Abs, Em, FixedAlignment, Frame, Point, Size, VAlignment}; use typst_library::layout::{Abs, FixedAlignment, Frame, Point, Size, VAlignment};
use typst_library::math::{EquationElem, MathSize}; use typst_library::math::{EquationElem, MathSize};
use typst_utils::LazyHash; use typst_utils::LazyHash;
@ -28,9 +28,6 @@ macro_rules! percent {
}; };
} }
/// How much less high scaled delimiters can be than what they wrap.
pub const DELIM_SHORT_FALL: Em = Em::new(0.1);
/// Converts some unit to an absolute length with the current font & font size. /// Converts some unit to an absolute length with the current font & font size.
pub trait Scaled { pub trait Scaled {
fn scaled(self, ctx: &MathContext, font_size: Abs) -> Abs; fn scaled(self, ctx: &MathContext, font_size: Abs) -> Abs;

View File

@ -2,7 +2,7 @@ use crate::foundations::{elem, func, Content, NativeElement, NativeFunc, SymbolE
use crate::layout::{Em, Length, Ratio, Rel}; use crate::layout::{Em, Length, Ratio, Rel};
use crate::math::{Mathy, StretchSize}; use crate::math::{Mathy, StretchSize};
const DELIM_SHORT_FALL: Em = Em::new(-0.1); pub const DELIM_SHORT_FALL: Em = Em::new(-0.1);
#[func(name = "x => x - 0.1em")] #[func(name = "x => x - 0.1em")]
pub const fn default_lr_size(base: Length) -> Rel { pub const fn default_lr_size(base: Length) -> Rel {

View File

@ -5,15 +5,27 @@ use unicode_math_class::MathClass;
use crate::diag::{bail, At, HintedStrResult, StrResult}; use crate::diag::{bail, At, HintedStrResult, StrResult};
use crate::foundations::{ use crate::foundations::{
array, cast, dict, elem, Array, Content, Dict, Fold, NoneValue, Resolve, Smart, array, cast, dict, elem, func, Array, Content, Dict, Fold, NativeFunc, NoneValue,
StyleChain, Symbol, Value, Resolve, Smart, StyleChain, Symbol, Value,
}; };
use crate::layout::{Abs, Em, HAlignment, Length, Rel}; use crate::layout::{Abs, Em, HAlignment, Length, Ratio, Rel};
use crate::math::Mathy; use crate::math::{Mathy, StretchSize, DELIM_SHORT_FALL};
use crate::visualize::Stroke; use crate::visualize::Stroke;
const DEFAULT_ROW_GAP: Em = Em::new(0.2); const DEFAULT_ROW_GAP: Em = Em::new(0.2);
const DEFAULT_COL_GAP: Em = Em::new(0.5); const DEFAULT_COL_GAP: Em = Em::new(0.5);
const VERTICAL_PADDING: Ratio = Ratio::new(1.1);
#[func(name = "x => x * 1.1 - 0.1em")]
const fn default_mat_size(base: Length) -> Rel {
Rel {
rel: Ratio::zero(),
abs: Length {
abs: Abs::raw(base.abs.to_raw() * VERTICAL_PADDING.get()),
em: DELIM_SHORT_FALL,
},
}
}
/// A column vector. /// A column vector.
/// ///
@ -40,6 +52,13 @@ pub struct VecElem {
#[default(DelimiterPair::PAREN)] #[default(DelimiterPair::PAREN)]
pub delim: DelimiterPair, pub delim: DelimiterPair,
/// The size of the delimiters, relative to the elements' total height.
///
/// See the [stretch documentation]($math.stretch.size) for more
/// information on sizes.
#[default(<default_mat_size>::data().into())]
pub delim_size: StretchSize,
/// The horizontal alignment that each element should have. /// The horizontal alignment that each element should have.
/// ///
/// ```example /// ```example
@ -101,6 +120,13 @@ pub struct MatElem {
#[default(DelimiterPair::PAREN)] #[default(DelimiterPair::PAREN)]
pub delim: DelimiterPair, pub delim: DelimiterPair,
/// The size of the delimiters, relative to the cells' total height.
///
/// See the [stretch documentation]($math.stretch.size) for more
/// information on sizes.
#[default(<default_mat_size>::data().into())]
pub delim_size: StretchSize,
/// The horizontal alignment that each cell should have. /// The horizontal alignment that each cell should have.
/// ///
/// ```example /// ```example
@ -244,6 +270,13 @@ pub struct CasesElem {
#[default(DelimiterPair::BRACE)] #[default(DelimiterPair::BRACE)]
pub delim: DelimiterPair, pub delim: DelimiterPair,
/// The size of the delimiters, relative to the branches' total height.
///
/// See the [stretch documentation]($math.stretch.size) for more
/// information on sizes.
#[default(<default_mat_size>::data().into())]
pub delim_size: StretchSize,
/// Whether the direction of cases should be reversed. /// Whether the direction of cases should be reversed.
/// ///
/// ```example /// ```example

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -16,6 +16,12 @@ $ x = cases(1, 2) $
#set math.cases(delim: sym.angle.l) #set math.cases(delim: sym.angle.l)
$ cases(a, b, c) $ $ cases(a, b, c) $
--- math-cases-delim-size ---
// Test setting delimiter size.
$ cases(reverse: #true, 1, 2, 3) cases(delim-size: #100%, 1, 2, 3) $
#set math.cases(delim-size: x => calc.max(x - 5pt, x * 0.901))
$ cases(1, 2) cases(1, 2, 3, 4) $
--- math-cases-linebreaks --- --- math-cases-linebreaks ---
// Warning: 40-49 linebreaks are ignored in branches // Warning: 40-49 linebreaks are ignored in branches
// Hint: 40-49 use commas instead to separate each line // Hint: 40-49 use commas instead to separate each line

View File

@ -54,6 +54,12 @@ $ a + mat(delim: #none, 1, 2; 3, 4) + b $
$ mat(1, 2; 3, 4; delim: "[") $, $ mat(1, 2; 3, 4; delim: "[") $,
) )
--- math-mat-delim-size ---
// Test setting delimiter size.
$ mat(c; c; c) mat(delim-size: #100%, c; c; c) $
#set math.mat(delim-size: x => calc.max(x - 5pt, x * 0.901))
$ mat(delim: "[", f; f; f; f) mat(delim: ||, x; x; x; x) $
--- math-mat-spread --- --- math-mat-spread ---
// Test argument spreading in matrix. // Test argument spreading in matrix.
$ mat(..#range(1, 5).chunks(2)) $ mat(..#range(1, 5).chunks(2))
@ -263,7 +269,7 @@ $ mat(a; b; c) mat(a \ b \ c) $
--- math-mat-vec-cases-unity --- --- math-mat-vec-cases-unity ---
// Test that matrices, vectors, and cases are all laid out the same. // Test that matrices, vectors, and cases are all laid out the same.
$ mat(z_(n_p); a^2) $ mat(z_(n_p); a^2)
vec(z_(n_p), a^2) vec(z_(n_p), a^2)
cases(reverse: #true, delim: \(, z_(n_p), a^2) cases(reverse: #true, delim: \(, z_(n_p), a^2)
cases(delim: \(, z_(n_p), a^2) $ cases(delim: \(, z_(n_p), a^2) $

View File

@ -50,6 +50,12 @@ $ vec(1, 2) $
// Error: 22-33 invalid delimiter: "%" // Error: 22-33 invalid delimiter: "%"
#set math.vec(delim: (none, "%")) #set math.vec(delim: (none, "%"))
--- math-vec-delim-size ---
// Test setting delimiter size.
$ vec(1, 2, 3) vec(delim-size: #100%, 1, 2, 3) $
#set math.vec(delim-size: x => calc.max(x - 5pt, x * 0.901))
$ vec(delim: "{", 1, 2, 3) vec(delim: "[", 1, 2, 3) $
--- math-vec-linebreaks --- --- math-vec-linebreaks ---
// Warning: 20-29 linebreaks are ignored in elements // Warning: 20-29 linebreaks are ignored in elements
// Hint: 20-29 use commas instead to separate each line // Hint: 20-29 use commas instead to separate each line