<p align="center">
<br/>
<a target="_blank"><img style="border-radius: 40px;" width="256px" src="assets/card.png" /></a>
<p align="center">Rust Hand Drawn Path Generator</p>
<p align="center" style="align: center;">
<a href="https://crates.io/crates/freedraw"><img src="https://shields.io/badge/Crates-FFC933?logo=Rust&logoColor=646464&style=round-square" alt="Crates" /></a>
<a href="https://github.com/ducflair/freedraw/releases"><img src="https://img.shields.io/crates/v/freedraw?style=round-square&label=latest%20stable" alt="Crates.io freedraw@latest release" /></a>
<a href="https://crates.io/crates/freedraw"><img src="https://img.shields.io/crates/d/freedraw?style=round-square&color=salmon" alt="Downloads" /></a>
<img src="https://shields.io/badge/Rust-CE412B?logo=Rust&logoColor=fff&style=round-square" alt="Rust" />
</p>
</p>
# freedraw
A Rust port of the [perfect-freehand](https://github.com/steveruizok/perfect-freehand) library, designed for creating smooth and beautiful freehand drawings.
<p align="center">
<img src="./assets/process.gif" alt="A GIF showing a stroke with input points, outline points, and a curved path" width="400"/>
</p>
## Overview
freedraw helps you create high-quality freehand strokes in Rust applications. It generates smooth, beautiful outlines from a series of input points, supporting pressure sensitivity and customizable styling.
## Features
- Create beautiful, smooth freehand strokes
- Pressure-sensitive drawing with natural tapering
- Customizable stroke properties (size, smoothing, thinning)
- Simulate pressure based on velocity
- Generate SVG paths from strokes
- Built as a lightweight, pure Rust implementation
## Installation
Add the dependency to your Cargo.toml:
```toml
[dependencies]
freedraw = "0.1.0"
```
## Basic Usage
```rust
use freedraw::{get_stroke, InputPoint, StrokeOptions};
fn main() {
// Create some example points
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)),
];
// 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)
}
```
## Converting to SVG Paths
The library includes a utility to convert stroke outlines to SVG path data:
```rust
use freedraw::{get_stroke, get_svg_path_from_stroke, InputPoint, StrokeOptions};
// Generate the stroke outline
let outline = get_stroke(&points, &options);
// Convert to SVG path data
let path_data = get_svg_path_from_stroke(&outline, true); // true = closed path
// Use in an SVG element
println!("<path d=\"{}\" fill=\"black\" />", path_data);
```
## Options
You can customize the appearance of strokes using the `StrokeOptions` struct:
```rust
let options = StrokeOptions {
size: Some(16.0), // Base size (diameter) of the stroke
thinning: Some(0.5), // Effect of pressure on stroke width
smoothing: Some(0.5), // How much to soften the stroke's edges
streamline: Some(0.5), // How much to streamline the stroke
simulate_pressure: Some(true), // Whether to simulate pressure based on velocity
start: Some(TaperOptions { // Tapering options for the start of the line
taper: Some(TaperType::Bool(true)),
..Default::default()
}),
end: Some(TaperOptions { // Tapering options for the end of the line
taper: Some(TaperType::Bool(true)),
..Default::default()
}),
..Default::default()
};
```
### Option Details
| `size` | number | 8 | The base size (diameter) of the stroke. |
| `thinning` | number | .5 | The effect of pressure on the stroke's size. |
| `smoothing` | number | .5 | How much to soften the stroke's edges. |
| `streamline` | number | .5 | How much to streamline the stroke. |
| `simulatePressure` | boolean | true | Whether to simulate pressure based on velocity. |
| `last` | boolean | true | Whether the stroke is complete. |
The `start` and `end` options accept a `TaperOptions` struct:
| `cap` | boolean | true | Whether to draw a cap. |
| `taper` | TaperType | None | The distance to taper. Can be a numerical value or boolean. |
| `easing` | EasingType | linear | An easing function for the tapering effect. |
## Input Points
The library supports two formats for input points:
1. Array format: `InputPoint::Array([x, y], pressure)` where pressure is optional
2. Struct format: `InputPoint::Struct { x, y, pressure }` where pressure is optional
If pressure is not provided, it defaults to 0.5.
## Advanced Usage
For advanced usage, the library exports smaller functions that `get_stroke` uses internally:
```rust
// Get detailed stroke points with pressure, vector, distance, etc.
let stroke_points = get_stroke_points(&input_points, &options);
// Generate outline points from the detailed stroke points
let outline_points = get_stroke_outline_points(&stroke_points, &options);
```
## Examples
Check out the examples directory for more usage examples:
- SVG path generation
- Different stroke styles and parameters
- Edge cases (one point, two points)
- HTML viewer for comparing different options
## License
MIT License
Original JavaScript library by [Steve Ruiz](https://twitter.com/steveruizok)