typst_library/math/
lr.rs

1use crate::foundations::{elem, func, Content, NativeElement, SymbolElem};
2use crate::layout::{Length, Rel};
3use crate::math::Mathy;
4
5/// Scales delimiters.
6///
7/// While matched delimiters scale by default, this can be used to scale
8/// unmatched delimiters and to control the delimiter scaling more precisely.
9#[elem(title = "Left/Right", Mathy)]
10pub struct LrElem {
11    /// The size of the brackets, relative to the height of the wrapped content.
12    #[resolve]
13    #[default(Rel::one())]
14    pub size: Rel<Length>,
15
16    /// The delimited content, including the delimiters.
17    #[required]
18    #[parse(
19        let mut arguments = args.all::<Content>()?.into_iter();
20        let mut body = arguments.next().unwrap_or_default();
21        arguments.for_each(|arg| body += SymbolElem::packed(',') + arg);
22        body
23    )]
24    pub body: Content,
25}
26
27/// Scales delimiters vertically to the nearest surrounding `{lr()}` group.
28///
29/// ```example
30/// $ { x mid(|) sum_(i=1)^n w_i|f_i (x)| < 1 } $
31/// ```
32#[elem(Mathy)]
33pub struct MidElem {
34    /// The content to be scaled.
35    #[required]
36    pub body: Content,
37}
38
39/// Floors an expression.
40///
41/// ```example
42/// $ floor(x/2) $
43/// ```
44#[func]
45pub fn floor(
46    /// The size of the brackets, relative to the height of the wrapped content.
47    #[named]
48    size: Option<Rel<Length>>,
49    /// The expression to floor.
50    body: Content,
51) -> Content {
52    delimited(body, '⌊', '⌋', size)
53}
54
55/// Ceils an expression.
56///
57/// ```example
58/// $ ceil(x/2) $
59/// ```
60#[func]
61pub fn ceil(
62    /// The size of the brackets, relative to the height of the wrapped content.
63    #[named]
64    size: Option<Rel<Length>>,
65    /// The expression to ceil.
66    body: Content,
67) -> Content {
68    delimited(body, '⌈', '⌉', size)
69}
70
71/// Rounds an expression.
72///
73/// ```example
74/// $ round(x/2) $
75/// ```
76#[func]
77pub fn round(
78    /// The size of the brackets, relative to the height of the wrapped content.
79    #[named]
80    size: Option<Rel<Length>>,
81    /// The expression to round.
82    body: Content,
83) -> Content {
84    delimited(body, '⌊', '⌉', size)
85}
86
87/// Takes the absolute value of an expression.
88///
89/// ```example
90/// $ abs(x/2) $
91/// ```
92#[func]
93pub fn abs(
94    /// The size of the brackets, relative to the height of the wrapped content.
95    #[named]
96    size: Option<Rel<Length>>,
97    /// The expression to take the absolute value of.
98    body: Content,
99) -> Content {
100    delimited(body, '|', '|', size)
101}
102
103/// Takes the norm of an expression.
104///
105/// ```example
106/// $ norm(x/2) $
107/// ```
108#[func]
109pub fn norm(
110    /// The size of the brackets, relative to the height of the wrapped content.
111    #[named]
112    size: Option<Rel<Length>>,
113    /// The expression to take the norm of.
114    body: Content,
115) -> Content {
116    delimited(body, '‖', '‖', size)
117}
118
119fn delimited(
120    body: Content,
121    left: char,
122    right: char,
123    size: Option<Rel<Length>>,
124) -> Content {
125    let span = body.span();
126    let mut elem = LrElem::new(Content::sequence([
127        SymbolElem::packed(left),
128        body,
129        SymbolElem::packed(right),
130    ]));
131    // Push size only if size is provided
132    if let Some(size) = size {
133        elem.push_size(size);
134    }
135    elem.pack().spanned(span)
136}