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}