MISCELLANEOUS TECHNICAL ARTICLES BY Dr A R COLLINS

PathSVG User Guide

PathSVG object

The PathSVGUtils module supplies the global PathSVG which is an Array subclass that takes SVG path data and provides methods such as translate, rotate etc to transform the path, and methods to append or join PathSVG. It also has methods to convert the path data to string or array formats. All these methods return a PathSVG array so methods may be chained.

Static method of the PathSVG object return the outline path of predefined common shapes; circle, square, arc etc.

The source code is available at PathSVGUtils-3v05.js and the minified version at PathSVGUtils-3v05-min.js

New in Version 3

The module also provides two new static methods PathSVG.splineFit and PathSVG.polyline. Both methods take an array of points and return a PathSVG path comprising cubic Bezier curves or straight line sections fitted through them.

PathSVG Constructor

Syntax:

const pathData = new PathSVG(data);

Description:

The PathSVG constructor uses the SVG-2 SVGPathElement.getPathData method to parse an array or string of SVG path data into a array of objects each defining a segment of the path. The data format for each segment is that SVG 2 SVGPathSegment format ie. {type:char, values:[...]}. This array of segments data defining the full path the format returned by the SVG 2 path element path.getPathData({normalize:true}) method. Setting 'normalize' true forces the command set used in the conversion to segments to be restricted to "M", "L", "C" and "Z" and the coordinates to be all absolute (reference to 0,0) not relative to the previous point's coordinates.

Parameters:

data: String or Array - holding a series of commands and their associated parameters. The full set of SVG path data commands is supported (see SVG syntax).

Note: If the path has been drawn in a vector graphics editor such as Inkscape®. the path data string may be copied from the XML edit window and pasted as the constructor argument.

Returns:

PathSVG object - An array of objects each defining a segment of the path by a single command 'type' and an array of coordinate 'values'. The Array object returned has the appendPath, dup, joinPath, translate, rotate, etc. methods added for further manipulation of the path data.

Example

The following lines of code will create identical PathSVG objects.

var path1 = new PathSVG("M 8.24,-0.1 l 1.2,0.2 0,1.6 -1.2,0.2 A 8.3,8.3 0 0 1 4.9,6.6");
var path2 = new PathSVG(["M",8.24,-0.1,"l",1.2,0.2,0,1.6,-1.2,0.2,"A",8.3,8.3,0,0,1,4.9,6.6]);

console.log(path1);  // [{type:'M', values:[8.24,-0.1]}, {type:'L', values:[10.44,0.1]}, ... ]
        

PathSVG Methods

appendPath

Syntax:

const newPth = pth1.appendPath(pth2);

Description:

The appendPath method extends pth1 segments array by appending the pth2 segments. If the 'pth2' is not type PathSVG, it is converted to a PathSVG object. Since the pth2 commands must start with a 'M' (move) command the returned path is a copy of the original with a non-contiguous section added.

Parameters:

pth2: Array, String or PathSVG object - The SVG path data defining a path to be appended to the 'pth1' path.

Returns:

PathSVG object - A new PathSVG object, the original paths 'pth1' and 'pth2' remain unchanged.

dup

Syntax:

const newPth = pth.dup();

Description:

The dup method creates a new PathSVG object, copying the segments array and all the methods from the original into the new object. This method is useful in creating complex objects made from similarly shaped components.

Parameters:

none.

Returns:

PathSVG object - A new PathSVG object which is a deep copy or the calling path.

joinPath

Syntax:

const newPth = pth1.joinPath(pth2);

Description:

The joinPath method extends pth1.segments array by adding the pth2 segments. If the 'pth2' is not type PathSVG, it is converted to a PathSVG object. The initial 'M' (move) command segment is deleted so that the returned path is continuous and extended.

Parameters:

pth2: Array or String or PathSVG - The SVG command data defining a path to be joined to pth1.

Returns:

PathSVG object - A new PathSVG object, the original paths 'pth1' and 'pth2' remain unchanged.

revWinding

Syntax:

const newPth = pth.revWinding();

Description:

The revWinding method creates a PathSVG object tracing the same path as the original the path segments and coordinates are rearranged to be traversed in reverse order. This method is useful when creating complex objects made from similarly shaped components with point or line symmetry and for creating holes in Shapes when filled by 'nonzero' fill rule.

Parameters:

none.

Returns:

PathSVG object - A new PathSVG object, the original 'pth' object remains unchanged.

rotate

Syntax:

const newPth = pth.rotate(degs);

Description:

The rotate method creates a copy of the path and rotates all the segment coordinates by 'degs' degrees centered on the drawing origin (0,0).

Parameters:

degs: Number - The angle of rotation measured in degrees counter-clockwise for Right Hand Cartesian coordinate systems and clockwise for Left Handed systems such as SVG and native canvas methods.

Returns:

PathSVG object - A new PathSVG object, the original path 'pth' remains unchanged.

scale

Syntax:

const newPth = pth.scale(xScale[, yScale]);

Description:

The scale method creates a copy of the path and multiplies all path segments' X coordinates by 'xScale' and Y coordinates by 'yScale' (if specified). If 'yScale' is undefined then 'xScale' value is used to scale the Y dimensions. The scaling is centered on the drawing origin (0,0).

Parameters:

xScale: Number - A number by which the object's X dimensions are scaled, negative numbers will flip the object horizontally.

yScale: Number - (optional) A number by which the object's Y dimensions are scaled, negative numbers will flip the object vertically.

Returns:

PathSVG object - A new PathSVG object, the original path 'pth' remains unchanged.

skew

Syntax:

const newPth = pth.skew(degH, degV);

Description:

The skew method creates a copy of the path and skews all path segments' X coordinates by an angle of 'degH' from the vertical axis leaving, and skews the Y coordinates by an angle of 'degV' from the horizontal axis.

Parameters:

degH, degV: Numbers - Skew angles measured in degrees counter-clockwise for RHC coordinate systems and clockwise for SVG coordinate systems.

Returns:

PathSVG object - A new PathSVG object, the original path 'pth' remains unchanged.

translate

Syntax:

const newPth = pth.translate(x, y);

Description:

The translate method creates a copy of the path and adds the values x and y to all the path x and y coordinates respectively. Effectively moving the new path by distances x and y from the drawing origin (0, 0).

Parameters:

x, y: Numbers - X and Y offsets to be added to the object coordinates.

Returns:

PathSVG object - A new PathSVG object, the original path 'pth' remains unchanged.

toString

Syntax:

const svgStr = pth.toString(sigFigs);

Description:

The toString method converts the PathSVG segments to a string with the SVG command segments separated by spaces and the segments coordinate pairs separated by commas. All floating point numbers are rounded are converted to strings using the Math.toPrecision method.

Parameters:

sigFigs: Number - The number of significant figures used when converting the coordinates to strings. The default value is 3.

Returns:

String - A string of SVG command letters and associated coordinates.

Example

If 'pth' is a PathSVG with the following segments:

          ['M', 8.24, -0.1], 
          ['L', 9.44, 0.1], 
          ['L', 9.44, 1.7], 
          ['L', 8.24, 1.9], 
          ['C', 7.724, 3.815, 6.539, 5.483, 4.9, 6.6]
        

then the pth.toString method will return the string:

console.log(pth.toString(2); // "M8.2,-0.10 L9.4,0.10 L9.4,1.7 L8.2,1.9 C7.7,3.8 6.5,5.5 4.9,6.6"
        
getTotalLength

Syntax:

const len = pth.getTotalLength();

Description:

The getTotalLength method calculates the total length of the PathSVG measured in the coordinates used to define the path segments. If the path is comprised of discontinuous sections the gaps do not contribute to the total length.

Parameters:

none.

Returns:

Number - The total path length.

getPointAtLength

Syntax:

const coordObj = pth.getPointAtLength(len);

Description:

The getPointAtLength method calculates the coordinates of the point on the path at a distance 'len' from the start of the path.

Parameters:

len: Number - A number representing the distance along the path measured in the coordinates used to define the path segments.

Returns:

Object - An object, {x: , y: }, with properties "x" and "y" representing the X and Y coordinates of the point along the path.

Static methods

Syntax:

const cir = PathSVG.circle(5); // 'cir' is a PathSVG object

arc

Syntax:

const shapeData = PathSVG.arc(radius, startAngle, endAngle, anticlockwise);

Description:

This method returns a PathSVG object defining the outline of a circular arc with radius 'radius'. The center of the arc will be the origin (0,0) and will start at angle 'startAngle' and end at 'endAngle'. The direction of sweep is set by the 'anticlockwise' argument.

Parameters:

radius: Number - The circular arc radius.

startAngle: Number - The angle from which the arc starts its sweep, measured in degrees.

endAngle: Number - The angle from which the arc ends its sweep, measured in degrees.

anticlockwise: Boolean - If true, the direction of arc sweep is anticlockwise, else it is clockwise.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the arc.

arcTo

Syntax:

const shapeData = PathSVG.arcTo(x0, y0, x1, y1, x2, y2, radius);

Description:

This method behaves like the Path2D.arcTo method designed, to generate rounded corners on straight sided shapes. Given the three points (x0,y0) (x1,y1) and (x2,y2) forming a vertex at (x1,y1), it will generate the circular arc tangent to both lines forming the rounded corner. It then returns a PathSVG object with straight lines from (x0,y0) to the tangent of the arc then continues with the straight line from the arc's other tangent to the point (x2,y2). See example below:

Parameters:

x0, y0: Numbers - The coordinates of the start point.

x1, y1: Numbers - The coordinates of the vertex.

x2, y2: Numbers - The coordinates of the end point.

radius: Number - The circular arc radius that joins the two lines forming the corner.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the two lines and the arc joining them.

Example

Here is the source code demonstrating the PathSVG.arcTo static method. Fig 1 shows the result.

Figure 1. PathSVG.arcTo example.

function arcToTest(cvsID)
{
  const g = new Cango(cvsID)
  g.clearCanvas();
  g.setWorldCoordsRHC(0, 0, 200);

  const p0 = {x:100, y:200}, 
        p1 = {x:50,  y:50}, 
        p2 = {x:125, y:20},
        r = 50;
  g.drawPath(["M", p0.x,p0.y, "L", p1.x,p1.y, p2.x,p2.y], {});
  g.drawText("p0", {x:p0.x, y:p0.y, lorg:8});
  g.drawText("p1", {x:p1.x, y:p1.y, lorg:3});
  g.drawText("p2", {x:p2.x, y:p2.y, lorg:1});

  const test1 = PathSVG.arcTo(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, r);
  g.drawPath(test1, {strokeColor:"red", lineWidth:2});
}

Note:

To put rounded corners on a polygon, calculate the mid point of each side by taking the mean of the end points coordinates and and generate the set of rounded corners from the midpoint-vertex-midpoint triplets. Join these using PathSVG.joinPath method to form the closed outline.

Example

Here is an demonstrating the PathSVG.arcTo static method used to round the corners of a polygon. Fig 2 shows the result.

Figure 2. PathSVG.arcTo example.

...

const p0 = {x:100, y:275},
      p1 = {x:260, y:50},
      p2 = {x:50, y:50},
      r = 25;

g.drawPath(["M", p0.x,p0.y, "L", p1.x,p1.y, p2.x,p2.y, p0.x,p0.y]);
g.drawText("p0", {x:p0.x, y:p0.y, lorg:8});
g.drawText("p1", {x:p1.x, y:p1.y, lorg:2});
g.drawText("p2", {x:p2.x, y:p2.y, lorg:3});

// find the mid points of the sides
const m01 = {x:(p1.x+p0.x)/2, y:(p1.y+p0.y)/2},
      m12 = {x:(p2.x+p1.x)/2, y:(p2.y+p1.y)/2},
      m20 = {x:(p0.x+p2.x)/2, y:(p0.y+p2.y)/2};

const pth1 = PathSVG.arcTo(m01.x, m01.y, p1.x, p1.y, m12.x, m12.y, r)
  .joinPath(PathSVG.arcTo(m12.x, m12.y, p2.x, p1.y, m20.x, m20.y, r))
  .joinPath(PathSVG.arcTo(m20.x, m20.y, p0.x, p0.y, m01.x, m01.y, r));

g.drawPath(pth1, {lineWidth:2, strokeColor:"green"});
circle

Syntax:

const shapeData = PathSVG.circle(d);

Description:

This method returns a PathSVG object defining the outline of a circle with diameter 'd'. The drawing origin will be at the center of the circle.

Parameters:

d: Number - Diameter of the circle measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the circle.

cross

Syntax:

const pathData = PathSVG.cross(w[, h]);

Description:

This method returns a PathSVG object defining the outline of a cross with arms of width 'w' and height 'h'. The drawing origin will be at the center of the cross.

Parameters:

w: Number - The length of the horizontal arm of the cross, measured in the world coordinates. If 'h' is not specified then the vertical arm of the cross is draw equal to the horizontal.

h: Number - (optional) The length of the vertical arms of the cross, measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the cross.

ellipse

Syntax:

const shapeData = PathSVG.ellipse(w, h);

Description:

This method returns a PathSVG object defining the outline of an ellipse with width (X axis) 'w' and height (Y axis) 'h'. The drawing origin will be at the center of the ellipse.

Parameters:

w: Number - The X axis width of the ellipse measured in the world coordinates.

h: Number - The Y axis height of the ellipse measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the ellipse.

ellipticalArc

Syntax:

const shapeData = PathSVG.ellipticalArc(radiusX, radiusY, startAngle, endAngle, anticlockwise);

Description:

This method returns a PathSVG object defining the outline of an elliptical arc with major X axis radius 'radiusX' and the Y axis radius 'radiusY'. The center of the ellipse will be the origin (0,0) and will start at angle 'startAngle' and end at 'endAngle'. The direction of sweep is set by the boolean 'anticlockwise' argument.

Parameters:

radiusX, radiusY: Numbers - The half length of the X and Y axes of the ellipse.

startAngle: Number - The angle from which the arc starts its sweep, measured in degrees.

endAngle: Number - The angle from which the arc ends its sweep, measured in degrees.

anticlockwise: Boolean - If true, the direction of arc sweep is anticlockwise, if false (or undefined) it is clockwise. The default value is false.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the elliptical arc.

ex

Syntax:

const pathData = PathSVG.ex(s);

Description:

This method returns a PathSVG object defining the outline of an X with arm lengths 's'. The drawing origin will be at the center of the ex.

Parameters:

s: Number - The length of each arm of the ex, measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the ex.

rectangle

Syntax:

const shapeData = PathSVG.rectangle(w, h[, rad]);

Description:

This method returns a PathSVG object defining the outline of a rectangle with width 'w' and height 'h'. The rectangle may optionally have its corners rounded with a user defined radius. The drawing origin will be at the center of the rectangle.

Parameters:

w: Number - The width (X dimension) of the rectangle, measured in the world coordinates.

h: Number - The height (Y dimension) of the rectangle, measured in the world coordinates.

rad: Number - The radius of rounded corners applied to the rectangle, measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the rectangle.

square

Syntax:

const shapeData = PathSVG.square(s);

Description:

This method returns a PathSVG object defining the outline of a square with sides of length 's'. The drawing origin will be at the center of the square.

Parameters:

s: Number - Side length of the square measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the square.

triangle

Syntax:

const shapeData = PathSVG.triangle(s);

Description:

This method returns a PathSVG object defining the outline of an equilateral triangle with sides of length 's'. The drawing origin will be at the center of the triangle.

s: Number - The length of each side of the triangle measured in the world coordinates.

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining the outline of the triangle.

splineFit

Syntax:

const pathData = PathSVG.splineFit(data [, yData]);

Description:

The splineFit function takes an array of 2D data points and fits a series of spline curves through them. The method used was written by Sergey Khokhlov. These splines are then converted to cubic Bezier curves and the resulting path returned as a PathSVG object.

Parameters:

The coordinates of the data points may be in any of three formats:

data: Array - An array of x, y coordinate pairs: [x0, y0, x1, y1, ...]

OR

data, yData: Two Arrays - The first an array of the X coordinate values and the second an array of the Y coordinate values: : [x0, x1, x2, ...], [y0, y1, y2 ...]

OR

data: Array - Array of objects with x and y properties holding the coordinate pairs: [{x:x0, y:y0}, {x:x1, y:y1}, ...]

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining a smooth path through the data points.

Example:

Fig 3 shows an example of a splineFit to a set of data contained in an array of X coordinates and a second array of Y coordinates. The snippet of source cose is show, the full source is available in the source code of this web page.

Figure 3. Example of a splineFit to an array of data points.

    ...
const testXs = [5, 10, 40, 60, 90,120,150,200];
const testYs = [10, 40, 30,  5, 45, 10, 45, 10];

// draw the smooth curve through the data points
const splnPth = PathSVG.splineFit(testXs, testYs);
g.drawPath(splnPth, {strokeColor:"blue"});
    
polyline

Syntax:

const pathData = PathSVG.polyline(data [, yData]);

Description:

The polyline method takes an array of 2D data points and fits a series of straight line segments between them. These are then converted to SVG line segments and the resulting path returned as a PathSVG object. The data points may be in any one of several formats:

Parameters:

The coordinates of the data points may be in any of three formats:

data: Array - An array of x, y coordinate pairs: [x0, y0, x1, y1, ...]

OR

data, yData: Two Arrays - The first an array of the X coordinate values and the second an array of the Y coordinate values: : [x0, x1, x2, ...], [y0, y1, y2 ...]

OR

data: Array - Array of objects with x and y properties holding the coordinate pairs: [{x:x0, y:y0}, {x:x1, y:y1}, ...]

Returns:

PathSVG object - An array of objects with properties holding the SVG commands and associated coordinate pairs defining a path of straight line segments joining the data points.

Example:

Fig 4 shows an example of a polyline applied to the same set of data points used in the splineFit example above.

Figure 4. Example of a PathSVG.polyline through an array of data points.

  ...
const testXs = [5, 10, 40, 60, 90,120,150,200];
const testYs = [10, 40, 30,  5, 45, 10, 45, 10];

// draw the polyline through the data points
const linPth = PathSVG.polyline(testXs, testYs);
g.drawPath(linPth, {strokeColor:"green"});
  

SVG path data syntax

The SVG path data syntax comprises single letter commands such as 'M' for move, 'L' for a line segment, 'C' for a cubic Bezier curve, etc and 'Z' for a close path instruction. the commands are followed by their associated coordinates.

Commands are case sensitive, uppercase command coordinates are interpreted as absolute i.e. measured from the world coordinate origin. Lowercase command coordinates are interpreted as being relative to the current pen position i.e. the end point of previous command. The SVG standard requires the first command must always be a move command ('M' or 'm') followed by the x, y values of the starting point of the path in world coordinates. This is followed by an arbitrarily long set of commands and their associated parameters and coordinates.

In native SVG applications the SVG data coordinates are interpreted as Left Handed Cartesian Y coordinate values increase down the screen and angles increase clockwise.

SVG path segment commands

Command
Parameters
Description
M
x,y
moveto: Moves the pen to a new location. No line is drawn. All path data must begin with a 'moveto' command.
Line Commands
L
x,y
lineto: Draws a line from the current point to the point (x,y).
H
x
horizontal lineto: Draws a horizontal line from the current point to x.
V
y
vertical lineto: Draws a vertical line from the current point to y.
Cubic Bezier Curve Commands
C
x1 y1 x2 y2 x y
curveto: Draw a cubic Bezier curve to the point (x,y) where the points (x1,y1) and (x2,y2) are the start and end control points, respectively.
S
x2 y2 x y
shorthand/smooth curveto: Draw a curve to the point (x,y) where the point (x2,y2) is the end control point and the start control point is the reflection of the last point's end control point.
Quadratic Bezier Curve Commands
Q
x1 y1 x y
quadratic Bezier curveto: Draw a quadratic Bezier between the last point and point (x,y) using the point (x1,y1) as the control point.
T
x y
shorthand/smooth quadratic Bezier curveto: Draw a quadratic Bezier between the last point and point (x,y) using the reflection of the last control point as the control point.
Elliptical Arc Curve Commands
A
rx, ry,
x-rotation,
large-arc-flag,
sweep-flag,
x, y
elliptical arc: Draws and arc starting from the current point and ending at (x, y). The ellipse has the two radii (rx, ry). The x-axis of the ellipse is rotated by 'x-axis-rotation' relative to the x-axis of the world coordinate system. The 'large-arc-flag' and the 'sweep-flag' together define which way the arc joins to start and end point.

Cgo2D interprets the 'x-axis-rotation' coordinate is positive anti-clockwise and 'sweep-flag = 1' is interpreted as the arc sweeping in an anti-clockwise direction.

SVG interprets the 'x-axis-rotation' as positive clockwise and 'sweep-flag = 1' flag is interpreted as the arc sweeping in a clockwise direction.
End Path Commands
Z
-
closepath: Closes the path. A line is drawn from the last point to the first.

Relative coordinates:

To use path end point and control point coordinates relative to the last pen position use a lowercase letter for the command, eg. 'a' rather than 'A'.

As an example, here is the string of commands defining a unit square with the drawing origin (0,0) at its center.

"M 0.5 -0.5 l 0 1 -1 0 0 -1 z";