Skip to content

Instantly share code, notes, and snippets.

@donpark
Forked from srt19170/hdlines.js
Created August 11, 2017 20:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donpark/3debaae9267450a586bc7f43792249d1 to your computer and use it in GitHub Desktop.
Save donpark/3debaae9267450a586bc7f43792249d1 to your computer and use it in GitHub Desktop.
Hand-Drawn Lines Code (Javascript)
//
// Draw a line with the given curve, and then resample it
// to a new set of points. <step> is the distance to step
// along the line when taking a new point.
//
function drawInterpolate(svg, points, step, curve) {
curve = curve || d3.curveCatmullRom.alpha(1.0);
step = step || 1;
var lineFunc = d3.line()
.curve(curve)
.x(function(d) {return d[0]; })
.y(function(d) {return d[1];});
// Draw line
var path = svg.append('path')
.attr('d', lineFunc(points))
.style("stroke-linecap", "round")
.style("stroke-width", 1)
.style("stroke", "black");
// Go through and find points corresponding to the line
var results = [];
var len = path.node().getTotalLength();
for(var cur = 0;cur<len;cur += step) {
var pt = path.node().getPointAtLength(cur);
results.push([pt.x, pt.y]);
};
// Remove the line now that we've measured it.
path.remove();
return results;
};
//
// "Jiggle" a series of line segments by the given magnitude.
//
function handDrawn(points, magnitude) {
magnitude = magnitude || 0.003;
// If we have a very short line, it sometimes resamples down to
// nothing. In this case, we can just return the original line.
if (points.length < 2) return result;
// Compute the gradients.
var gradients = points.map(function (a, i, d) {
if (i == 0) return [d[1][0] - d[0][0], d[1][1] - d[0][1]];
if (i == points.length - 1)
return [d[i][0] - d[i - 1][0], d[i][1] - d[i - 1][1]];
return [0.5 * (d[i + 1][0] - d[i - 1][0]),
0.5 * (d[i + 1][1] - d[i - 1][1])];
});
// Normalize the gradient vectors to be unit vectors.
gradients = gradients.map(function (d) {
var len = Math.sqrt(d[0] * d[0] + d[1] * d[1]);
if (len == 0) return [0, 0];
return [d[0] / len, d[1] / len];
});
// Generate some perturbations.
var perturbations = smoothLine(points.map(d3.randomNormal()), 3);
// Add in the perturbations. We keep the first and last point
// unchanged so that we know line segments will be able to match
// up precisely.
var result = points.slice(1,-1).map(function (d, i) {
// Need i+1 here because we sliced off [0]
var p = perturbations[i+1],
g = gradients[i+1];
return [d[0] + magnitude * g[1] * p,
d[1] - magnitude * g[0] * p];
});
// add first element and last element back
result.unshift(points[0]);
result.push(points[points.length-1]);
return result;
}
//
// This routine creates a smooth polyline of the given width
// and color. Points is an array of [x, y] values.
//
function drawLine(svg, points, width, color) {
if (points.length < 2) return null;
var lineFunc = d3.line()
.curve(d3.curveCatmullRom.alpha(1.0))
.x( function(d) {return d[0]; })
.y(function(d) {return d[1];});
var g = svg.append('g');
return g.append('path')
.attr('d', lineFunc(points))
.style("stroke-linecap", "round")
.style("stroke-width", width)
.style("stroke", color);
};
//
// This routine draws a polyline of the given width
// and color, with added "jiggle". Note that I'm using
// hard-coded domain and magnitude values that work for me.
//
function drawLineHnd(svg, points, width, color, step, magnitude) {
magnitude = magnitude || 0.003;
step = step || 1;
if (points.length < 2) return null;
var p = drawInterpolate(svg, points, step);
p = handDrawn(p, magnitude);
return drawLine(svg, p, width, color);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment