typst_library/math/
cancel.rs

1use crate::foundations::{cast, elem, Content, Func, Smart};
2use crate::layout::{Abs, Angle, Length, Ratio, Rel};
3use crate::math::Mathy;
4use crate::visualize::Stroke;
5
6/// Displays a diagonal line over a part of an equation.
7///
8/// This is commonly used to show the elimination of a term.
9///
10/// # Example
11/// ```example
12/// >>> #set page(width: 140pt)
13/// Here, we can simplify:
14/// $ (a dot b dot cancel(x)) /
15///     cancel(x) $
16/// ```
17#[elem(Mathy)]
18pub struct CancelElem {
19    /// The content over which the line should be placed.
20    #[required]
21    pub body: Content,
22
23    /// The length of the line, relative to the length of the diagonal spanning
24    /// the whole element being "cancelled". A value of `{100%}` would then have
25    /// the line span precisely the element's diagonal.
26    ///
27    /// ```example
28    /// >>> #set page(width: 140pt)
29    /// $ a + cancel(x, length: #200%)
30    ///     - cancel(x, length: #200%) $
31    /// ```
32    #[resolve]
33    #[default(Rel::new(Ratio::one(), Abs::pt(3.0).into()))]
34    pub length: Rel<Length>,
35
36    /// Whether the cancel line should be inverted (flipped along the y-axis).
37    /// For the default angle setting, inverted means the cancel line
38    /// points to the top left instead of top right.
39    ///
40    /// ```example
41    /// >>> #set page(width: 140pt)
42    /// $ (a cancel((b + c), inverted: #true)) /
43    ///     cancel(b + c, inverted: #true) $
44    /// ```
45    #[default(false)]
46    pub inverted: bool,
47
48    /// Whether two opposing cancel lines should be drawn, forming a cross over
49    /// the element. Overrides `inverted`.
50    ///
51    /// ```example
52    /// >>> #set page(width: 140pt)
53    /// $ cancel(Pi, cross: #true) $
54    /// ```
55    #[default(false)]
56    pub cross: bool,
57
58    /// How much to rotate the cancel line.
59    ///
60    /// - If given an angle, the line is rotated by that angle clockwise with
61    ///   respect to the y-axis.
62    /// - If `{auto}`, the line assumes the default angle; that is, along the
63    ///   rising diagonal of the content box.
64    /// - If given a function `angle => angle`, the line is rotated, with
65    ///   respect to the y-axis, by the angle returned by that function. The
66    ///   function receives the default angle as its input.
67    ///
68    /// ```example
69    /// >>> #set page(width: 140pt)
70    /// $ cancel(Pi)
71    ///   cancel(Pi, angle: #0deg)
72    ///   cancel(Pi, angle: #45deg)
73    ///   cancel(Pi, angle: #90deg)
74    ///   cancel(1/(1+x), angle: #(a => a + 45deg))
75    ///   cancel(1/(1+x), angle: #(a => a + 90deg)) $
76    /// ```
77    pub angle: Smart<CancelAngle>,
78
79    /// How to [stroke]($stroke) the cancel line.
80    ///
81    /// ```example
82    /// >>> #set page(width: 140pt)
83    /// $ cancel(
84    ///   sum x,
85    ///   stroke: #(
86    ///     paint: red,
87    ///     thickness: 1.5pt,
88    ///     dash: "dashed",
89    ///   ),
90    /// ) $
91    /// ```
92    #[resolve]
93    #[fold]
94    #[default(Stroke {
95        // Default stroke has 0.5pt for better visuals.
96        thickness: Smart::Custom(Abs::pt(0.5).into()),
97        ..Default::default()
98    })]
99    pub stroke: Stroke,
100}
101
102/// Defines the cancel line.
103#[derive(Debug, Clone, PartialEq, Hash)]
104pub enum CancelAngle {
105    Angle(Angle),
106    Func(Func),
107}
108
109cast! {
110    CancelAngle,
111    self => match self {
112        Self::Angle(v) => v.into_value(),
113        Self::Func(v) => v.into_value()
114    },
115    v: Angle => CancelAngle::Angle(v),
116    v: Func => CancelAngle::Func(v),
117}