lyon

repository·main·Indexed 25 days ago

https://github.com/nical/lyon

A Rust-based path tessellation library that converts complex 2D paths (such as SVG paths containing lines, quadratic beziers, and cubic beziers) into triangle meshes for efficient GPU rendering. It includes a CLI for tessellating paths, visualizing them via the `show` command, and performing transformations. The ecosystem consists of several crates: `lyon_tessellation` for filled and stroked paths, `lyon_algorithms` for vector path manipulation, `lyon_geom` for 2D geometric primitives, `lyon_path` for path data structures, and `lyon_extra` for extended functionality.

Tokens
31.8K
Snippets
58
Records
185
Agent score
82%

What's inside lyon

  1. What is Lyon?

    main
    Lyon is a Rust library designed for path tessellation, specifically optimized for GPU-based 2D graphics rendering. It allows you to convert complex 2D paths (composed of lines, quadratic beziers, and cubic beziers) into vertex and index buffers that can be uploaded to a GPU for rendering.
  2. What is Lyon and how does it work?

    main

    Lyon is a path tessellation library written in Rust designed for GPU-based 2D graphics rendering. It provides tools to turn complex, SVG-compliant paths into triangle geometry (vertex and index buffers) that can be consumed by graphics APIs like OpenGL, Vulkan, or D3D.

    Note that Lyon is not a full SVG renderer; it provides the primitives to tessellate path fills and strokes, but the user is responsible for the actual rendering logic and handling SVG file parsing.

  3. Use lyon::tessellation for 2D path tessellation

    main
    The lyon_tessellation crate (or the lyon::tessellation module within the lyon crate) provides functionality for the tessellation of filled and stroked 2D paths. This allows you to convert complex 2D paths into a set of triangles suitable for rendering in graphics APIs.
  4. Use lyon::algorithms for vector path manipulation

    main

    The lyon_algorithms crate (or the lyon::algorithms module within the main lyon crate) provides a collection of algorithms for manipulating and analyzing vector paths.

    Available algorithms include:

    • Path bounding box: Calculate the axis-aligned bounding box of a path.
    • Path area: Calculate the area enclosed by a path.
    • Path length: Calculate the total length of a path.
    • Winding numbers: Determine the winding number of a path at any given position.
    • Hatching: Generate hatching patterns for a path.
    • Path hit testing: Check if a point or shape intersects with a path.
    • Path ray casting: Perform ray casting operations against a path.
    • Walking along a path: Iterate or traverse along the segments of a path.
  5. Use lyon::extra functionalities

    main

    The lyon_extra crate provides optional extensions for the Lyon ecosystem. It includes:

    • Automatic reduced test case generation: Useful for routines that accept paths as input.
    • Extended path syntax parser: A parser designed for Lyon's extended path syntax.

    You can use lyon_extra in two ways:

    1. As a standalone crate by adding lyon_extra to your Cargo.toml.
    2. As part of the lyon crate by enabling the extra module (if supported by your version of lyon).
  6. Use lyon::path for vector graphics path data

    main

    lyon_path provides path data structures and tools specifically designed for vector graphics. It can be integrated into your project in two ways:

    1. As a standalone crate: Use lyon_path directly.
    2. As part of the Lyon ecosystem: Use the lyon::path module if you are already using the full lyon crate.
  7. Create and tessellate a path with Lyon

    main

    To use Lyon, you typically follow these steps:

    1. Build a Path: Use lyon::path::Path::builder() to define a sequence of commands like begin, line_to, quadratic_bezier_to, cubic_bezier_to, and end.
    2. Define Vertex Data: You can use the default vertex types or define your own custom vertex struct (e.g., for specific GPU layouts).
    3. Tessellate: Use a FillTessellator to process the path. You provide FillOptions and a BuffersBuilder which maps the tessellator's output (FillVertex) into your custom vertex buffers (VertexBuffers).
    4. Output: The result is a VertexBuffers object containing the vertices and indices ready for GPU upload.
    extern crate lyon;
    use lyon::math::point;
    use lyon::path::Path;
    use lyon::tessellation::*
    
    fn main() {
        // 1. Build a Path
        let mut builder = Path::builder();
        builder.begin(point(0.0, 0.0));
        builder.line_to(point(1.0, 0.0));
        builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0));
        builder.cubic_bezier_to(point(1.0, 1.0), point(0.0, 1.0), point(0.0, 0.0));
        builder.end(true);
        let path = builder.build();
    
        // 2. Define custom vertex type
        #[derive(Copy, Clone, Debug)]
        struct MyVertex { position: [f32; 2] };
    
        // 3. Prepare buffers and tessellator
        let mut geometry: VertexBuffers<MyVertex, u16> = VertexBuffers::new();
        let mut tessellator = FillTessellator::new();
    
        // 4. Compute the tessellation
        tessellator.tessellate_path(
            &path,
            &FillOptions::default(),
            &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
                MyVertex {
                    position: vertex.position().to_array(),
                }
            }),
        ).unwrap();
    
        // The tessellated geometry is ready to be uploaded to the GPU.
        println!(" -- {} vertices {} indices",
            geometry.vertices.len(),
            geometry.indices.len()
        );
    }
  8. Tessellate a path into custom vertex buffers

    main

    To use Lyon, you typically follow these steps:

    1. Build a Path: Use lyon::path::Path::builder() to define lines, quadratic beziers, and cubic beziers.
    2. Define Vertex Type: You can use the default vertex type or define your own (e.g., a struct containing [f32; 2] positions).
    3. Initialize Tessellator: Create a FillTessellator.
    4. Compute Tessellation: Use tessellate_path with a BuffersBuilder. The BuffersBuilder allows you to map Lyon's internal FillVertex data into your custom vertex format.
    5. Output: The result is stored in VertexBuffers<V, I>, which contains the vertices and indices ready for GPU upload.
    extern crate lyon;
    use lyon::math::point;
    use lyon::path::Path;
    use lyon::tessellation::*;
    
    fn main() {
        // Build a Path.
        let mut builder = Path::builder();
        builder.begin(point(0.0, 0.0));
        builder.line_to(point(1.0, 0.0));
        builder.quadratic_bezier_to(point(2.0, 0.0), point(2.0, 1.0));
        builder.cubic_bezier_to(point(1.0, 1.0), point(0.0, 1.0), point(0.0, 0.0));
        builder.end(true);
        let path = builder.build();
    
        // Let's use our own custom vertex type instead of the default one.
        #[derive(Copy, Clone, Debug)]
        struct MyVertex { position: [f32; 2] }
    
        // Will contain the result of the tessellation.
        let mut geometry: VertexBuffers<MyVertex, u16> = VertexBuffers::new();
        let mut tessellator = FillTessellator::new();
        {
            // Compute the tessellation.
            tessellator.tessellate_path(
                &path,
                &FillOptions::default(),
                &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
                    MyVertex {
                        position: vertex.position().to_array(),
                    }
                }),
            ).unwrap();
        }
    
        // The tessellated geometry is ready to be uploaded to the GPU.
        println!(" -- {} vertices {} indices",
            geometry.vertices.len(),
            geometry.indices.len()
        );
    }
  9. How PathMeasurements and PathSampler work together

    main

    Lyon provides an acceleration structure for sampling distances along a path using PathMeasurements. Building these measurements can be expensive, so you should create them once and reuse them for multiple queries.

    To perform queries, you create a PathSampler from the PathMeasurements object. The sampler can be configured to use either real distances or normalized distances (0.0 to 1.0).

    Key Differences: PathMeasurements vs. PathWalker

    • PathWalker computes everything on the fly without storing data.
    • PathMeasurements stores data to speed up random access queries.
    • PathMeasurements supports normalized distances; PathWalker does not.
    • Use PathMeasurements if you can cache the results and perform many queries.
    ```rust
    use lyon_algorithms::{
        math::point,
        path::Path,
        length::approximate_length,
        measure::{PathMeasurements, SampleType},
    };
    
    let mut path = Path::builder();
    path.begin(point(0.0, 0.0));
    path.quadratic_bezier_to(point(1.0, 1.0), point(2.0, 0.0));
    path.end(false);
    let path = path.build();
    
    // Build the acceleration structure.
    let measurements = PathMeasurements::from_path(&path, 1e-3);
    let mut sampler = measurements.create_sampler(&path, SampleType::Normalized);
    
    let sample  = sampler.sample(0.5);
    println!(
  10. Implement the `Pattern` trait for custom path walking

    main

    The Pattern trait defines how the path walker decides when to take the next step. To implement it, you must provide a next method that receives a WalkerEvent and returns an Option<f32>.

    • If next returns Some(distance), the walker will move distance units along the path before calling the method again.
    • If next returns None, path walking stops immediately.

    Additionally, you can optionally implement begin to handle the start of each sub-path, allowing you to specify a custom distance to the first step of a new sub-path.

    /// Types implementing the `Pattern` can be used to walk along a path
    /// at constant speed.
    ///
    /// At each step, the pattern receives the position, tangent and already
    /// traversed distance along the path and returns the distance until the
    /// next step.
    pub trait Pattern {
        /// This method is invoked at each step along the path.
        ///
        /// If this method returns None, path walking stops. Otherwise the returned
        /// value is the distance along the path to the next element in the pattern.
        fn next(&mut self, event: WalkerEvent) -> Option<f32>;
    
        /// Invoked at the start each sub-path.
        ///
        /// Takes the leftover requested distance from the previous sub-path path,
        /// if any.
        ///
        /// If this method returns None, path walking stops. Otherwise the returned
        /// value is the distance along the path to the next element in the pattern.
        fn begin(&mut self, distance: f32) -> Option<f32> {
            Some(distance)
        }
    }
  11. How PathBuilder and SvgPathBuilder work together

    main

    Lyon provides two primary abstractions for constructing paths:

    1. PathBuilder: A simple, efficient, and low-level interface. It requires manual management of sub-paths using begin and end methods. All coordinates are absolute. This is the preferred trait to implement when creating new path data structures.
    2. SvgPathBuilder: A higher-level interface that follows the SVG specification. It is more permissive and handles corner cases (like adding segments without an explicit begin) automatically, but at a slight runtime cost. It supports relative coordinates and SVG-specific commands (like smooth_cubic_bezier_to).

    Relationship: Any implementation of PathBuilder can be converted into an SvgPathBuilder using the .with_svg() adapter method.

    // Using PathBuilder (Low-level)
    let mut builder = Path::builder();
    builder.begin(point(0.0, 0.0));
    builder.line_to(point(1.0, 0.0));
    builder.end(false);
    
    // Using SvgPathBuilder (High-level adapter)
    let mut builder = Path::builder().with_svg();
    builder.move_to(point(0.0, 0.0));
    builder.line_to(point(1.0, 0.0));