diff options
Diffstat (limited to 'crates/typst-library/src/math/lr.rs')
| -rw-r--r-- | crates/typst-library/src/math/lr.rs | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/crates/typst-library/src/math/lr.rs b/crates/typst-library/src/math/lr.rs new file mode 100644 index 00000000..07ab0dd4 --- /dev/null +++ b/crates/typst-library/src/math/lr.rs @@ -0,0 +1,135 @@ +use crate::foundations::{elem, func, Content, NativeElement, Smart}; +use crate::layout::{Length, Rel}; +use crate::math::Mathy; +use crate::text::TextElem; + +/// Scales delimiters. +/// +/// While matched delimiters scale by default, this can be used to scale +/// unmatched delimiters and to control the delimiter scaling more precisely. +#[elem(title = "Left/Right", Mathy)] +pub struct LrElem { + /// The size of the brackets, relative to the height of the wrapped content. + pub size: Smart<Rel<Length>>, + + /// The delimited content, including the delimiters. + #[required] + #[parse( + let mut arguments = args.all::<Content>()?.into_iter(); + let mut body = arguments.next().unwrap_or_default(); + arguments.for_each(|arg| body += TextElem::packed(',') + arg); + body + )] + pub body: Content, +} + +/// Scales delimiters vertically to the nearest surrounding `{lr()}` group. +/// +/// ```example +/// $ { x mid(|) sum_(i=1)^n w_i|f_i (x)| < 1 } $ +/// ``` +#[elem(Mathy)] +pub struct MidElem { + /// The content to be scaled. + #[required] + pub body: Content, +} + +/// Floors an expression. +/// +/// ```example +/// $ floor(x/2) $ +/// ``` +#[func] +pub fn floor( + /// The size of the brackets, relative to the height of the wrapped content. + #[named] + size: Option<Smart<Rel<Length>>>, + /// The expression to floor. + body: Content, +) -> Content { + delimited(body, '⌊', '⌋', size) +} + +/// Ceils an expression. +/// +/// ```example +/// $ ceil(x/2) $ +/// ``` +#[func] +pub fn ceil( + /// The size of the brackets, relative to the height of the wrapped content. + #[named] + size: Option<Smart<Rel<Length>>>, + /// The expression to ceil. + body: Content, +) -> Content { + delimited(body, '⌈', '⌉', size) +} + +/// Rounds an expression. +/// +/// ```example +/// $ round(x/2) $ +/// ``` +#[func] +pub fn round( + /// The size of the brackets, relative to the height of the wrapped content. + #[named] + size: Option<Smart<Rel<Length>>>, + /// The expression to round. + body: Content, +) -> Content { + delimited(body, '⌊', '⌉', size) +} + +/// Takes the absolute value of an expression. +/// +/// ```example +/// $ abs(x/2) $ +/// ``` +#[func] +pub fn abs( + /// The size of the brackets, relative to the height of the wrapped content. + #[named] + size: Option<Smart<Rel<Length>>>, + /// The expression to take the absolute value of. + body: Content, +) -> Content { + delimited(body, '|', '|', size) +} + +/// Takes the norm of an expression. +/// +/// ```example +/// $ norm(x/2) $ +/// ``` +#[func] +pub fn norm( + /// The size of the brackets, relative to the height of the wrapped content. + #[named] + size: Option<Smart<Rel<Length>>>, + /// The expression to take the norm of. + body: Content, +) -> Content { + delimited(body, '‖', '‖', size) +} + +fn delimited( + body: Content, + left: char, + right: char, + size: Option<Smart<Rel<Length>>>, +) -> Content { + let span = body.span(); + let mut elem = LrElem::new(Content::sequence([ + TextElem::packed(left), + body, + TextElem::packed(right), + ])); + // Push size only if size is provided + if let Some(size) = size { + elem.push_size(size); + } + elem.pack().spanned(span) +} |
