typst_library/math/
lr.rs

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