lyon
repository·main·Indexed 25 days ago
https://github.com/nical/lyonA 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.
What's inside lyon
- 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.
What is Lyon and how does it work?
mainLyon 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.
Use lyon::tessellation for 2D path tessellation
mainThelyon_tessellationcrate (or thelyon::tessellationmodule within thelyoncrate) 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.Use lyon::geom for 2D geometric primitives
mainlyon_geomprovides 2D geometric primitives built on top of the euclid crate.You can use these primitives in two ways:
- As a standalone crate:
lyon_geom. - As part of the main
lyonproject via thelyon::geommodule.
- As a standalone crate:
Use lyon::algorithms for vector path manipulation
mainThe
lyon_algorithmscrate (or thelyon::algorithmsmodule within the mainlyoncrate) 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.
Use lyon::extra functionalities
mainThe
lyon_extracrate 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_extrain two ways:- As a standalone crate by adding
lyon_extrato yourCargo.toml. - As part of the
lyoncrate by enabling theextramodule (if supported by your version oflyon).
Use lyon::path for vector graphics path data
mainlyon_pathprovides path data structures and tools specifically designed for vector graphics. It can be integrated into your project in two ways:- As a standalone crate: Use
lyon_pathdirectly. - As part of the Lyon ecosystem: Use the
lyon::pathmodule if you are already using the fulllyoncrate.
- As a standalone crate: Use
Create and tessellate a path with Lyon
mainTo use Lyon, you typically follow these steps:
- Build a Path: Use
lyon::path::Path::builder()to define a sequence of commands likebegin,line_to,quadratic_bezier_to,cubic_bezier_to, andend. - Define Vertex Data: You can use the default vertex types or define your own custom vertex struct (e.g., for specific GPU layouts).
- Tessellate: Use a
FillTessellatorto process the path. You provideFillOptionsand aBuffersBuilderwhich maps the tessellator's output (FillVertex) into your custom vertex buffers (VertexBuffers). - Output: The result is a
VertexBuffersobject 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() ); }- Build a Path: Use
Tessellate a path into custom vertex buffers
mainTo use Lyon, you typically follow these steps:
- Build a Path: Use
lyon::path::Path::builder()to define lines, quadratic beziers, and cubic beziers. - Define Vertex Type: You can use the default vertex type or define your own (e.g., a struct containing
[f32; 2]positions). - Initialize Tessellator: Create a
FillTessellator. - Compute Tessellation: Use
tessellate_pathwith aBuffersBuilder. TheBuffersBuilderallows you to map Lyon's internalFillVertexdata into your custom vertex format. - 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() ); }- Build a Path: Use
How PathMeasurements and PathSampler work together
mainLyon 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
PathSamplerfrom thePathMeasurementsobject. The sampler can be configured to use either real distances or normalized distances (0.0 to 1.0).Key Differences: PathMeasurements vs. PathWalker
PathWalkercomputes everything on the fly without storing data.PathMeasurementsstores data to speed up random access queries.PathMeasurementssupports normalized distances;PathWalkerdoes not.- Use
PathMeasurementsif 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!(Implement the `Pattern` trait for custom path walking
mainThe
Patterntrait defines how the path walker decides when to take the next step. To implement it, you must provide anextmethod that receives aWalkerEventand returns anOption<f32>.- If
nextreturnsSome(distance), the walker will movedistanceunits along the path before calling the method again. - If
nextreturnsNone, path walking stops immediately.
Additionally, you can optionally implement
beginto 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) } }- If
How PathBuilder and SvgPathBuilder work together
mainLyon provides two primary abstractions for constructing paths:
PathBuilder: A simple, efficient, and low-level interface. It requires manual management of sub-paths usingbeginandendmethods. All coordinates are absolute. This is the preferred trait to implement when creating new path data structures.SvgPathBuilder: A higher-level interface that follows the SVG specification. It is more permissive and handles corner cases (like adding segments without an explicitbegin) automatically, but at a slight runtime cost. It supports relative coordinates and SVG-specific commands (likesmooth_cubic_bezier_to).
Relationship: Any implementation of
PathBuildercan be converted into anSvgPathBuilderusing 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));