diff --git a/crates/typst-docs/src/html.rs b/crates/typst-docs/src/html.rs index ea81fa5c9..c79f07cb8 100644 --- a/crates/typst-docs/src/html.rs +++ b/crates/typst-docs/src/html.rs @@ -356,7 +356,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { md::escape::escape_html(&mut buf, &display).unwrap(); buf.push_str(""); return Html::new(buf); - } else if !matches!(lang, "example" | "typ") { + } else if !matches!(lang, "example" | "typ" | "preview") { let set = &*typst_library::text::SYNTAXES; let buf = syntect::html::highlighted_html_for_string( &display, @@ -369,10 +369,14 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { return Html::new(buf); } - let root = typst::syntax::parse(&display); - let highlighted = Html::new(typst::syntax::highlight_html(&root)); - if lang == "typ" { - return Html::new(format!("
{}
", highlighted.as_str())); + let mut highlighted = None; + if matches!(lang, "example" | "typ") { + let root = typst::syntax::parse(&display); + let html = Html::new(typst::syntax::highlight_html(&root)); + if lang == "typ" { + return Html::new(format!("
{}
", html.as_str())); + } + highlighted = Some(html); } let id = FileId::new(None, VirtualPath::new("main.typ")); diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs index 30ba4ebb9..d32b5d66f 100644 --- a/crates/typst-docs/src/lib.rs +++ b/crates/typst-docs/src/lib.rs @@ -77,7 +77,7 @@ pub trait Resolver { fn image(&self, filename: &str, data: &[u8]) -> String; /// Produce HTML for an example. - fn example(&self, hash: u128, source: Html, frames: &[Frame]) -> Html; + fn example(&self, hash: u128, source: Option, frames: &[Frame]) -> Html; /// Determine the commits between two tags. fn commits(&self, from: &str, to: &str) -> Vec; @@ -780,7 +780,7 @@ mod tests { None } - fn example(&self, _: u128, _: Html, _: &[Frame]) -> Html { + fn example(&self, _: u128, _: Option, _: &[Frame]) -> Html { Html::new(String::new()) } diff --git a/crates/typst/src/geom/color.rs b/crates/typst/src/geom/color.rs index a6d2135f3..260137ea9 100644 --- a/crates/typst/src/geom/color.rs +++ b/crates/typst/src/geom/color.rs @@ -32,158 +32,122 @@ const ANGLE_EPSILON: f32 = 1e-5; /// - HSL through the [`color.hsl` function]($color.hsl) /// - HSV through the [`color.hsv` function]($color.hsv) /// -/// Typst provides the following built-in colors: -/// -/// `black`, `gray`, `silver`, `white`, `navy`, `blue`, `aqua`, `teal`, -/// `eastern`, `purple`, `fuchsia`, `maroon`, `red`, `orange`, `yellow`, -/// `olive`, `green`, and `lime`. /// /// # Example -/// The predefined colors and the color constructors are available globally and -/// also in the color type's scope, so you can write either of the following -/// two: +/// /// ```example /// #rect(fill: aqua) -/// #rect(fill: color.aqua) /// ``` /// -/// ## Color maps -/// Typst also includes a number of preset color maps. In the following section, -/// the list of available color maps is given, along with a sample of each gradient -/// and relevant comments. Most of these color maps are chosen to be color blind -/// friendly. +/// # Predefined colors +/// Typst defines the following built-in colors: /// -/// ### Turbo -/// The [`turbo`]($color.map.turbo) gradient is a rainbow-like gradient that is -/// perceptually uniform. You can learn more about the turbo color map on -/// Google's [blog post](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html). +/// | Color | Definition | +/// |-----------|:-------------------| +/// | `black` | `{luma(0)}` | +/// | `gray` | `{luma(170)}` | +/// | `silver` | `{luma(221)}` | +/// | `white` | `{luma(255)}` | +/// | `navy` | `{rgb("#001f3f")}` | +/// | `blue` | `{rgb("#0074d9")}` | +/// | `aqua` | `{rgb("#7fdbff")}` | +/// | `teal` | `{rgb("#39cccc")}` | +/// | `eastern` | `{rgb("#239dad")}` | +/// | `purple` | `{rgb("#b10dc9")}` | +/// | `fuchsia` | `{rgb("#f012be")}` | +/// | `maroon` | `{rgb("#85144b")}` | +/// | `red` | `{rgb("#ff4136")}` | +/// | `orange` | `{rgb("#ff851b")}` | +/// | `yellow` | `{rgb("#ffdc00")}` | +/// | `olive` | `{rgb("#3d9970")}` | +/// | `green` | `{rgb("#2ecc40")}` | +/// | `lime` | `{rgb("#01ff70")}` | /// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.turbo)) -/// ``` +/// The predefined colors and the most important color constructors are +/// available globally and also in the color type's scope, so you can write +/// either `color.red` or just `red`. /// -/// ### Cividis -/// The [`cividis`]($color.map.cividis) gradient is a blue to gray to yellow -/// gradient. You can learn more about the Cividis color map on the -/// Berkley Institute for Data Science's [blog post](https://bids.github.io/colormap/). +/// ```preview +/// #let colors = ( +/// "black", "gray", "silver", "white", +/// "navy", "blue", "aqua", "teal", +/// "eastern", "purple", "fuchsia", +/// "maroon", "red", "orange", "yellow", +/// "olive", "green", "lime", +/// ) /// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.cividis)) -/// ``` -/// -/// ### Rainbow -/// The [`rainbow`]($color.map.rainbow) gradient cycles through the full color -/// spectrum. This color map is best used by setting the interpolation color -/// space to [HSL]($color.hsl). -/// -/// **Attention:** The rainbow gradient is _not suitable_ for data visualization -/// because it is not perceptually uniform, so the differences between values -/// become unclear to your readers. It should only be used for decorative -/// purposes. -/// -/// ```example -/// #rect( -/// width: 100%, -/// height: 20pt, -/// fill: gradient.linear(..color.map.rainbow, space: color.hsl) +/// #set text(font: "PT Sans") +/// #set page(width: auto) +/// #grid( +/// columns: 9, +/// gutter: 10pt, +/// ..colors.map(name => { +/// let c = eval(name) +/// let cp = c.components() +/// let x = cp.sum() / cp.len() +/// set text(fill: white) if x < 50% +/// set square(stroke: black) if c == white +/// set align(center + horizon) +/// square(size: 50pt, fill: c, name) +/// }) /// ) /// ``` /// -/// ### Spectral -/// The [`spectral`]($color.map.spectral) gradient is a red to yellow to blue -/// gradient. Spectral does not take any parameters. +/// # Predefined color maps +/// Typst also includes a number of preset color maps that can be used for +/// gradients. Most of these color maps are chosen to be color blind friendly. /// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.spectral)) +/// | Map | Details | +/// |------------|:------------------------------------------------------------| +/// | `turbo` | A perceptually uniform rainbow-like color map. Read [this blog post](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) for more details. | +/// | `cividis` | A blue to gray to yellow color map. See [this blog post](https://bids.github.io/colormap/) for more details. | +/// | `rainbow` | Cycles through the full color spectrum. This color map is best used by setting the interpolation color space to [HSL]($color.hsl). The rainbow gradient is **not suitable** for data visualization because it is not perceptually uniform, so the differences between values become unclear to your readers. It should only be used for decorative purposes. | +/// | `spectral` | Red to yellow to blue color map. | +/// | `viridis` | A purple to teal to yellow color map. | +/// | `inferno` | A black to red to yellow color map. | +/// | `magma` | A black to purple to yellow color map. | +/// | `plasma` | A purple to pink to yellow color map. | +/// | `rocket` | A black to red to white color map. | +/// | `mako` | A black to teal to yellow color map. | +/// | `vlag` | A light blue to white to red color map. | +/// | `icefire` | A light teal to black to yellow color map. | +/// | `flare` | A orange to purple color map that is perceptually uniform. | +/// | `crest` | A blue to white to red color map. | +/// +/// Some popular presets are not included because they are not available under a +/// free licence. Others, like +/// [Jet](https://jakevdp.github.io/blog/2014/10/16/how-bad-is-your-colormap/), +/// are not included because they are not not color blind friendly. Feel free to +/// use or create a package with other presets that are useful to you! +/// +/// ```preview +/// #set page(width: auto, height: auto) +/// #set text(font: "PT Sans", size: 8pt) +/// +/// #let maps = ( +/// "turbo", "cividis", "rainbow", "spectral", +/// "viridis", "inferno", "magma", "plasma", +/// "rocket", "mako", "vlag", "icefire", +/// "flare", "crest", +/// ) +/// +/// #stack(dir: ltr, spacing: 3pt, ..maps.map((name) => { +/// let map = eval("color.map." + name) +/// stack( +/// dir: ttb, +/// block( +/// width: 15pt, +/// height: 100pt, +/// fill: gradient.linear(..map, angle: 90deg), +/// ), +/// block( +/// width: 15pt, +/// height: 32pt, +/// move(dy: 8pt, rotate(90deg, name)), +/// ), +/// ) +/// })) /// ``` -/// -/// ### Viridis -/// The [`viridis`]($color.map.viridis) gradient is a purple to teal to yellow -/// gradient. Viridis does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.viridis)) -/// ``` -/// -/// ### Inferno -/// The [`inferno`]($color.map.inferno) gradient is a black to red to yellow -/// gradient. Inferno does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.inferno)) -/// ``` -/// -/// ### Magma -/// The [`magma`]($color.map.magma) gradient is a black to purple to yellow -/// gradient. Magma does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.magma)) -/// ``` -/// -/// ### Plasma -/// The [`plasma`]($color.map.plasma) gradient is a purple to pink to yellow -/// gradient. Plasma does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.plasma)) -/// ``` -/// -/// ### Rocket -/// The [`rocket`]($color.map.rocket) gradient is a black to red to white -/// gradient. Rocket does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.rocket)) -/// ``` -/// -/// ### Mako -/// The [`mako`]($color.map.mako) gradient is a black to teal to yellow gradient -///. Mako does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.mako)) -/// ``` -/// -/// ### Vlag -/// The [`vlag`]($color.map.vlag) gradient is a light blue to white to red -/// gradient. Vlag does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.vlag)) -/// ``` -/// -/// ### Icefire -/// The [`icefire`]($color.map.icefire) gradient is a light teal to black to -/// yellow gradient. Icefire does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.icefire)) -/// ``` -/// -/// ### Flare -/// The [`flare`]($color.map.flare) gradient is an orange to purple gradient that -/// is perceptually uniform. Flare does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.flare)) -/// ``` -/// -/// ### Crest -/// The [`crest`]($color.map.crest) gradient is a blue to white to red gradient . -///Crest does not take any parameters. -/// -/// ```example -/// #rect(width: 100%, height: 20pt, fill: gradient.linear(..color.map.crest)) -/// ``` -/// -/// ### On other presets -/// [Jet](https://jakevdp.github.io/blog/2014/10/16/how-bad-is-your-colormap/) -/// is not color blind friendly and should not be used for data visualization, -/// which is why it is not included in Typst. Other popular presets are not -/// neccesarily under a free licence, which is why we could not include them. -/// -/// Feel free to use or create a package with other presets useful to you! #[ty(scope)] #[derive(Debug, Copy, Clone)] pub enum Color { @@ -251,7 +215,6 @@ impl Color { pub const MAP: fn() -> Module = || { // Lazy to avoid re-allocating. static MODULE: Lazy = Lazy::new(map); - MODULE.clone() }; @@ -276,10 +239,11 @@ impl Color { /// Create a grayscale color. /// - /// A grayscale color is represented internally by a single `lightness` component. + /// A grayscale color is represented internally by a single `lightness` + /// component. /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example /// #for x in range(250, step: 50) { @@ -294,7 +258,9 @@ impl Color { /// The lightness component. #[external] lightness: Component, - /// The color to convert to grayscale. + /// Alternatively: The color to convert to grayscale. + /// + /// If this is given, the `lightness` should not be given. #[external] color: Color, ) -> SourceResult { @@ -315,14 +281,15 @@ impl Color { /// - Creating grayscale images with uniform perceived lightness /// - Creating smooth and uniform color transition and gradients /// - /// A linear Oklab color is represented internally by an array of four components: + /// A linear Oklab color is represented internally by an array of four + /// components: /// - lightness ([`ratio`]($ratio)) /// - a ([`float`]($float) in the range `[-0.4..0.4]`) /// - b ([`float`]($float) in the range `[-0.4..0.4]`) /// - alpha ([`ratio`]($ratio)) /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example /// #square( @@ -346,7 +313,9 @@ impl Color { /// The key component. #[external] alpha: RatioComponent, - /// The color to convert to Oklab. + /// Alternatively: The color to convert to Oklab. + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -375,19 +344,20 @@ impl Color { /// perform color operations such as blending and interpolation. Although, /// you should prefer to use the [`oklab` function]($color.oklab) for these. /// - /// A linear RGB(A) color is represented internally by an array of four components: + /// A linear RGB(A) color is represented internally by an array of four + /// components: /// - red ([`ratio`]($ratio)) /// - green ([`ratio`]($ratio)) /// - blue ([`ratio`]($ratio)) /// - alpha ([`ratio`]($ratio)) /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example - /// #square( - /// fill: color.linear-rgb(30%, 50%, 10%) - /// ) + /// #square(fill: color.linear-rgb( + /// 30%, 50%, 10%, + /// )) /// ``` #[func(title = "Linear RGB")] pub fn linear_rgb( @@ -406,7 +376,9 @@ impl Color { /// The alpha component. #[external] alpha: Component, - /// The color to convert to linear RGB(A). + /// Alternatively: The color to convert to linear RGB(A). + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -450,20 +422,6 @@ impl Color { /// The real arguments (the other arguments are just for the docs, this /// function is a bit involved, so we parse the arguments manually). args: Args, - /// The color in hexadecimal notation. - /// - /// Accepts three, four, six or eight hexadecimal digits and optionally - /// a leading hash. - /// - /// If this string is given, the individual components should not be given. - /// - /// ```example - /// #text(16pt, rgb("#239dad"))[ - /// *Typst* - /// ] - /// ``` - #[external] - hex: Str, /// The red component. #[external] red: Component, @@ -476,7 +434,23 @@ impl Color { /// The alpha component. #[external] alpha: Component, - /// The color to convert to RGB(A). + /// Alternatively: The color in hexadecimal notation. + /// + /// Accepts three, four, six or eight hexadecimal digits and optionally + /// a leading hash. + /// + /// If this is given, the individual components should not be given. + /// + /// ```example + /// #text(16pt, rgb("#239dad"))[ + /// *Typst* + /// ] + /// ``` + #[external] + hex: Str, + /// Alternatively: The color to convert to RGB(a). + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -511,8 +485,8 @@ impl Color { /// - yellow ([`ratio`]($ratio)) /// - key ([`ratio`]($ratio)) /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example /// #square( @@ -536,7 +510,9 @@ impl Color { /// The key component. #[external] key: RatioComponent, - /// The color to convert to CMYK. + /// Alternatively: The color to convert to CMYK. + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -569,8 +545,8 @@ impl Color { /// - lightness ([`ratio`]($ratio)) /// - alpha ([`ratio`]($ratio)) /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example /// #square( @@ -594,7 +570,9 @@ impl Color { /// The alpha component. #[external] alpha: Component, - /// The color to convert to HSL. + /// Alternatively: The color to convert to HSL. + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -627,8 +605,8 @@ impl Color { /// - value ([`ratio`]($ratio)) /// - alpha ([`ratio`]($ratio)) /// - /// These components are also available using the [`components`]($color.components) - /// method. + /// These components are also available using the + /// [`components`]($color.components) method. /// /// ```example /// #square( @@ -652,7 +630,9 @@ impl Color { /// The alpha component. #[external] alpha: Component, - /// The color to convert to HSL. + /// Alternatively: The color to convert to HSL. + /// + /// If this is given, the individual components should not be given. #[external] color: Color, ) -> SourceResult { @@ -673,7 +653,7 @@ impl Color { }) } - /// Converts this color into its components. + /// Extracts the components of this color. /// /// The size and values of this array depends on the color space. You can /// obtain the color space using [`space`]($color.space). Below is a table diff --git a/crates/typst/src/geom/gradient.rs b/crates/typst/src/geom/gradient.rs index b7944b10c..f2281930c 100644 --- a/crates/typst/src/geom/gradient.rs +++ b/crates/typst/src/geom/gradient.rs @@ -19,29 +19,41 @@ use crate::syntax::{Span, Spanned}; /// the [`gradient.radial` function]($gradient.radial), and conic gradients /// through the [`gradient.conic` function]($gradient.conic). /// +/// A gradient can be used for the following purposes: +/// - As a fill to paint the interior of a shape: +/// `{rect(fill: gradient.linear(..))}` +/// - As a stroke to paint the outline of a shape: +/// `{rect(stroke: 1pt + gradient.linear(..))}` +/// - As the fill of text: +/// `{set text(fill: gradient.linear(..))}` +/// - As a color map you can [sample]($gradient.sample) from: +/// `{gradient.linear(..).sample(0.5)}` +/// +/// # Examples /// ```example +/// >>> #set square(size: 50pt) /// #stack( /// dir: ltr, -/// square(size: 50pt, fill: gradient.linear(..color.map.rainbow)), -/// square(size: 50pt, fill: gradient.radial(..color.map.rainbow)), -/// square(size: 50pt, fill: gradient.conic(..color.map.rainbow)), +/// spacing: 1fr, +/// square(fill: gradient.linear(..color.map.rainbow)), +/// square(fill: gradient.radial(..color.map.rainbow)), +/// square(fill: gradient.conic(..color.map.rainbow)), /// ) /// ``` /// -/// # Gradients on text -/// Gradients are supported on text but only when setting the relativeness to -/// either `{auto}` (the default value) or `{"parent"}`. It was decided that -/// glyph-by-glyph gradients would not be supported out-of-the-box but can be -/// emulated using [show rules]($styling/#show-rules). -/// -/// You can use gradients on text as follows: +/// Gradients are also supported on text, but only when setting the +/// [relativeness]($gradient.relative) to either `{auto}` (the default value) or +/// `{"parent"}`. To create word-by-word or glyph-by-glyph gradients, you can +/// wrap the words or characters of your text in [boxes]($box) manually or +/// through a [show rule]($styling/#show-rules). /// /// ```example -/// #set page(margin: 1pt) +/// >>> #set page(width: auto, height: auto, margin: 12pt) +/// >>> #set text(size: 12pt) /// #set text(fill: gradient.linear(red, blue)) /// #let rainbow(content) = { -/// set text(fill: gradient.linear(..color.map.rainbow)) -/// box(content) +/// set text(fill: gradient.linear(..color.map.rainbow)) +/// box(content) /// } /// /// This is a gradient on text, but with a #rainbow[twist]! @@ -56,24 +68,13 @@ use crate::syntax::{Span, Spanned}; /// the offsets when defining a gradient. In this case, Typst will space all /// stops evenly. /// -/// # Usage -/// Gradients can be used for the following purposes: -/// - As fills to paint the interior of a shape: -/// `{rect(fill: gradient.linear(..))}` -/// - As strokes to paint the outline of a shape: -/// `{rect(stroke: 1pt + gradient.linear(..))}` -/// - As color maps you can [sample]($gradient.sample) from: -/// `{gradient.linear(..).sample(0.5)}` -/// -/// Gradients are not currently supported on text. -/// /// # Relativeness /// The location of the `{0%}` and `{100%}` stops is dependant on the dimensions /// of a container. This container can either be the shape they are painted on, -/// or to the closest container ancestor. This is controlled by the `relative` -/// argument of a gradient constructor. By default, gradients are relative to -/// the shape they are painted on, unless the gradient is applied on text, in -/// which case they are relative to the closest ancestor container. +/// or to the closest surrounding container. This is controlled by the +/// `relative` argument of a gradient constructor. By default, gradients are +/// relative to the shape they are painted on, unless the gradient is applied on +/// text, in which case they are relative to the closest ancestor container. /// /// Typst determines the ancestor container as follows: /// - For shapes that are placed at the root/top level of the document, the @@ -98,16 +99,16 @@ use crate::syntax::{Span, Spanned}; /// choosing an interpolation space. /// /// | Color space | Perceptually uniform? | -/// | ------------------------------- |:----------------------| -/// | [Oklab]($color.oklab) | *Yes* | -/// | [sRGB]($color.rgb) | *No* | +/// | ------------------------------- |-----------------------| +/// | [Oklab]($color.oklab) | *Yes* | +/// | [sRGB]($color.rgb) | *No* | /// | [linear-RGB]($color.linear-rgb) | *Yes* | -/// | [CMYK]($color.cmyk) | *No* | -/// | [Grayscale]($color.luma) | *Yes* | -/// | [HSL]($color.hsl) | *No* | -/// | [HSV]($color.hsv) | *No* | +/// | [CMYK]($color.cmyk) | *No* | +/// | [Grayscale]($color.luma) | *Yes* | +/// | [HSL]($color.hsl) | *No* | +/// | [HSV]($color.hsv) | *No* | /// -/// ```example +/// ```preview /// >>> #set text(fill: white, font: "IBM Plex Sans", 8pt) /// >>> #set block(spacing: 0pt) /// #let spaces = ( @@ -141,52 +142,20 @@ use crate::syntax::{Span, Spanned}; /// right-to-left, and 270° from bottom-to-top. /// /// ```example -/// #set block(spacing: 0pt) +/// >>> #set square(size: 50pt) /// #stack( /// dir: ltr, -/// square(size: 50pt, fill: gradient.linear(red, blue, angle: 0deg)), -/// square(size: 50pt, fill: gradient.linear(red, blue, angle: 90deg)), -/// square(size: 50pt, fill: gradient.linear(red, blue, angle: 180deg)), -/// square(size: 50pt, fill: gradient.linear(red, blue, angle: 270deg)), +/// spacing: 1fr, +/// square(fill: gradient.linear(red, blue, angle: 0deg)), +/// square(fill: gradient.linear(red, blue, angle: 90deg)), +/// square(fill: gradient.linear(red, blue, angle: 180deg)), +/// square(fill: gradient.linear(red, blue, angle: 270deg)), /// ) /// ``` /// /// # Presets -/// You can find the full list of presets in the documentation of [`color`]($color), -/// below is an overview of them. Note that not all presets are suitable for data -/// visualization and full details and relevant sources can be found in the -/// documentation of [`color`]($color). -/// -/// ```example -/// #set text(fill: white, size: 18pt) -/// #set text(top-edge: "bounds", bottom-edge: "bounds") -/// #let presets = ( -/// ("turbo", color.map.turbo), -/// ("cividis", color.map.cividis), -/// ("rainbow", color.map.rainbow), -/// ("spectral", color.map.spectral), -/// ("viridis", color.map.viridis), -/// ("inferno", color.map.inferno), -/// ("magma", color.map.magma), -/// ("plasma", color.map.plasma), -/// ("rocket", color.map.rocket), -/// ("mako", color.map.mako), -/// ("vlag", color.map.vlag), -/// ("icefire", color.map.icefire), -/// ("flare", color.map.flare), -/// ("crest", color.map.crest), -/// ) -/// -/// #stack( -/// spacing: 3pt, -/// ..presets.map(((name, preset)) => block( -/// width: 100%, -/// height: 20pt, -/// fill: gradient.linear(..preset), -/// align(center + horizon, smallcaps(name)), -/// )) -/// ) -/// ``` +/// Typst predefines color maps that you can use with your gradients. See the +/// [`color`]($color/#predefined-color-maps) documentation for more details. #[ty(scope)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Gradient { @@ -198,13 +167,16 @@ pub enum Gradient { #[scope] #[allow(clippy::too_many_arguments)] impl Gradient { - /// Creates a new linear gradient. + /// Creates a new linear gradient, in which colors transition along a + /// straight line. /// /// ```example /// #rect( /// width: 100%, /// height: 20pt, - /// fill: gradient.linear(..color.map.viridis) + /// fill: gradient.linear( + /// ..color.map.viridis, + /// ), /// ) /// ``` #[func(title = "Linear Gradient")] @@ -225,9 +197,10 @@ impl Gradient { space: ColorSpace, /// The [relative placement](#relativeness) of the gradient. /// - /// For an element placed at the root/top level of the document, the parent - /// is the page itself. For other elements, the parent is the innermost block, - /// box, column, grid, or stack that contains the element. + /// For an element placed at the root/top level of the document, the + /// parent is the page itself. For other elements, the parent is the + /// innermost block, box, column, grid, or stack that contains the + /// element. #[named] #[default(Smart::Auto)] relative: Smart, @@ -267,31 +240,34 @@ impl Gradient { }))) } - /// Creates a new radial gradient. + /// Creates a new radial gradient, in which colors radiate away from an + /// origin. + /// + /// The gradient is defined by two circles: the focal circle and the end + /// circle. The focal circle is a circle with center `focal-center` and + /// radius `focal-radius`, that defines the points at which the gradient + /// starts and has the color of the first stop. The end circle is a circle + /// with center `center` and radius `radius`, that defines the points at + /// which the gradient ends and has the color of the last stop. The gradient + /// is then interpolated between these two circles. + /// + /// Using these four values, also called the focal point for the starting + /// circle and the center and radius for the end circle, we can define a + /// gradient with more interesting properties than a basic radial gradient: /// /// ```example - /// #circle( - /// radius: 20pt, - /// fill: gradient.radial(..color.map.viridis) - /// ) - /// ``` - /// - /// _Focal Point_ - /// The gradient is defined by two circles: the focal circle and the end circle. - /// The focal circle is a circle with center `focal-center` and radius `focal-radius`, - /// that defines the points at which the gradient starts and has the color of the - /// first stop. The end circle is a circle with center `center` and radius `radius`, - /// that defines the points at which the gradient ends and has the color of the last - /// stop. The gradient is then interpolated between these two circles. - /// - /// Using these four values, also called the focal point for the starting circle and - /// the center and radius for the end circle, we can define a gradient with more - /// interesting properties than a basic radial gradient: - /// - /// ```example - /// #circle( - /// radius: 20pt, - /// fill: gradient.radial(..color.map.viridis, focal-center: (10%, 40%), focal-radius: 5%) + /// >>> #set circle(radius: 30pt) + /// #stack( + /// dir: ltr, + /// spacing: 1fr, + /// circle(fill: gradient.radial( + /// ..color.map.viridis, + /// )), + /// circle(fill: gradient.radial( + /// ..color.map.viridis, + /// focal-center: (10%, 40%), + /// focal-radius: 5%, + /// )), /// ) /// ``` #[func] @@ -316,14 +292,14 @@ impl Gradient { #[named] #[default(Smart::Auto)] relative: Smart, - /// The center of the last circle of the gradient. + /// The center of the end circle of the gradient. /// /// A value of `{(50%, 50%)}` means that the end circle is /// centered inside of its container. #[named] #[default(Axes::splat(Ratio::new(0.5)))] center: Axes, - /// The radius of the last circle of the gradient. + /// The radius of the end circle of the gradient. /// /// By default, it is set to `{50%}`. The ending radius must be bigger /// than the focal radius. @@ -384,24 +360,24 @@ impl Gradient { }))) } - /// Creates a new conic gradient (i.e a gradient whose color changes - /// radially around a center point). + /// Creates a new conic gradient, in which colors change radially around a + /// center point. /// - /// ```example - /// #circle( - /// radius: 20pt, - /// fill: gradient.conic(..color.map.viridis) - /// ) - /// ``` - /// - /// _Center Point_ /// You can control the center point of the gradient by using the `center` /// argument. By default, the center point is the center of the shape. /// /// ```example - /// #circle( - /// radius: 20pt, - /// fill: gradient.conic(..color.map.viridis, center: (10%, 40%)) + /// >>> #set circle(radius: 30pt) + /// #stack( + /// dir: ltr, + /// spacing: 1fr, + /// circle(fill: gradient.conic( + /// ..color.map.viridis, + /// )), + /// circle(fill: gradient.conic( + /// ..color.map.viridis, + /// center: (20%, 30%), + /// )), /// ) /// ``` #[func] @@ -453,117 +429,18 @@ impl Gradient { }))) } - /// Returns the stops of this gradient. - #[func] - pub fn stops(&self) -> Vec { - match self { - Self::Linear(linear) => linear - .stops - .iter() - .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) - .collect(), - Self::Radial(radial) => radial - .stops - .iter() - .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) - .collect(), - Self::Conic(conic) => conic - .stops - .iter() - .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) - .collect(), - } - } - - /// Returns the mixing space of this gradient. - #[func] - pub fn space(&self) -> ColorSpace { - match self { - Self::Linear(linear) => linear.space, - Self::Radial(radial) => radial.space, - Self::Conic(conic) => conic.space, - } - } - - /// Returns the relative placement of this gradient. - #[func] - pub fn relative(&self) -> Smart { - match self { - Self::Linear(linear) => linear.relative, - Self::Radial(radial) => radial.relative, - Self::Conic(conic) => conic.relative, - } - } - - /// Returns the angle of this gradient. - #[func] - pub fn angle(&self) -> Option { - match self { - Self::Linear(linear) => Some(linear.angle), - Self::Radial(_) => None, - Self::Conic(conic) => Some(conic.angle), - } - } - - /// Returns the kind of this gradient. - #[func] - pub fn kind(&self) -> Func { - match self { - Self::Linear(_) => Self::linear_data().into(), - Self::Radial(_) => Self::radial_data().into(), - Self::Conic(_) => Self::conic_data().into(), - } - } - - /// Sample the gradient at a given position. - /// - /// The position is either a position along the gradient (a [ratio]($ratio) - /// between `{0%}` and `{100%}`) or an [angle]($angle). Any value outside - /// of this range will be clamped. - /// - /// _The angle will be used for conic gradients once they are available._ - #[func] - pub fn sample( - &self, - /// The position at which to sample the gradient. - t: RatioOrAngle, - ) -> Color { - let value: f64 = t.to_ratio().get(); - - match self { - Self::Linear(linear) => sample_stops(&linear.stops, linear.space, value), - Self::Radial(radial) => sample_stops(&radial.stops, radial.space, value), - Self::Conic(conic) => sample_stops(&conic.stops, conic.space, value), - } - } - - /// Samples the gradient at the given positions. - /// - /// The position is either a position along the gradient (a [ratio]($ratio) - /// between `{0%}` and `{100%}`) or an [angle]($angle). Any value outside - /// of this range will be clamped. - /// - /// _The angle will be used for conic gradients once they are available._ - #[func] - pub fn samples( - &self, - /// The positions at which to sample the gradient. - #[variadic] - ts: Vec, - ) -> Array { - ts.into_iter().map(|t| self.sample(t).into_value()).collect() - } - /// Creates a sharp version of this gradient. /// - /// _Sharp gradients_ have discreet jumps between colors, instead of a + /// Sharp gradients have discreet jumps between colors, instead of a /// smooth transition. They are particularly useful for creating color /// lists for a preset gradient. /// /// ```example + /// #set rect(width: 100%, height: 20pt) /// #let grad = gradient.linear(..color.map.rainbow) - /// #rect(width: 100%, height: 20pt, fill: grad) - /// #rect(width: 100%, height: 20pt, fill: grad.sharp(5)) + /// #rect(fill: grad) + /// #rect(fill: grad.sharp(5)) + /// #rect(fill: grad.sharp(5, smoothness: 20%)) /// ``` #[func] pub fn sharp( @@ -651,6 +528,15 @@ impl Gradient { /// Repeats this gradient a given number of times, optionally mirroring it /// at each repetition. + /// + /// ```example + /// #circle( + /// radius: 40pt, + /// fill: gradient + /// .radial(aqua, white) + /// .repeat(4), + /// ) + /// ``` #[func] pub fn repeat( &self, @@ -721,6 +607,100 @@ impl Gradient { })), }) } + + /// Returns the kind of this gradient. + #[func] + pub fn kind(&self) -> Func { + match self { + Self::Linear(_) => Self::linear_data().into(), + Self::Radial(_) => Self::radial_data().into(), + Self::Conic(_) => Self::conic_data().into(), + } + } + + /// Returns the stops of this gradient. + #[func] + pub fn stops(&self) -> Vec { + match self { + Self::Linear(linear) => linear + .stops + .iter() + .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) + .collect(), + Self::Radial(radial) => radial + .stops + .iter() + .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) + .collect(), + Self::Conic(conic) => conic + .stops + .iter() + .map(|(color, offset)| Stop { color: *color, offset: Some(*offset) }) + .collect(), + } + } + + /// Returns the mixing space of this gradient. + #[func] + pub fn space(&self) -> ColorSpace { + match self { + Self::Linear(linear) => linear.space, + Self::Radial(radial) => radial.space, + Self::Conic(conic) => conic.space, + } + } + + /// Returns the relative placement of this gradient. + #[func] + pub fn relative(&self) -> Smart { + match self { + Self::Linear(linear) => linear.relative, + Self::Radial(radial) => radial.relative, + Self::Conic(conic) => conic.relative, + } + } + + /// Returns the angle of this gradient. + #[func] + pub fn angle(&self) -> Option { + match self { + Self::Linear(linear) => Some(linear.angle), + Self::Radial(_) => None, + Self::Conic(conic) => Some(conic.angle), + } + } + + /// Sample the gradient at a given position. + /// + /// The position is either a position along the gradient (a [ratio]($ratio) + /// between `{0%}` and `{100%}`) or an [angle]($angle). Any value outside + /// of this range will be clamped. + #[func] + pub fn sample( + &self, + /// The position at which to sample the gradient. + t: RatioOrAngle, + ) -> Color { + let value: f64 = t.to_ratio().get(); + + match self { + Self::Linear(linear) => sample_stops(&linear.stops, linear.space, value), + Self::Radial(radial) => sample_stops(&radial.stops, radial.space, value), + Self::Conic(conic) => sample_stops(&conic.stops, conic.space, value), + } + } + + /// Samples the gradient at multiple positions at once and returns the + /// results as an array. + #[func] + pub fn samples( + &self, + /// The positions at which to sample the gradient. + #[variadic] + ts: Vec, + ) -> Array { + ts.into_iter().map(|t| self.sample(t).into_value()).collect() + } } impl Gradient {