### 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 ( {strokes.map((d, i) => )} {currentStroke && } ) } ``` -------------------------------- ### 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 ( {points && } ) } ``` -------------------------------- ### 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]] ``` ```