Last active
January 30, 2018 23:00
-
-
Save sbrl/ed97524db09481e1b018 to your computer and use it in GitHub Desktop.
[SmoothLine.js] A class to aid the drawing of smooth lines. #es6 #module #microlibrary
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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 <bugs@starbeamrainbowlabs.com>. Alternatively, you can tweet | |
* me at @SBRLabs. | |
******************************************************************************* | |
* Author: Starbeamrainbowlabs <bugs@starbeamrainbowlabs.com> | |
* | |
* 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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment