2024-10-27 18:04:55 +00:00

117 lines
3.4 KiB
Rust

use crate::foundations::{cast, elem, Content, Func, Smart};
use crate::layout::{Abs, Angle, Length, Ratio, Rel};
use crate::math::Mathy;
use crate::visualize::Stroke;
/// Displays a diagonal line over a part of an equation.
///
/// This is commonly used to show the elimination of a term.
///
/// # Example
/// ```example
/// >>> #set page(width: 140pt)
/// Here, we can simplify:
/// $ (a dot b dot cancel(x)) /
/// cancel(x) $
/// ```
#[elem(Mathy)]
pub struct CancelElem {
/// The content over which the line should be placed.
#[required]
pub body: Content,
/// The length of the line, relative to the length of the diagonal spanning
/// the whole element being "cancelled". A value of `{100%}` would then have
/// the line span precisely the element's diagonal.
///
/// ```example
/// >>> #set page(width: 140pt)
/// $ a + cancel(x, length: #200%)
/// - cancel(x, length: #200%) $
/// ```
#[default(Rel::new(Ratio::one(), Abs::pt(3.0).into()))]
pub length: Rel<Length>,
/// Whether the cancel line should be inverted (flipped along the y-axis).
/// For the default angle setting, inverted means the cancel line
/// points to the top left instead of top right.
///
/// ```example
/// >>> #set page(width: 140pt)
/// $ (a cancel((b + c), inverted: #true)) /
/// cancel(b + c, inverted: #true) $
/// ```
#[default(false)]
pub inverted: bool,
/// Whether two opposing cancel lines should be drawn, forming a cross over
/// the element. Overrides `inverted`.
///
/// ```example
/// >>> #set page(width: 140pt)
/// $ cancel(Pi, cross: #true) $
/// ```
#[default(false)]
pub cross: bool,
/// How much to rotate the cancel line.
///
/// - If given an angle, the line is rotated by that angle clockwise with
/// respect to the y-axis.
/// - If `{auto}`, the line assumes the default angle; that is, along the
/// rising diagonal of the content box.
/// - If given a function `angle => angle`, the line is rotated, with
/// respect to the y-axis, by the angle returned by that function. The
/// function receives the default angle as its input.
///
/// ```example
/// >>> #set page(width: 140pt)
/// $ cancel(Pi)
/// cancel(Pi, angle: #0deg)
/// cancel(Pi, angle: #45deg)
/// cancel(Pi, angle: #90deg)
/// cancel(1/(1+x), angle: #(a => a + 45deg))
/// cancel(1/(1+x), angle: #(a => a + 90deg)) $
/// ```
pub angle: Smart<CancelAngle>,
/// How to [stroke]($stroke) the cancel line.
///
/// ```example
/// >>> #set page(width: 140pt)
/// $ cancel(
/// sum x,
/// stroke: #(
/// paint: red,
/// thickness: 1.5pt,
/// dash: "dashed",
/// ),
/// ) $
/// ```
#[resolve]
#[fold]
#[default(Stroke {
// Default stroke has 0.5pt for better visuals.
thickness: Smart::Custom(Abs::pt(0.5).into()),
..Default::default()
})]
pub stroke: Stroke,
}
/// Defines the cancel line.
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum CancelAngle {
Angle(Angle),
Func(Func),
}
cast! {
CancelAngle,
self => match self {
Self::Angle(v) => v.into_value(),
Self::Func(v) => v.into_value()
},
v: Angle => CancelAngle::Angle(v),
v: Func => CancelAngle::Func(v),
}