From 3dcbe859fb6c0db3672feddf4c2b5043d5c24e0e Mon Sep 17 00:00:00 2001 From: SekoiaTree <51149447+SekoiaTree@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:26:47 +0200 Subject: [PATCH] Add gaps for matrix, case, and vector (#2186) --- crates/typst-library/src/math/matrix.rs | 106 +++++++++++++++++++----- tests/ref/math/matrix-gaps.png | Bin 0 -> 3021 bytes tests/typ/math/matrix-gaps.typ | 17 ++++ 3 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 tests/ref/math/matrix-gaps.png create mode 100644 tests/typ/math/matrix-gaps.typ diff --git a/crates/typst-library/src/math/matrix.rs b/crates/typst-library/src/math/matrix.rs index abb0da353..dd30209a5 100644 --- a/crates/typst-library/src/math/matrix.rs +++ b/crates/typst-library/src/math/matrix.rs @@ -2,8 +2,8 @@ use typst::model::Resolve; use super::*; -const ROW_GAP: Em = Em::new(0.5); -const COL_GAP: Em = Em::new(0.5); +const DEFAULT_ROW_GAP: Em = Em::new(0.5); +const DEFAULT_COL_GAP: Em = Em::new(0.5); const VERTICAL_PADDING: Ratio = Ratio::new(0.1); const DEFAULT_STROKE_THICKNESS: Em = Em::new(0.05); @@ -28,6 +28,16 @@ pub struct VecElem { #[default(Some(Delimiter::Paren))] pub delim: Option, + /// The gap between elements. + /// + /// ```example + /// #set math.vec(gap: 1em) + /// $ vec(1, 2) $ + /// ``` + #[resolve] + #[default(DEFAULT_ROW_GAP.into())] + pub gap: Rel, + /// The elements of the vector. #[variadic] pub children: Vec, @@ -37,7 +47,12 @@ impl LayoutMath for VecElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let delim = self.delim(ctx.styles()); - let frame = layout_vec_body(ctx, &self.children(), FixedAlign::Center)?; + let frame = layout_vec_body( + ctx, + &self.children(), + FixedAlign::Center, + self.gap(ctx.styles()), + )?; layout_delimiters( ctx, frame, @@ -110,6 +125,40 @@ pub struct MatElem { #[fold] pub augment: Option, + /// The gap between rows and columns. + /// + /// ```example + /// #set math.mat(gap: 1em) + /// $ mat(1, 2; 3, 4) $ + /// ``` + #[external] + pub gap: Rel, + + /// The gap between rows. Takes precedence over `gap`. + /// + /// ```example + /// #set math.mat(row-gap: 1em) + /// $ mat(1, 2; 3, 4) $ + /// ``` + #[resolve] + #[parse( + let gap = args.named("gap")?; + args.named("row-gap")?.or(gap) + )] + #[default(DEFAULT_ROW_GAP.into())] + pub row_gap: Rel, + + /// The gap between columns. Takes precedence over `gap`. + /// + /// ```example + /// #set math.mat(column-gap: 1em) + /// $ mat(1, 2; 3, 4) $ + /// ``` + #[resolve] + #[parse(args.named("column-gap")?.or(gap))] + #[default(DEFAULT_COL_GAP.into())] + pub column_gap: Rel, + /// An array of arrays with the rows of the matrix. /// /// ```example @@ -179,8 +228,13 @@ impl LayoutMath for MatElem { } let delim = self.delim(ctx.styles()); - - let frame = layout_mat_body(ctx, &self.rows(), augment, self.span())?; + let frame = layout_mat_body( + ctx, + &self.rows(), + augment, + Axes::new(self.column_gap(ctx.styles()), self.row_gap(ctx.styles())), + self.span(), + )?; layout_delimiters( ctx, @@ -216,6 +270,16 @@ pub struct CasesElem { #[default(Delimiter::Brace)] pub delim: Delimiter, + /// The gap between branches. + /// + /// ```example + /// #set math.cases(gap: 1em) + /// $ x = cases(1, 2) $ + /// ``` + #[resolve] + #[default(DEFAULT_ROW_GAP.into())] + pub gap: Rel, + /// The branches of the case distinction. #[variadic] pub children: Vec, @@ -225,7 +289,12 @@ impl LayoutMath for CasesElem { #[tracing::instrument(skip(ctx))] fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { let delim = self.delim(ctx.styles()); - let frame = layout_vec_body(ctx, &self.children(), FixedAlign::Start)?; + let frame = layout_vec_body( + ctx, + &self.children(), + FixedAlign::Start, + self.gap(ctx.styles()), + )?; layout_delimiters(ctx, frame, Some(delim.open()), None, self.span()) } } @@ -279,8 +348,9 @@ fn layout_vec_body( ctx: &mut MathContext, column: &[Content], align: FixedAlign, + row_gap: Rel, ) -> SourceResult { - let gap = ROW_GAP.scaled(ctx); + let gap = row_gap.relative_to(ctx.regions.base().y); ctx.style(ctx.style.for_denominator()); let mut flat = vec![]; for child in column { @@ -295,13 +365,11 @@ fn layout_mat_body( ctx: &mut MathContext, rows: &[Vec], augment: Option>, + gap: Axes>, span: Span, ) -> SourceResult { - let row_gap = ROW_GAP.scaled(ctx); - let col_gap = COL_GAP.scaled(ctx); - - let half_row_gap = row_gap * 0.5; - let half_col_gap = col_gap * 0.5; + let gap = gap.zip_map(ctx.regions.base(), Rel::relative_to); + let half_gap = gap * 0.5; // We provide a default stroke thickness that scales // with font size to ensure that augmentation lines @@ -359,7 +427,7 @@ fn layout_mat_body( // For each row, combine maximum ascent and descent into a row height. // Sum the row heights, then add the total height of the gaps between rows. let total_height = - heights.iter().map(|&(a, b)| a + b).sum::() + row_gap * (nrows - 1) as f64; + heights.iter().map(|&(a, b)| a + b).sum::() + gap.y * (nrows - 1) as f64; // Width starts at zero because it can't be calculated until later let mut frame = Frame::new(Size::new(Abs::zero(), total_height)); @@ -380,7 +448,7 @@ fn layout_mat_body( frame.push_frame(pos, cell); - y += ascent + descent + row_gap; + y += ascent + descent + gap.y; } // Advance to the end of the column @@ -389,23 +457,23 @@ fn layout_mat_body( // If a vertical line should be inserted after this column if vline.0.contains(&(index + 1)) { frame.push( - Point::with_x(x + half_col_gap), + Point::with_x(x + half_gap.x), line_item(total_height, true, stroke.clone(), span), ); } // Advance to the start of the next column - x += col_gap; + x += gap.x; } // Once all the columns are laid out, the total width can be calculated - let total_width = x - col_gap; + let total_width = x - gap.x; // This allows the horizontal lines to be laid out for line in hline.0 { let offset = (heights[0..line].iter().map(|&(a, b)| a + b).sum::() - + row_gap * (line - 1) as f64) - + half_row_gap; + + gap.y * (line - 1) as f64) + + half_gap.y; frame.push( Point::with_y(offset), diff --git a/tests/ref/math/matrix-gaps.png b/tests/ref/math/matrix-gaps.png new file mode 100644 index 0000000000000000000000000000000000000000..3d04e696dd4b0d97c64ae4e604e2917cfa9dca83 GIT binary patch literal 3021 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 XJ?y#1QLGS%_H>+si+w5Pc;deR`cXvI literal 0 HcmV?d00001 diff --git a/tests/typ/math/matrix-gaps.typ b/tests/typ/math/matrix-gaps.typ new file mode 100644 index 000000000..ef33b518e --- /dev/null +++ b/tests/typ/math/matrix-gaps.typ @@ -0,0 +1,17 @@ +// Test matrices, cases and vec gaps + +--- +#set math.mat(row-gap: 1em, column-gap: 2em) +$ mat(1, 2; 3, 4) $ + +--- +#set math.mat(gap: 1em) +$ mat(1, 2; 3, 4) $ + +--- +#set math.cases(gap: 1em) +$ x = cases(1, 2) $ + +--- +#set math.vec(gap: 1em) +$ vec(1, 2) $