typst_library/math/cancel.rs
1use crate::foundations::{Content, Func, Smart, cast, elem};
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 #[default(Rel::new(Ratio::one(), Abs::pt(3.0).into()))]
33 pub length: Rel<Length>,
34
35 /// Whether the cancel line should be inverted (flipped along the y-axis).
36 /// For the default angle setting, inverted means the cancel line
37 /// points to the top left instead of top right.
38 ///
39 /// ```example
40 /// >>> #set page(width: 140pt)
41 /// $ (a cancel((b + c), inverted: #true)) /
42 /// cancel(b + c, inverted: #true) $
43 /// ```
44 #[default(false)]
45 pub inverted: bool,
46
47 /// Whether two opposing cancel lines should be drawn, forming a cross over
48 /// the element. Overrides `inverted`.
49 ///
50 /// ```example
51 /// >>> #set page(width: 140pt)
52 /// $ cancel(Pi, cross: #true) $
53 /// ```
54 #[default(false)]
55 pub cross: bool,
56
57 /// How much to rotate the cancel line.
58 ///
59 /// - If given an angle, the line is rotated by that angle clockwise with
60 /// respect to the y-axis.
61 /// - If `{auto}`, the line assumes the default angle; that is, along the
62 /// rising diagonal of the content box.
63 /// - If given a function `angle => angle`, the line is rotated, with
64 /// respect to the y-axis, by the angle returned by that function. The
65 /// function receives the default angle as its input.
66 ///
67 /// ```example
68 /// >>> #set page(width: 140pt)
69 /// $ cancel(Pi)
70 /// cancel(Pi, angle: #0deg)
71 /// cancel(Pi, angle: #45deg)
72 /// cancel(Pi, angle: #90deg)
73 /// cancel(1/(1+x), angle: #(a => a + 45deg))
74 /// cancel(1/(1+x), angle: #(a => a + 90deg)) $
75 /// ```
76 pub angle: Smart<CancelAngle>,
77
78 /// How to [stroke]($stroke) the cancel line.
79 ///
80 /// ```example
81 /// >>> #set page(width: 140pt)
82 /// $ cancel(
83 /// sum x,
84 /// stroke: #(
85 /// paint: red,
86 /// thickness: 1.5pt,
87 /// dash: "dashed",
88 /// ),
89 /// ) $
90 /// ```
91 #[fold]
92 #[default(Stroke {
93 // Default stroke has 0.5pt for better visuals.
94 thickness: Smart::Custom(Abs::pt(0.5).into()),
95 ..Default::default()
96 })]
97 pub stroke: Stroke,
98}
99
100/// Defines the cancel line.
101#[derive(Debug, Clone, PartialEq, Hash)]
102pub enum CancelAngle {
103 Angle(Angle),
104 Func(Func),
105}
106
107cast! {
108 CancelAngle,
109 self => match self {
110 Self::Angle(v) => v.into_value(),
111 Self::Func(v) => v.into_value()
112 },
113 v: Angle => CancelAngle::Angle(v),
114 v: Func => CancelAngle::Func(v),
115}