mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Vector and case layout
This commit is contained in:
parent
a0146b5b9b
commit
953bdc1859
@ -1,5 +1,8 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const ROW_GAP: Em = Em::new(0.5);
|
||||||
|
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
|
||||||
|
|
||||||
/// # Vector
|
/// # Vector
|
||||||
/// A column vector.
|
/// A column vector.
|
||||||
///
|
///
|
||||||
@ -18,7 +21,7 @@ use super::*;
|
|||||||
/// ## Category
|
/// ## Category
|
||||||
/// math
|
/// math
|
||||||
#[func]
|
#[func]
|
||||||
#[capable(Texify)]
|
#[capable(LayoutMath)]
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct VecNode(Vec<Content>);
|
pub struct VecNode(Vec<Content>);
|
||||||
|
|
||||||
@ -47,52 +50,13 @@ impl VecNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texify for VecNode {
|
impl LayoutMath for VecNode {
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
let kind = match t.styles.get(Self::DELIM) {
|
let delim = ctx.outer.get(Self::DELIM);
|
||||||
Delimiter::Paren => "pmatrix",
|
layout(ctx, &self.0, Align::Center, Some(delim.open()), Some(delim.close()))
|
||||||
Delimiter::Bracket => "bmatrix",
|
|
||||||
Delimiter::Brace => "Bmatrix",
|
|
||||||
Delimiter::Bar => "vmatrix",
|
|
||||||
};
|
|
||||||
|
|
||||||
t.push_str("\\begin{");
|
|
||||||
t.push_str(kind);
|
|
||||||
t.push_str("}");
|
|
||||||
|
|
||||||
for component in &self.0 {
|
|
||||||
component.texify(t)?;
|
|
||||||
t.push_str("\\\\");
|
|
||||||
}
|
|
||||||
t.push_str("\\end{");
|
|
||||||
t.push_str(kind);
|
|
||||||
t.push_str("}");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A vector / matrix delimiter.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum Delimiter {
|
|
||||||
Paren,
|
|
||||||
Bracket,
|
|
||||||
Brace,
|
|
||||||
Bar,
|
|
||||||
}
|
|
||||||
|
|
||||||
castable! {
|
|
||||||
Delimiter,
|
|
||||||
/// Delimit the vector with parentheses.
|
|
||||||
"(" => Self::Paren,
|
|
||||||
/// Delimit the vector with brackets.
|
|
||||||
"[" => Self::Bracket,
|
|
||||||
/// Delimit the vector with curly braces.
|
|
||||||
"{" => Self::Brace,
|
|
||||||
/// Delimit the vector with vertical bars.
|
|
||||||
"|" => Self::Bar,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Cases
|
/// # Cases
|
||||||
/// A case distinction.
|
/// A case distinction.
|
||||||
///
|
///
|
||||||
@ -113,7 +77,7 @@ castable! {
|
|||||||
/// ## Category
|
/// ## Category
|
||||||
/// math
|
/// math
|
||||||
#[func]
|
#[func]
|
||||||
#[capable(Texify)]
|
#[capable(LayoutMath)]
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct CasesNode(Vec<Content>);
|
pub struct CasesNode(Vec<Content>);
|
||||||
|
|
||||||
@ -133,14 +97,132 @@ impl CasesNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texify for CasesNode {
|
impl LayoutMath for CasesNode {
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
t.push_str("\\begin{cases}");
|
layout(ctx, &self.0, Align::Left, Some('{'), None)
|
||||||
for component in &self.0 {
|
|
||||||
component.texify(t)?;
|
|
||||||
t.push_str("\\\\");
|
|
||||||
}
|
|
||||||
t.push_str("\\end{cases}");
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A vector / matrix delimiter.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum Delimiter {
|
||||||
|
Paren,
|
||||||
|
Bracket,
|
||||||
|
Brace,
|
||||||
|
Bar,
|
||||||
|
DoubleBar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Delimiter {
|
||||||
|
/// The delimiter's opening character.
|
||||||
|
fn open(self) -> char {
|
||||||
|
match self {
|
||||||
|
Self::Paren => '(',
|
||||||
|
Self::Bracket => '[',
|
||||||
|
Self::Brace => '{',
|
||||||
|
Self::Bar => '|',
|
||||||
|
Self::DoubleBar => '‖',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The delimiter's closing character.
|
||||||
|
fn close(self) -> char {
|
||||||
|
match self {
|
||||||
|
Self::Paren => ')',
|
||||||
|
Self::Bracket => ']',
|
||||||
|
Self::Brace => '}',
|
||||||
|
Self::Bar => '|',
|
||||||
|
Self::DoubleBar => '‖',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
castable! {
|
||||||
|
Delimiter,
|
||||||
|
/// Delimit the vector with parentheses.
|
||||||
|
"(" => Self::Paren,
|
||||||
|
/// Delimit the vector with brackets.
|
||||||
|
"[" => Self::Bracket,
|
||||||
|
/// Delimit the vector with curly braces.
|
||||||
|
"{" => Self::Brace,
|
||||||
|
/// Delimit the vector with vertical bars.
|
||||||
|
"|" => Self::Bar,
|
||||||
|
/// Delimit the vector with double vertical bars.
|
||||||
|
"||" => Self::DoubleBar,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Layout a matrix.
|
||||||
|
fn layout(
|
||||||
|
ctx: &mut MathContext,
|
||||||
|
elements: &[Content],
|
||||||
|
align: Align,
|
||||||
|
left: Option<char>,
|
||||||
|
right: Option<char>,
|
||||||
|
) -> SourceResult<()> {
|
||||||
|
let axis = scaled!(ctx, axis_height);
|
||||||
|
let gap = ROW_GAP.scaled(ctx);
|
||||||
|
|
||||||
|
ctx.style(ctx.style.for_denominator());
|
||||||
|
let mut rows = vec![];
|
||||||
|
for element in elements {
|
||||||
|
rows.push(ctx.layout_row(element)?);
|
||||||
|
}
|
||||||
|
ctx.unstyle();
|
||||||
|
|
||||||
|
if let Some(left) = left {
|
||||||
|
ctx.push(GlyphFragment::new(ctx, left));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut frame = stack(ctx, rows, align, gap, 0);
|
||||||
|
frame.set_baseline(frame.height() / 2.0 + axis);
|
||||||
|
|
||||||
|
ctx.push(frame);
|
||||||
|
|
||||||
|
if let Some(right) = right {
|
||||||
|
ctx.push(GlyphFragment::new(ctx, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user