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 000000000..3d04e696d Binary files /dev/null and b/tests/ref/math/matrix-gaps.png differ 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) $