"use strict"; /******************************************************************************* **************************** ES6 Smooth Line Class **************************** ******************************************************************************* * v0.1 ******************************************************************************* * A smooth line class built upon my earlier bezier curve and vector classes. * * Given a number of points (not all of which have to be specified at once), * this class will add the appropriate lineTos to the given drawing context. * * This class was originally written on Codepen. Links: * * Codepen: https://codepen.io/sbrl/details/zrEyrg/ * Blog post: (coming soon!) * * This class depends on my earler bezier curve and vector classes. Links: * * Vector class: https://gist.github.com/sbrl/69a8fa588865cacef9c0 * Bezier curve class: https://gist.github.com/sbrl/efd57e458e71f8a54171 * * Bug reports can be made as a comment on this gist, or * sent to . Alternatively, you can tweet * me at @SBRLabs. ******************************************************************************* * Author: Starbeamrainbowlabs * * Changelog: * v0.1: Initial revision. * */ class SmoothLine { constructor() { this.points = []; this.interpolatedPoints = []; this.bezierCurves = []; this.lastPointLength = -1; } /** * Adds one or more points to the smooth line. * @param {Vector} point A single vector or an array of vectors to add onto the end of the smooth line. */ add(point) { if (Array.isArray(point)) this.points.push(...point); else this.points.push(point); } /** * Internal. Interpolates THe given array of vectors once. * @param {Vector[]} points The array of vectors to interpolate. * @param {number} time The percentage between 0 and 1 at which to interpolate. * @return {Vector[]} The interpolated vectors. */ interpolateOnce(points, time) { // Input validation checks if (time < 0 || time > 1) throw new Error(`The time specified was out of bounds! It should be between 0 and 1, but a value of ${time} was provided.`); if (!Array.isArray(points)) throw new Error("THe points provided are not in an array!"); if (points.length < 3) throw new Error("A minimum of 3 points are required to draw a smooth line."); var result = []; // Loop over all the points, except the last one for (let i = 0; i < points.length - 1; i++) { // Find the difference between the current point and the next one along // To get the vector of the line between 2 points, you do b - a for the points a and b. let difference = points[i + 1].clone().subtract(points[i]); // Multiply the line's vector by the time in order to extract a percentage along the line difference.multiply(time); // Add the first point on to put the vector back in the right place, // and then add it to the interpolated pionts array. // It's important to add the first control point on again here as we // made the vector relative to 0 in order to perform the // interpolation rather than relative to the first point on the line // as it should be. result.push(difference.add(points[i])); } return result; } /** * Adds the smooth line to the path of the given canvas drawing context. * @param {CanvasDrawingContext2D} context The drawing context to add the smooth line to. * @param {number} segmentCount The number of segments that each bezier curve should have. */ line(context, segmentCount) { if (this.points.length < 3) throw new Error(`At least 3 points are required to draw a smooth line, but only ${this.points.length} points are currently specified.`); if (this.lastPointLength !== this.points.length) { // Reset the bezier curve cache this.bezierCurves = []; this.interpolatedPoints = this.interpolateOnce(this.points, 0.5); // Loop over every point except the frst & last ones for (let i = 1; i < this.points.length - 1; i++) { let nextPointSet = [ this.interpolatedPoints[i - 1], this.points[i], this.interpolatedPoints[i] ]; // If this is the first iteration, make the first point of the bezier curve the first point that we were given if (i == 1) nextPointSet[0] = this.points[0]; // If this is the last iteration, make the end point of the bezier curve the last point we were given if (i == this.points.length - 2) nextPointSet[2] = this.points[this.points.length - 1]; // The above 2 checks are needed to make sure that the smooth line starts and ends at the points that we were given let nextBezier = new BezierCurve(nextPointSet); this.bezierCurves.push(nextBezier); } } // Spin through all the bezier curves and get them to add themselves to the current path for (let i = 0; i < this.bezierCurves.length; i++) this.bezierCurves[i].curve(context, segmentCount); // Update the cached poits length this.lastPointLength = this.points.length; } }