### Install perfect-freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Commands to install the library using common package managers.
```bash
npm install perfect-freehand
```
```bash
yarn add perfect-freehand
```
--------------------------------
### JavaScript Example: Using getStrokePoints
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
This example demonstrates how to use the `getStrokePoints` function, which is a lower-level utility exported by the library. It takes an array of points and an optional options object, returning an array of processed points with additional properties like pressure, vector, and distance. It requires importing `getStrokePoints` and sample point data.
```javascript
import { getStrokePoints } from 'perfect-freehand'
import samplePoints from "./samplePoints.json"
const strokePoints = getStrokePoints(samplePoints)
```
--------------------------------
### Project Development Commands
Source: https://github.com/steveruizok/perfect-freehand/blob/main/CLAUDE.md
Common CLI commands for managing the perfect-freehand monorepo, including dependency installation, testing, building, and linting.
```bash
yarn
yarn start
yarn test
yarn build:packages
yarn format
```
--------------------------------
### Setup SVG Container
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Defines the HTML structure for an SVG element that covers the entire viewport, serving as the drawing surface.
```html
```
--------------------------------
### Integrate perfect-freehand with React for interactive drawing
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
This example demonstrates how to capture pointer events in a React component to generate and render freehand strokes as SVG paths. It uses a state-based approach to manage active points and completed strokes.
```tsx
import * as React from 'react'
import { getStroke } from 'perfect-freehand'
function getSvgPathFromStroke(stroke: number[][], closed = true): string {
if (!stroke.length) return ''
const d = stroke.reduce(
(acc, [x0, y0], i, arr) => {
const [x1, y1] = arr[(i + 1) % arr.length]
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2)
return acc
},
['M', ...stroke[0], 'Q']
)
d.push('Z')
return d.join(' ')
}
interface Point {
x: number
y: number
pressure: number
}
export default function DrawingCanvas() {
const [points, setPoints] = React.useState([])
const [strokes, setStrokes] = React.useState([])
const options = { size: 16, thinning: 0.5, smoothing: 0.5, streamline: 0.5, simulatePressure: true }
function handlePointerDown(e: React.PointerEvent) {
e.currentTarget.setPointerCapture(e.pointerId)
setPoints([{ x: e.pageX, y: e.pageY, pressure: e.pressure }])
}
function handlePointerMove(e: React.PointerEvent) {
if (e.buttons !== 1) return
setPoints([...points, { x: e.pageX, y: e.pageY, pressure: e.pressure }])
}
function handlePointerUp() {
if (points.length > 0) {
const stroke = getStroke(points, { ...options, last: true })
setStrokes([...strokes, getSvgPathFromStroke(stroke)])
setPoints([])
}
}
const currentStroke = points.length > 0 ? getSvgPathFromStroke(getStroke(points, options)) : ''
return (
)
}
```
--------------------------------
### Example Usage: Rendering Stroke to SVG Path (JSX)
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Demonstrates how to use the `getStroke` function (assumed to be defined elsewhere) and the `getSvgPathFromStroke` function to generate SVG path data and render it within a React component using an SVG `` element.
```jsx
const outlinePoints = getStroke(inputPoints) // Assuming getStroke is available
const pathData = getSvgPathFromStroke(outlinePoints)
```
--------------------------------
### Example Usage: Rendering Stroke to HTML Canvas Path2D (JavaScript)
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Shows how to generate SVG path data using `getStroke` and `getSvgPathFromStroke`, and then utilize this data to create a `Path2D` object for rendering on an HTML Canvas element. This approach allows for efficient drawing of complex paths on the canvas.
```javascript
const outlinePoints = getStroke(inputPoints) // Assuming getStroke is available
const pathData = getSvgPathFromStroke(outlinePoints)
const myPath = new Path2D(pathData)
// Assuming 'ctx' is a valid CanvasRenderingContext2D
// ctx.fill(myPath)
```
--------------------------------
### Render strokes on HTML Canvas
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
This example shows how to use perfect-freehand with the native HTML Canvas API. It utilizes Path2D objects to render stroke outlines generated from pointer input.
```typescript
import { getStroke } from 'perfect-freehand'
function getSvgPathFromStroke(points: number[][]): string {
if (points.length < 4) return ''
const average = (a: number, b: number) => (a + b) / 2
let [a, b, c] = [points[0], points[1], points[2]]
let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(2)},${b[1].toFixed(2)} ${average(b[0], c[0]).toFixed(2)},${average(b[1], c[1]).toFixed(2)} T`
for (let i = 2; i < points.length - 1; i++) {
const [x1, y1] = points[i]
const [x2, y2] = points[i + 1]
result += `${average(x1, x2).toFixed(2)},${average(y1, y2).toFixed(2)} `
}
return result + 'Z'
}
const canvas = document.getElementById('canvas') as HTMLCanvasElement
const ctx = canvas.getContext('2d')!
let currentPoints: [number, number, number][] = []
const completedStrokes: Path2D[] = []
canvas.addEventListener('pointerdown', (e) => {
canvas.setPointerCapture(e.pointerId)
currentPoints = [[e.offsetX, e.offsetY, e.pressure]]
})
canvas.addEventListener('pointermove', (e) => {
if (!canvas.hasPointerCapture(e.pointerId)) return
currentPoints.push([e.offsetX, e.offsetY, e.pressure])
render()
})
canvas.addEventListener('pointerup', () => {
if (currentPoints.length > 0) {
const outline = getStroke(currentPoints, { size: 12, thinning: 0.5, smoothing: 0.5, streamline: 0.5, last: true })
const pathData = getSvgPathFromStroke(outline)
completedStrokes.push(new Path2D(pathData))
currentPoints = []
render()
}
})
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = 'black'
for (const path of completedStrokes) { ctx.fill(path) }
if (currentPoints.length > 0) {
const outline = getStroke(currentPoints, { size: 12, thinning: 0.5, smoothing: 0.5, streamline: 0.5 })
const pathData = getSvgPathFromStroke(outline)
ctx.fill(new Path2D(pathData))
}
}
```
--------------------------------
### Implement interactive stroke in React
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
A complete example of capturing pointer events in a React component and rendering the resulting stroke as an SVG path.
```jsx
import * as React from 'react'
import { getStroke } from 'perfect-freehand'
import { getSvgPathFromStroke } from './utils'
export default function Example() {
const [points, setPoints] = React.useState([])
function handlePointerDown(e) {
e.target.setPointerCapture(e.pointerId)
setPoints([[e.pageX, e.pageY, e.pressure]])
}
function handlePointerMove(e) {
if (e.buttons !== 1) return
setPoints([...points, [e.pageX, e.pageY, e.pressure]])
}
const stroke = getStroke(points, {
size: 16,
thinning: 0.5,
smoothing: 0.5,
streamline: 0.5,
})
const pathData = getSvgPathFromStroke(stroke)
return (
)
}
```
--------------------------------
### Get Stroke API
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Demonstrates how to use the `getStroke` function to generate stroke path data from a set of points, with various customization options.
```APIDOC
## GET STROKE FUNCTION
### Description
Generates a stroke outline from an array of points, optionally accepting an options object to customize the stroke's appearance.
### Method
Function Call
### Endpoint
`getStroke(points, options)`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- **points** (Array<[number, number, number]> | Array<{x: number, y: number, pressure: number}>) - Required - An array of points defining the stroke. Each point can be a tuple `[x, y, pressure]` or an object `{ x, y, pressure }`.
- **options** (object) - Optional - An object to customize the stroke's appearance.
- **size** (number) - Optional - The base size (diameter) of the stroke. Defaults to 8.
- **thinning** (number) - Optional - The effect of pressure on the stroke's size. Defaults to 0.5.
- **smoothing** (number) - Optional - How much to soften the stroke's edges. Defaults to 0.5.
- **streamline** (number) - Optional - How much to streamline the stroke. Defaults to 0.5.
- **simulatePressure** (boolean) - Optional - Whether to simulate pressure based on velocity. Defaults to true.
- **easing** (function) - Optional - An easing function to apply to each point's pressure. Defaults to `t => t`.
- **start** (object) - Optional - Tapering options for the start of the line.
- **cap** (boolean) - Optional - Whether to draw a cap. Defaults to true.
- **taper** (number | boolean) - Optional - The distance to taper. If set to true, the taper will be the total length of the stroke. Defaults to 0.
- **easing** (function) - Optional - An easing function for the tapering effect. Defaults to `t => t`.
- **end** (object) - Optional - Tapering options for the end of the line.
- **cap** (boolean) - Optional - Whether to draw a cap. Defaults to true.
- **taper** (number | boolean) - Optional - The distance to taper. If set to true, the taper will be the total length of the stroke. Defaults to 0.
- **easing** (function) - Optional - An easing function for the tapering effect. Defaults to `t => t`.
- **last** (boolean) - Optional - Whether the stroke is complete. Defaults to true.
### Request Example
```javascript
getStroke(myPoints, {
size: 8,
thinning: 0.5,
smoothing: 0.5,
streamline: 0.5,
easing: (t) => t,
simulatePressure: true,
last: true,
start: {
cap: true,
taper: 0,
easing: (t) => t,
},
end: {
cap: true,
taper: 0,
easing: (t) => t,
},
})
```
### Response
#### Success Response (200)
- **Array>** - An array of points representing the outline of the stroke.
```
--------------------------------
### Get Stroke Points API
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Details on the `getStrokePoints` function, which processes input points into a format suitable for stroke generation.
```APIDOC
## GET STROKE POINTS FUNCTION
### Description
Processes an array of input points and returns a set of adjusted points with additional information like pressure, vector, and running length.
### Method
Function Call
### Endpoint
`getStrokePoints(points, options)`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- **points** (Array<[number, number, number]> | Array<{x: number, y: number, pressure: number}>) - Required - An array of points defining the stroke. Each point can be a tuple `[x, y, pressure]` or an object `{ x, y, pressure }`.
- **options** (object) - Optional - An object to customize the stroke processing. See `getStroke` options for details.
### Request Example
```javascript
import { getStrokePoints } from 'perfect-freehand'
import samplePoints from "./samplePoints.json"
const strokePoints = getStrokePoints(samplePoints)
```
### Response
#### Success Response (200)
- **Array<{ point: [number, number], pressure: number, vector: [number, number], distance: number, runningLength: number }>** - An array of processed points, each containing the point coordinates, pressure, vector, distance from the previous point, and cumulative running length.
```
--------------------------------
### Perfect Freehand Usage
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Demonstrates how to use the `getStroke` function to generate outline points from input points, with options for customization.
```APIDOC
## `getStroke` Function
### Description
Generates polygon points (a "stroke") that surround a given set of input points, simulating pressure-sensitive freehand drawing.
### Method
`getStroke(inputPoints, options?, options?)
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Parameters
#### `inputPoints`
- **Type**: `Array<[number, number]>` or `Array<{x: number, y: number, pressure?: number}>`
- **Required**: Yes
- **Description**: An array of points. Each point can be an array `[x, y]` or `[x, y, pressure]`, or an object `{x: number, y: number, pressure?: number}`. Pressure values range from 0 to 1.
#### `options` (Optional)
- **Type**: `object`
- **Description**: An object containing options to customize the stroke generation.
- **`size`** (number): The base size of the stroke. Defaults to 16.
- **`thinning`** (number): Controls how much the stroke tapers. Ranges from 0 (no thinning) to 1 (maximum thinning). Defaults to 0.5.
- **`smoothing`** (number): Controls the smoothness of the stroke. Ranges from 0 (sharp corners) to 1 (very smooth). Defaults to 0.5.
- **`simulatePressure`** (boolean): If true, pressure is simulated based on the distance between points. If false, uses the provided pressure values in `inputPoints`. Defaults to true.
- **`easing`** (function): A custom easing function for pressure simulation. Defaults to `t => t`.
- **`start`** (object): Options for the start of the stroke.
- **`cap`** (boolean): Whether to add a cap at the start. Defaults to true.
- **`size`** (number): The size of the cap. Defaults to `size`.
- **`end`** (object): Options for the end of the stroke.
- **`cap`** (boolean): Whether to add a cap at the end. Defaults to true.
- **`size`** (number): The size of the cap. Defaults to `size`.
### Request Example
```javascript
import { getStroke } from 'perfect-freehand'
const inputPoints = [
[0, 0],
[10, 5],
[20, 8],
]
const outlinePoints = getStroke(inputPoints)
// With options and real pressure
const inputPointsWithPressure = [
[0, 0, 0.5],
[10, 5, 0.7],
[20, 8, 0.8],
]
const outlinePointsWithOptions = getStroke(inputPointsWithPressure, {
size: 32,
thinning: 0.7,
simulatePressure: false,
})
```
### Response
#### Success Response (200)
- **`outlinePoints`** (Array<[number, number]>) - An array of points representing the outline of the stroke.
#### Response Example
```json
{
"outlinePoints": [
[0, 0],
[10, 5],
[20, 8],
// ... more points defining the polygon
]
}
```
```
--------------------------------
### Provide Input Points as Objects
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Demonstrates an alternative way to provide input points to the `getStroke` function using an array of objects, where each object has `x`, `y`, and optional `pressure` properties. This method is noted to have a potential performance impact.
```javascript
const inputPoints = [
{ x: 0, y: 0, pressure: 0.5 },
{ x: 10, y: 5, pressure: 0.7 },
{ x: 20, y: 8, pressure: 0.8 },
// ...
]
const outlinePoints = getStroke(inputPoints, {
simulatePressure: false,
})
```
--------------------------------
### Apply Manual Smoothing to Paths
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Demonstrates manual techniques to improve line quality by simplifying points, applying a low-pass filter, and using quadratic curves for smoother rendering.
```javascript
import { simplify } from './helpers';
points = lowPass(simplify(points));
const pathData = points.slice(1).reduce((acc, point, i, arr) => {
const next = arr[i + 1];
if (!next) return acc;
acc.push(point, [(point[0] + next[0]) / 2, (point[1] + next[1]) / 2]);
return acc;
}, ['M', points[0], 'Q']).join(' ');
```
--------------------------------
### Generate a Stroke with Custom Options
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Demonstrates how to use the getStroke function with a comprehensive options object to define size, thinning, smoothing, and tapering behavior.
```javascript
getStroke(myPoints, {
size: 8,
thinning: 0.5,
smoothing: 0.5,
streamline: 0.5,
easing: (t) => t,
simulatePressure: true,
last: true,
start: {
cap: true,
taper: 0,
easing: (t) => t,
},
end: {
cap: true,
taper: 0,
easing: (t) => t,
},
})
```
--------------------------------
### Use Real Pressure Data with getStroke
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Shows how to utilize real pressure data from input devices like pens or styluses. This involves providing pressure as the third value in input points and setting `simulatePressure` to `false`.
```javascript
const inputPoints = [
[0, 0, 0.5],
[10, 5, 0.7],
[20, 8, 0.8],
// ...
]
const outlinePoints = getStroke(inputPoints, {
simulatePressure: false,
})
```
--------------------------------
### Flatten Self-Intersecting Stroke Paths for SVG (JavaScript)
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
This function takes stroke points, flattens any self-intersecting polygons using the `polygon-clipping` library, and then converts the resulting flat polygons into SVG path data strings. It's useful for rendering strokes as solid, non-overlapping shapes. Requires the `polygon-clipping` package to be installed.
```javascript
import polygonClipping from 'polygon-clipping'
function getFlatSvgPathFromStroke(stroke) {
const faces = polygonClipping.union([stroke])
const d = []
faces.forEach((face) =>
face.forEach((points) => {
d.push(getSvgPathFromStroke(points)) // Assumes getSvgPathFromStroke is defined elsewhere
})
)
return d.join(' ')
}
```
--------------------------------
### Generate Stroke Outline Points with Perfect Freehand
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
Demonstrates how to use the getStroke function to convert raw input points into a polygon outline. It covers both array and object input formats and shows how to apply advanced configuration options for styling and behavior.
```typescript
import { getStroke } from 'perfect-freehand'
const inputPoints = [[0, 0, 0.5], [10, 5, 0.6], [20, 12, 0.7], [35, 18, 0.8], [50, 22, 0.75], [70, 25, 0.6], [90, 26, 0.5]]
const outlinePoints = getStroke(inputPoints)
const objectPoints = [{ x: 0, y: 0, pressure: 0.5 }, { x: 10, y: 5, pressure: 0.6 }, { x: 20, y: 12, pressure: 0.7 }]
const outlineFromObjects = getStroke(objectPoints)
const stroke = getStroke(inputPoints, {
size: 16,
thinning: 0.5,
smoothing: 0.5,
streamline: 0.5,
simulatePressure: true,
easing: (t) => t,
last: true,
start: { cap: true, taper: 0, easing: (t) => t * (2 - t) },
end: { cap: true, taper: 0, easing: (t) => --t * t * t + 1 }
})
```
--------------------------------
### Use object-based input points
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Providing input points as an array of objects instead of arrays, with optional pressure values.
```javascript
const inputPoints = [
{ x: 0, y: 0, pressure: 0.5 },
{ x: 10, y: 5, pressure: 0.7 },
{ x: 20, y: 8, pressure: 0.8 }
]
const outlinePoints = getStroke(inputPoints, {
simulatePressure: false,
})
```
--------------------------------
### Render SVG Path from Points
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Imports point data, generates SVG path string, creates the path element, and appends it to the DOM.
```javascript
import points from './sample.json';
const pathData = ['M', points[0], 'L', points.slice(1)].join(' ');
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', pathData);
path.setAttribute('fill', 'none');
path.setAttribute('stroke', 'black');
path.setAttribute('stroke-width', '4');
document.getElementById('svg').appendChild(path);
```
--------------------------------
### Use real pressure data
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Configuring getStroke to use actual pressure values from a stylus or pen input.
```javascript
const inputPoints = [
[0, 0, 0.5],
[10, 5, 0.7],
[20, 8, 0.8]
]
const outlinePoints = getStroke(inputPoints, {
simulatePressure: false,
})
```
--------------------------------
### Generate Stroke Outline with getStrokeOutlinePoints
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
Generates the outline polygon for a stroke from an array of stroke points. This function applies tapering, caps, and smoothing to create the final stroke shape. It takes stroke points (from `getStrokePoints`) and configuration options to customize the outline generation. Options include size, thinning, smoothing, easing, and start/end cap and taper configurations.
```typescript
import { getStrokePoints, getStrokeOutlinePoints } from 'perfect-freehand'
const inputPoints = [
[0, 0, 0.5],
[15, 8, 0.6],
[30, 20, 0.75],
[50, 28, 0.8],
[75, 32, 0.7],
[100, 33, 0.5],
]
// Step 1: Convert input to stroke points
const strokePoints = getStrokePoints(inputPoints, {
size: 20,
streamline: 0.6,
last: true,
})
// Step 2: Generate outline with custom options
const outlinePoints = getStrokeOutlinePoints(strokePoints, {
size: 20,
thinning: 0.6,
smoothing: 0.5,
simulatePressure: false, // Use actual pressure data
easing: (t) => Math.sin(t * Math.PI / 2), // Sine easing
start: {
cap: true,
taper: 20, // 20px taper at start
easing: (t) => t * t, // Quadratic ease-in
},
end: {
cap: false, // Flat end cap
taper: 0,
},
last: true,
})
// Create stroke with full-length taper (pointed ends)
const taperedOutline = getStrokeOutlinePoints(strokePoints, {
size: 20,
thinning: 0.5,
start: { taper: true }, // Taper the full stroke length
end: { taper: true }, // Pointed end
last: true,
})
// Render the outline as an SVG polygon
const polygonPoints = outlinePoints.map(([x, y]) => `${x},${y}`).join(' ')
// Use in SVG:
```
--------------------------------
### getStroke Function
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Generates a set of outline points for a polygon based on input points and optional configuration settings.
```APIDOC
## getStroke(points, options)
### Description
Generates the points for a polygon based on an array of input points, simulating pressure-sensitive strokes.
### Parameters
#### Path Parameters
- **points** (Array<[number, number, number?]> | Array<{x: number, y: number, pressure?: number}>) - Required - An array of points representing the stroke path.
#### Request Body
- **options** (Object) - Optional - Configuration object to customize the stroke appearance.
- **size** (number) - Optional - The base size of the stroke.
- **thinning** (number) - Optional - The amount of thinning applied to the stroke.
- **smoothing** (number) - Optional - The amount of smoothing applied to the stroke.
- **streamline** (number) - Optional - The amount of streamlining applied to the stroke.
- **simulatePressure** (boolean) - Optional - Whether to simulate pressure based on distance. Defaults to true.
### Request Example
const stroke = getStroke([[0, 0, 0.5], [10, 5, 0.7]], { size: 32, thinning: 0.7 });
### Response
#### Success Response (200)
- **outlinePoints** (Array<[number, number]>) - An array of points forming the polygon outline.
#### Response Example
[[0.5, 1.2], [10.2, 5.5], ...]
```
--------------------------------
### Rendering Stroke Points to SVG Path
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
This section explains how to convert the points returned by `getStroke` into SVG path data using the `getSvgPathFromStroke` function. It also shows how to use this SVG path data with HTML elements like `` or the Canvas API.
```APIDOC
## Rendering Stroke Points to SVG Path
### Description
Converts an array of stroke points into an SVG path string. This function is essential for visualizing the output of `getStroke`.
### Function
`getSvgPathFromStroke(points, closed = true)`
### Parameters
- **points** (Array>) - An array of points, where each point is an array of two numbers [x, y]. This is the output from `getStroke`.
- **closed** (boolean) - Optional. Defaults to `true`. If true, the path will be closed with a `Z` command.
### Request Example
```js
const outlinePoints = getStroke(inputPoints)
const pathData = getSvgPathFromStroke(outlinePoints)
console.log(pathData) // Output: "M10.00,10.00 Q20.00,20.00 25.00,25.00 T30.00,30.00 35.00,35.00 Z"
```
### Response
- **string** - An SVG path data string.
### Response Example
```jsx
```
### Usage with Canvas
```js
const myPath = new Path2D(pathData)
ctx.fill(myPath)
```
```
--------------------------------
### Control Stroke Thinning with Perfect-Freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Generates a stroke with adjustable thinning based on pressure. The 'thinning' option, a value between -1 and 1, controls how much pressure affects the line's width. Positive values increase thickness with pressure, negative values decrease it, and zero disables the effect.
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: 0.5,
})
```
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: 0.9,
})
```
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: -0.9,
})
```
--------------------------------
### Advanced Stroke Processing Functions
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Shows how to manually process points using getStrokePoints to calculate stroke metadata and getStrokeOutlinePoints to generate the final outline coordinates.
```javascript
import { getStrokePoints, getStrokeOutlinePoints } from 'perfect-freehand'
import samplePoints from './samplePoints.json'
const strokePoints = getStrokePoints(samplePoints)
const outlinePoints = getStrokeOutlinePoints(strokePoints)
```
--------------------------------
### Generate Stroke Path with Perfect-Freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Generates a polygon path representing a stroke from an array of input points using the getStroke function. This function takes an array of points and returns a new array of points defining a polygon that surrounds the original line, allowing for variable width based on pressure.
```javascript
const stroke = getStroke(currentPoints)
```
--------------------------------
### Define Stroke Options with TypeScript
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Illustrates how to use the StrokeOptions type definition to ensure type safety when configuring stroke parameters outside of the main function call.
```typescript
import { StrokeOptions, getStroke } from 'perfect-freehand'
const options: StrokeOptions = {
size: 16,
}
const stroke = getStroke(options)
```
--------------------------------
### Generate basic stroke points
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Basic usage of the getStroke function to convert an array of coordinate pairs into outline points.
```javascript
import { getStroke } from 'perfect-freehand'
const inputPoints = [
[0, 0],
[10, 5],
[20, 8]
]
const outlinePoints = getStroke(inputPoints)
```
--------------------------------
### Create SVG Path Data from Stroke
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Converts a Perfect-Freehand stroke (an array of points defining a polygon) into an SVG path data string. It uses 'M' for move to, 'L' for line to, and 'Z' for close path commands, with quadratic bezier curves for smoother transitions.
```javascript
const pathData = ['M', first, 'L', rest, 'Z'].join(' ')
```
```javascript
const pathData = stroke
.reduce(
(acc, [x0, y0], i, arr) => {
if (i === arr.length - 1) return acc
const [x1, y1] = arr[i + 1]
return acc.concat(` ${x0},${y0} ${(x0 + x1) / 2},${(y0 + y1) / 2}`)
},
['M ', `${stroke[0][0]},${stroke[0][1]}`, ' Q']
)
.concat('Z')
.join('')
```
--------------------------------
### getStroke
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Generates a set of points representing the outline of a pressure-sensitive stroke based on input points and configuration options.
```APIDOC
## getStroke
### Description
Calculates the outline points for a freehand stroke based on an array of input points and an optional configuration object.
### Method
Function Call
### Parameters
#### Request Body
- **points** (Array) - Required - An array of points formatted as [x, y, pressure] or {x, y, pressure}.
- **options** (Object) - Optional - Configuration object for stroke appearance.
- **size** (number) - Optional - Base diameter of the stroke (default: 8).
- **thinning** (number) - Optional - Pressure effect on size (default: 0.5).
- **smoothing** (number) - Optional - Softening of stroke edges (default: 0.5).
- **streamline** (number) - Optional - Streamlining factor (default: 0.5).
- **simulatePressure** (boolean) - Optional - Simulate pressure via velocity (default: true).
- **last** (boolean) - Optional - Whether the stroke is complete (default: true).
### Request Example
```js
getStroke(myPoints, { size: 8, thinning: 0.5 });
```
### Response
#### Success Response (200)
- **outline** (Array) - An array of [x, y] coordinates defining the stroke outline.
```
--------------------------------
### Adjust Stroke Size with Perfect-Freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Generates a stroke with a specified size using the getStroke function. The 'size' option in the second parameter controls the thickness of the generated polygon, allowing for larger or smaller strokes.
```javascript
const stroke = getStroke(sample.points, {
size: 16,
})
```
--------------------------------
### Apply Easing to Stroke Thinning
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Generates a stroke with thinning adjusted by an easing function. The 'easing' option takes a function that modifies the pressure input, allowing for finer control over how thinning is applied along the stroke.
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: -0.9,
easing: (t) => 1 - Math.cos((t * Math.PI) / 2),
})
```
--------------------------------
### Adjust Stroke Smoothing with Perfect-Freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Generates a stroke with adjustable point density along its edges using the 'smoothing' option. Lower values result in more points and potentially jagged edges, while higher values reduce the point count for a smoother, less defined appearance.
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: 0.5,
streamline: 0.5,
smoothing: 0,
})
```
```javascript
const stroke = getStroke(sample.points, {
size: 16,
thinning: 0.5,
streamline: 0.5,
smoothing: 1,
})
```
--------------------------------
### StrokeOptions Type for Customizing Strokes
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
Defines the TypeScript interface for `StrokeOptions`, which allows for detailed customization of stroke generation. This includes parameters like size, thinning, smoothing, streamline, pressure simulation, easing functions, and start/end cap and taper configurations. These options can be defined separately and reused for multiple stroke generation calls.
```typescript
import type { StrokeOptions } from 'perfect-freehand'
import { getStroke } from 'perfect-freehand'
// Define reusable stroke presets
const penOptions: StrokeOptions = {
size: 8,
thinning: 0.5,
smoothing: 0.5,
streamline: 0.5,
simulatePressure: false, // Use real stylus pressure
start: { cap: true, taper: 0 },
end: { cap: true, taper: 0 },
last: true,
}
const brushOptions: StrokeOptions = {
size: 24,
thinning: 0.7, // More pressure sensitivity
smoothing: 0.8, // Softer edges
streamline: 0.7, // More smoothing
simulatePressure: true,
easing: (t) => t * t, // Quadratic pressure response
start: { cap: true, taper: 10 },
end: { cap: true, taper: 10 },
}
const markerOptions: StrokeOptions = {
size: 16,
thinning: 0, // Constant width (no pressure effect)
smoothing: 0.3,
streamline: 0.4,
start: { cap: false }, // Flat caps
end: { cap: false },
}
const calligraphyOptions: StrokeOptions = {
size: 12,
thinning: -0.5, // Negative = thinner with more pressure
smoothing: 0.4,
streamline: 0.3,
start: { taper: true, easing: (t) => t },
end: { taper: true, easing: (t) => t },
}
// Use preset with input points
const points = [[0, 0], [10, 5], [20, 15], [30, 20]]
const penStroke = getStroke(points, penOptions)
const brushStroke = getStroke(points, brushOptions)
```
--------------------------------
### getStrokePoints & getStrokeOutlinePoints
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
Advanced utilities for processing raw points into stroke-ready data and then into outline coordinates.
```APIDOC
## getStrokePoints
### Description
Processes raw input points into an intermediate format containing vector and distance data.
## getStrokeOutlinePoints
### Description
Converts the output of getStrokePoints into the final [x, y] coordinates that define the stroke's outline.
### Request Example
```js
const points = getStrokePoints(rawPoints);
const outline = getStrokeOutlinePoints(points, options);
```
```
--------------------------------
### Generate Stroke Outline Points with Perfect Freehand
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Generates an array of points defining the outline of a pressure-sensitive stroke from input stroke points. This function is a core part of creating stroke visuals.
```javascript
import { getStrokePoints, getStrokeOutlinePoints } from 'perfect-freehand'
import samplePoints from "./samplePoints.json"
const strokePoints = getStrokePoints(samplePoints)
const outlinePoints = getStrokeOutlinePoints(strokePoints)
```
--------------------------------
### getStroke Function
Source: https://context7.com/steveruizok/perfect-freehand/llms.txt
The getStroke function processes an array of input points to generate the outline polygon of a freehand stroke, supporting various customization options for size, thinning, and tapering.
```APIDOC
## getStroke
### Description
Generates an array of outline points that form a polygon representing a pressure-sensitive stroke. It accepts raw input points and returns the calculated outline coordinates.
### Method
Function Call
### Endpoint
getStroke(points, options)
### Parameters
#### Path Parameters
- **points** (Array<[number, number, number] | {x: number, y: number, pressure: number}>) - Required - An array of points representing the stroke input.
#### Request Body
- **options** (Object) - Optional - Configuration object for stroke customization:
- **size** (number) - Base diameter of the stroke.
- **thinning** (number) - Pressure effect on size (-1 to 1).
- **smoothing** (number) - Edge softening (0 to 1).
- **streamline** (number) - Point smoothing (0 to 1).
- **simulatePressure** (boolean) - Whether to simulate pressure based on velocity.
- **easing** (function) - Pressure easing function.
- **start/end** (Object) - Configuration for stroke caps and tapering.
### Request Example
const points = [[0, 0, 0.5], [10, 5, 0.6]];
const options = { size: 16, thinning: 0.5 };
getStroke(points, options);
### Response
#### Success Response (200)
- **Array<[number, number]>** - An array of [x, y] coordinates representing the outline polygon.
#### Response Example
[[0.5, 0.2], [10.2, 5.1], [20.1, 12.3]]
```
--------------------------------
### Convert Stroke Points to SVG Path Data (JavaScript)
Source: https://github.com/steveruizok/perfect-freehand/blob/main/packages/perfect-freehand/README.md
This function converts an array of stroke points, typically generated by `getStroke`, into an SVG path data string. It handles the conversion of quadratic bezier curves and line segments into a format suitable for SVG `` elements or HTML Canvas `Path2D` objects. Ensure the input `points` array has at least 4 points for valid output.
```javascript
const average = (a, b) => (a + b) / 2
function getSvgPathFromStroke(points, closed = true) {
const len = points.length
if (len < 4) {
return ``
}
let a = points[0]
let b = points[1]
const c = points[2]
let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(
2
)},${b[1].toFixed(2)} ${average(b[0], c[0]).toFixed(2)},${average(
b[1],
c[1]
).toFixed(2)} T`
for (let i = 2, max = len - 1; i < max; i++) {
a = points[i]
b = points[i + 1]
result += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed(
2
)} `
}
if (closed) {
result += 'Z'
}
return result
}
```
--------------------------------
### Define Point Data Structure
Source: https://github.com/steveruizok/perfect-freehand/blob/main/tutorial/script.md
Represents the input data as a JSON array of coordinate pairs (x, y) captured from user input.
```json
{
"points": [
[47.671875, 179.84375],
[48.1171875, 179.8515625]
]
}
```
--------------------------------
### getStrokeOutlinePoints Function
Source: https://github.com/steveruizok/perfect-freehand/blob/main/README.md
Generates an array of points defining the outline of a pressure-sensitive stroke from an array of input points.
```APIDOC
## getStrokeOutlinePoints
### Description
A function that accepts an array of points (formatted as `{ point, pressure, vector, distance, runningLength }`, i.e. the output of `getStrokePoints`) and (optionally) an options object, and returns an array of points (`[x, y]`) defining the outline of a pressure-sensitive stroke.
### Method
`getStrokeOutlinePoints(points: Array<{ point: [number, number], pressure: number, vector: [number, number], distance: number, runningLength: number }>, options?: StrokeOptions): Array<[number, number]>`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- **points** (Array<{ point: [number, number], pressure: number, vector: [number, number], distance: number, runningLength: number }>) - Required - An array of points, typically the output of `getStrokePoints`.
- **options** (StrokeOptions) - Optional - An object to configure the stroke generation.
### Request Example
```javascript
import { getStrokePoints, getStrokeOutlinePoints } from 'perfect-freehand'
import samplePoints from "./samplePoints.json"
const strokePoints = getStrokePoints(samplePoints)
const outlinePoints = getStrokeOutlinePoints(strokePoints)
```
### Response
#### Success Response (200)
- **outlinePoints** (Array<[number, number]>) - An array of [x, y] coordinates defining the stroke outline.
#### Response Example
```json
[[10, 10], [20, 15], [30, 10]]
```
```