perfect_freehand

Crates.ioperfect_freehand
lib.rsperfect_freehand
version0.1.0
created_at2025-12-20 15:51:01.843257+00
updated_at2025-12-20 15:51:01.843257+00
descriptionA Rust port of the perfect-freehand library for creating smooth, beautiful freehand lines
homepagehttps://github.com/sibaiper/perfect_freehand
repositoryhttps://github.com/sibaiper/perfect_freehand
max_upload_size
id1996654
size32,571
Sibai (sibaiper)

documentation

https://docs.rs/perfect_freehand

README

Perfect Freehand

A Rust port of perfect-freehand library

A library for creating smooth, natural-looking freehand strokes from input points. It produces an array of outline points that form a polygon around the input, perfect for drawing applications.

Installation

Add this to your Cargo.toml:

[dependencies]
perfect_freehand = "0.1"

Usage

The main function is get_stroke(), which takes an array of input points (like those from mouse movements) and returns a vector of outline points forming a smooth stroke polygon.

// pseudocode-ish
use perfect_freehand::{get_stroke, InputPoint, StrokeOptions};

fn main() {
    // Collect input points from user (e.g., mouse coordinates)
    let points = vec![
        InputPoint::Array([100.0, 100.0], Some(0.5)),
        InputPoint::Array([200.0, 150.0], Some(0.7)),
        InputPoint::Array([300.0, 100.0], Some(0.5)),
        // ... more points
    ];

    // Create default options
    let options = StrokeOptions::default();

    // Generate the stroke outline
    let outline = get_stroke(&points, &options);

    // Use the outline points to draw a path
    // (e.g., convert to SVG or render with a graphics library)
}

Rendering

You can render the stroke points with any graphics library. Here's an example using a hypothetical graphics backend:

use perfect_freehand::{get_stroke, InputPoint, StrokeOptions};

fn render_stroke(points: &[InputPoint]) {
    let options = StrokeOptions::default();
    let outline = get_stroke(points, &options);
    
    // Draw the polygon outline
    for i in 0..outline.len() {
        let p1 = outline[i];
        let p2 = outline[(i + 1) % outline.len()];
        draw_line(p1, p2);
    }
}

To get filled strokes, you can triangulate the polygon using a library like earcutr and then render the triangles:

use perfect_freehand::{get_stroke, InputPoint, StrokeOptions};
use earcutr::earcut;

fn render_filled_stroke(points: &[InputPoint]) {
    let options = StrokeOptions::default();
    let outline = get_stroke(points, &options);
    
    // Convert to vertex coordinates
    let mut vertices: Vec<f64> = Vec::new();
    for p in &outline {
        vertices.push(p[0]);
        vertices.push(p[1]);
    }
    
    // Triangulate
    let triangles = earcut(&vertices, &[], 2);
    
    // Draw each triangle
    for tri in triangles.chunks(3) {
        let a = outline[tri[0]];
        let b = outline[tri[1]];
        let c = outline[tri[2]];
        draw_triangle(a, b, c);
    }
}

Acknowledgements

This project is a port of perfect-freehand
by Steve Ruiz. Huge thanks to him for the original work. This Rust implementation is based on and improves upon previous Rust ports of the library.

License

This project is licensed as the original under the MIT License.


Copyright (c) 2021 Steve Ruiz - Original
Copyright (c) 2025 Sibai Eshak - Rust port

Commit count: 0

cargo fmt