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}