Skip to content

Latest commit

 

History

History
202 lines (149 loc) · 7.71 KB

penplot.md

File metadata and controls

202 lines (149 loc) · 7.71 KB

📕 canvas-sketch-utilDocumentationpenplot


canvas-sketch-util/penplot

A set of utilities around pen plotting with the AxiDraw V3. This is ideally used alongside 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

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

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:

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 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().

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.

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:

// 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);

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 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.

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 ].

💡

This is a convenience function to be used alongside canvas-sketch.

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:

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:

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