UNPKG

canvas-sketch-util

Version:

Utilities for sketching in Canvas, WebGL and generative art

202 lines (149 loc) 7.89 kB
#### <sup>:closed_book: [canvas-sketch-util](../README.md) → [Documentation](./README.md) → `penplot`</sup> --- ### `canvas-sketch-util/penplot` A set of utilities around pen plotting with the [AxiDraw V3](https://shop.evilmadscientist.com/productsmenu/846). This is ideally used alongside [canvas-sketch](https://github.com/mattdesl/canvas-sketch) CLI tools for exporting SVG files. This tool allows you to create arbitrary "path" instances using familiar Canvas2D APIs, and then serialize these as a complete SVG that should be plottable. In this utility, a "path" can be a polyline, Path instance, or SVGPath string. ### Example ```js const { pathsToSVG, createPath } = require('canvas-sketch-util/penplot'); // You can create a Path serializer like so const path0 = createPath(context => { context.moveTo(25, 50); context.lineTo(10, 10); context.arc(52, 50, 25, 0, Math.PI * 2); }); // You can also append commands onto the path // path.lineTo(50, 50); // And/or you can use manual polylines like so const path1 = [ [ 0, 0 ], [ 50, 25 ], [ 25, 50 ] ]; // Generate a SVG file as a string // Accepts a single or multiple (potentially nested) "path" interfaces // A "path" can be a polyline, SVGPath string, or Path object from createPath const svg = pathsToSVG([ path0, path1 ], { width: 2, height: 2, units: 'cm', lineWidth: 0.04, // optimize the SVG output for pen plotter use optimize: true }); ``` ### Functions - [createPath](#createPath) - [pathsToPolylines](#pathsToPolylines) - [pathsToSVG](#pathsToSVG) - [renderPaths](#renderPaths) <a name="createPath"></a> ### `path = createPath([fn])` Creates a new *Path* serializer which can be used for drawing lines, arcs, rectangles and shapes with Canvas2D-style functions. If you specify a `fn`, it will be called with the path as the argument before returning. For example: ```js const path = createPath(context => { // Circle in centre of page context.arc(width / 2, height / 2, 25, 0, Math.PI * 2); }); // Get a SVG string of the path const svg = path.toString(); ``` The *Path* interface has the following drawing functions, see [d3-path API](https://www.npmjs.com/package/d3-path#api-reference) for details. The drawing functions match those in Canvas2D contexts. - `moveTo(x, y)` - `lineTo(x, y)` - `quadraticCurveTo(cpx, cpy, x, y)` - `bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x, y)` - `arcTo(x1, y1, x2, y2, radius)` - `arc(x, y, radius, startAngle, endAngle[, anticlockwise])` - `closePath()` - `rect(x, y, width, height)` To get the SVGPath string, use `path.toString()`. <a name="pathsToPolylines"></a> ### `polylines = pathsToPolylines(paths, opt)` Converts a single or multiple (potentially listed) array of 'paths' (Path objects, SVGPath strings, or polylines) into a flat 1-dimensional list of polyline contours. This is done by converting all SVGPath strings into cubic bezier arcs, and then subdividing them into discrete lists of points. ```js const inputs = /* .. path, polylines, etc .. */; pathsToPolylines(inputs).forEach(contour => { context.beginPath(); contour.forEach(point => { context.lineTo(point[0], point[1]); }); context.stroke(); }); ``` You can specify `{ curveResolution }` option (a number) to adjust the smoothness when converting SVG paths into discrete polyline lists. The number is the inverse of the distance threshold to further subdivide curves, a higher resolution leads to more subdivisions. By default, a reasonable default will be selected from your `{ units }` option (4 units converted to pixels at 96 DPI). For example: ```js // Use a resolution of 1 pathsToPolylines(inputs, { curveResolution: 1 }); // Choose a reasonable default resolution based on units pathsToPolylines(inputs, { units: 'cm' }); // No options specified, will default to a resolution of 4 pathsToPolylines(inputs); ``` <a name="pathsToSVG"></a> ### `svg = pathsToSVG(paths, opt)` Generates a physically-sized SVG file as a string from the given list of `paths` with the specified options in `opt`. The `paths` can be a single or multiple nested path instances, such as Path objects from `createPath`, or SVGPath strings, or polylines (nested 2D points using arrays). Options: - `units` (defaults to `'px'`) a unit string like `'cm'`, `'px'`, `'in'`, etc - `width` (required) the width of the resulting output in `units` - `height` (required) the height of the resulting output in `units` - `lineWidth` (defaults to 0.03 cm in user `units`) the line width of strokes - `strokeStyle` (defaults to `'black'`) the color of the strokes - `precision` (defaults to 5) the decimal precision for floating point numbers as they are converted to strings - `fillStyle` (defaults to `'none'`) the fill style of SVG path elements - `curveResolution` (defaults to a reasonable smoothness) the resolution when converting SVG paths into discrete arcs, see [pathsToPolylines](#pathsToPolylines) for details - `optimize` (defaults to false) if true, enables path sorting for optimal traversal distance and merging end-points to reduce pen lifts. Can also specify an object instead of a boolean: - `optimize.sort` (default true) – can disable distance sorting - `optimize.removeDuplicates` (default true) – remove duplicaet adjacent points (before merging) - `optimize.removeCollinear` (default true) – remove unnecessary collinear adjacent points within lines - `optimize.merge` (default true) – can disable end point merging - `optimize.mergeThreshold` (default 0.25 mm in your units) – adjsut the distance threshold at which to merge end points Returns a string of the SVG file. The SVG is formatted in such a way that it can be easily opened and exported to AxiDraw V3 with Inkscape. <a name="renderPaths"></a> ### `layers = renderPaths(lines, props)` Renders the specified list of `lines` (each containing an array of 2D coordinates) using the specified `props` (expected to be from `canvas-sketch`), returning an array of renderable layers: `[ canvas, svgOutput ]`. > :bulb: > > <sup>This is a convenience function to be used alongside `canvas-sketch`.</sup> This will render the lines as 2D paths into the canvas context given by `props`, and then convert them into a SVG file. The return value is a list of layers that can be used for exporting both a PNG and SVG file with `canvas-sketch-cli`. Example: ```js const canvasSketch = require('canvas-sketch'); const { createPath, renderPaths } = require('canvas-sketch-util/penplot'); const settings = { dimensions: 'A4', pixelsPerInch: 300, units: 'cm', }; const sketch = ({ width, height }) => { // Create shapes with path interface const shape0 = createPath(ctx => ctx.arc(0, 0, 50, 0, Math.PI * 2)); // And/or with polylines or plain SVGStrings, e.g. from a .svg file const shape1 = [ [ 0, 0 ], [ 50, 25 ] ]; // Combine into an array or nested array const paths = [ shape0, shape1 ]; // Export both PNG and SVG files on 'Cmd + S' return props => renderPaths(paths, props); }; canvasSketch(sketch, settings); ``` You can also override settings such as `lineWidth` or `strokeStyle` if you want a different SVG output: ```js const sketch = ({ width, height }) => { // ... return props => renderPaths(paths, { ...props, lineWidth: 0.05 }); }; ``` Full list of expected props: - `context` (required) The canvas context - `units` The units of the artwork - `width` (required) the width of the artwork in `units` - `height` (required) the width of the artwork in `units` - `background` The background `fillStyle` for 2D canvas, default `'white'` - `foreground` The foreground `strokeStyle` applied only to the 2D canvas, defaults to `'black'` (use this if you wish to have a white stroke on black PNG, but still a black stroke SVG) - Other properties passed into `pathsToSVG` function ## #### <sup>[← Back to Documentation](./README.md)