mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Add gaps for matrix, case, and vector (#2186)
This commit is contained in:
parent
50f354e989
commit
3dcbe859fb
@ -2,8 +2,8 @@ use typst::model::Resolve;
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ROW_GAP: Em = Em::new(0.5);
|
const DEFAULT_ROW_GAP: Em = Em::new(0.5);
|
||||||
const COL_GAP: Em = Em::new(0.5);
|
const DEFAULT_COL_GAP: Em = Em::new(0.5);
|
||||||
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
|
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);
|
||||||
@ -28,6 +28,16 @@ pub struct VecElem {
|
|||||||
#[default(Some(Delimiter::Paren))]
|
#[default(Some(Delimiter::Paren))]
|
||||||
pub delim: Option<Delimiter>,
|
pub delim: Option<Delimiter>,
|
||||||
|
|
||||||
|
/// The gap between elements.
|
||||||
|
///
|
||||||
|
/// ```example
|
||||||
|
/// #set math.vec(gap: 1em)
|
||||||
|
/// $ vec(1, 2) $
|
||||||
|
/// ```
|
||||||
|
#[resolve]
|
||||||
|
#[default(DEFAULT_ROW_GAP.into())]
|
||||||
|
pub gap: Rel<Length>,
|
||||||
|
|
||||||
/// The elements of the vector.
|
/// The elements of the vector.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: Vec<Content>,
|
||||||
@ -37,7 +47,12 @@ impl LayoutMath for VecElem {
|
|||||||
#[tracing::instrument(skip(ctx))]
|
#[tracing::instrument(skip(ctx))]
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
let delim = self.delim(ctx.styles());
|
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(
|
layout_delimiters(
|
||||||
ctx,
|
ctx,
|
||||||
frame,
|
frame,
|
||||||
@ -110,6 +125,40 @@ pub struct MatElem {
|
|||||||
#[fold]
|
#[fold]
|
||||||
pub augment: Option<Augment>,
|
pub augment: Option<Augment>,
|
||||||
|
|
||||||
|
/// The gap between rows and columns.
|
||||||
|
///
|
||||||
|
/// ```example
|
||||||
|
/// #set math.mat(gap: 1em)
|
||||||
|
/// $ mat(1, 2; 3, 4) $
|
||||||
|
/// ```
|
||||||
|
#[external]
|
||||||
|
pub gap: Rel<Length>,
|
||||||
|
|
||||||
|
/// 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<Length>,
|
||||||
|
|
||||||
|
/// 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<Length>,
|
||||||
|
|
||||||
/// An array of arrays with the rows of the matrix.
|
/// An array of arrays with the rows of the matrix.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
@ -179,8 +228,13 @@ impl LayoutMath for MatElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let delim = self.delim(ctx.styles());
|
let delim = self.delim(ctx.styles());
|
||||||
|
let frame = layout_mat_body(
|
||||||
let frame = layout_mat_body(ctx, &self.rows(), augment, self.span())?;
|
ctx,
|
||||||
|
&self.rows(),
|
||||||
|
augment,
|
||||||
|
Axes::new(self.column_gap(ctx.styles()), self.row_gap(ctx.styles())),
|
||||||
|
self.span(),
|
||||||
|
)?;
|
||||||
|
|
||||||
layout_delimiters(
|
layout_delimiters(
|
||||||
ctx,
|
ctx,
|
||||||
@ -216,6 +270,16 @@ pub struct CasesElem {
|
|||||||
#[default(Delimiter::Brace)]
|
#[default(Delimiter::Brace)]
|
||||||
pub delim: Delimiter,
|
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<Length>,
|
||||||
|
|
||||||
/// The branches of the case distinction.
|
/// The branches of the case distinction.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: Vec<Content>,
|
||||||
@ -225,7 +289,12 @@ impl LayoutMath for CasesElem {
|
|||||||
#[tracing::instrument(skip(ctx))]
|
#[tracing::instrument(skip(ctx))]
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
let delim = self.delim(ctx.styles());
|
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())
|
layout_delimiters(ctx, frame, Some(delim.open()), None, self.span())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,8 +348,9 @@ fn layout_vec_body(
|
|||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
column: &[Content],
|
column: &[Content],
|
||||||
align: FixedAlign,
|
align: FixedAlign,
|
||||||
|
row_gap: Rel<Abs>,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let gap = ROW_GAP.scaled(ctx);
|
let gap = row_gap.relative_to(ctx.regions.base().y);
|
||||||
ctx.style(ctx.style.for_denominator());
|
ctx.style(ctx.style.for_denominator());
|
||||||
let mut flat = vec![];
|
let mut flat = vec![];
|
||||||
for child in column {
|
for child in column {
|
||||||
@ -295,13 +365,11 @@ fn layout_mat_body(
|
|||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
rows: &[Vec<Content>],
|
rows: &[Vec<Content>],
|
||||||
augment: Option<Augment<Abs>>,
|
augment: Option<Augment<Abs>>,
|
||||||
|
gap: Axes<Rel<Abs>>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let row_gap = ROW_GAP.scaled(ctx);
|
let gap = gap.zip_map(ctx.regions.base(), Rel::relative_to);
|
||||||
let col_gap = COL_GAP.scaled(ctx);
|
let half_gap = gap * 0.5;
|
||||||
|
|
||||||
let half_row_gap = row_gap * 0.5;
|
|
||||||
let half_col_gap = col_gap * 0.5;
|
|
||||||
|
|
||||||
// We provide a default stroke thickness that scales
|
// We provide a default stroke thickness that scales
|
||||||
// with font size to ensure that augmentation lines
|
// 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.
|
// 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.
|
// Sum the row heights, then add the total height of the gaps between rows.
|
||||||
let total_height =
|
let total_height =
|
||||||
heights.iter().map(|&(a, b)| a + b).sum::<Abs>() + row_gap * (nrows - 1) as f64;
|
heights.iter().map(|&(a, b)| a + b).sum::<Abs>() + gap.y * (nrows - 1) as f64;
|
||||||
|
|
||||||
// Width starts at zero because it can't be calculated until later
|
// Width starts at zero because it can't be calculated until later
|
||||||
let mut frame = Frame::new(Size::new(Abs::zero(), total_height));
|
let mut frame = Frame::new(Size::new(Abs::zero(), total_height));
|
||||||
@ -380,7 +448,7 @@ fn layout_mat_body(
|
|||||||
|
|
||||||
frame.push_frame(pos, cell);
|
frame.push_frame(pos, cell);
|
||||||
|
|
||||||
y += ascent + descent + row_gap;
|
y += ascent + descent + gap.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance to the end of the column
|
// 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 a vertical line should be inserted after this column
|
||||||
if vline.0.contains(&(index + 1)) {
|
if vline.0.contains(&(index + 1)) {
|
||||||
frame.push(
|
frame.push(
|
||||||
Point::with_x(x + half_col_gap),
|
Point::with_x(x + half_gap.x),
|
||||||
line_item(total_height, true, stroke.clone(), span),
|
line_item(total_height, true, stroke.clone(), span),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance to the start of the next column
|
// 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
|
// 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
|
// This allows the horizontal lines to be laid out
|
||||||
for line in hline.0 {
|
for line in hline.0 {
|
||||||
let offset = (heights[0..line].iter().map(|&(a, b)| a + b).sum::<Abs>()
|
let offset = (heights[0..line].iter().map(|&(a, b)| a + b).sum::<Abs>()
|
||||||
+ row_gap * (line - 1) as f64)
|
+ gap.y * (line - 1) as f64)
|
||||||
+ half_row_gap;
|
+ half_gap.y;
|
||||||
|
|
||||||
frame.push(
|
frame.push(
|
||||||
Point::with_y(offset),
|
Point::with_y(offset),
|
||||||
|
BIN
tests/ref/math/matrix-gaps.png
Normal file
BIN
tests/ref/math/matrix-gaps.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
17
tests/typ/math/matrix-gaps.typ
Normal file
17
tests/typ/math/matrix-gaps.typ
Normal file
@ -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) $
|
Loading…
x
Reference in New Issue
Block a user