Skip to content

Instantly share code, notes, and snippets.

@potch
Last active June 6, 2022 10:54
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save potch/4214346 to your computer and use it in GitHub Desktop.
Save potch/4214346 to your computer and use it in GitHub Desktop.
Path data generation helper for SVG <path> elements

SVG path data generator

This is excerpted from my work on emilio.js, a d3 plotting library, but useful enough to stand on its own.

  • Generators are constructed with new Path().

  • Takes two optional arguments, xScale and yScale.

    • Methods for coordinate space translation.
    • Works great with d3.scale() objects!
    • xScale and yScale affect the x and y coordinate spaces, respectively.
    • If only xScale is supplied, y values will be in the xScale space.
  • Chainable!

      var p = new Path();
      p.move(0,0).line(50,50).smoothCurve(100,50,100,0);
      pathEl.setAttribute('d', p.data()); // 'M0,0L50,50S100,50 100,0'
    
  • M = move(x, y)
  • L = line(x, y)
  • H = horiz(x)
  • V = vert(y)
  • C = curve(c1x, c1y, c2x, c2y, x, y)
  • S = smoothCurve(cx, cy, x, y)
  • Q = quad(cx, cy, x, y)
  • T = smoothQuad(x, y)
  • A = arc(rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y)
  • Z = close()

Additional Methods

  • abs() - subsequent calls will be in absolute coordinate space (default).
  • rel() - subsequent calls will be in relative coordinate space.
  • data() - returns the computed d atrribute for the <path>.
function Path(xScale, yScale) {
// init our coordinate transformation methods, if they weren't supplied.
xScale = xScale || function(v) { return v };
yScale = yScale || xScale;
var self = this;
var data = '';
var lastAct = '';
// we default to absolute coordinate space.
var isRelative = false;
// generates drawing functions for a given command char.
function act(c, fn) {
return function() {
// Determine which command char to write- abs vs. rel.
var cOut = isRelative ? c : c.toUpperCase();
// Formatting nicety. Don't write the same command char twice.
data += (c === lastAct) ? ' ' : cOut;
lastAct = c;
// call the drawing function.
fn.apply(this, Array.prototype.slice.call(arguments));
// chain!
return self;
}
}
// we do x,y coordinate pairs a lot.
function xy(x, y) {
data += xScale(x) + ',' + yScale(y);
}
// not necessary but nice.
function sp() {
data += ' ';
}
// we do this twice, so why not abstract?
function oneCP(cx, cy, x, y) {
xy(cx, cy);
sp();
xy(x, y);
}
self.move = act('m', xy);
self.line = act('l', xy);
self.horizontal = act('h', function(v) {
data += xScale(v);
});
self.vertical = act('v', function(v) {
data += yScale(v);
});
self.curve = act('c', function(c1x, c1y, c2x, c2y, x, y) {
xy(c1x, c1y);
sp();
xy(c2x, c2y);
sp();
xy(x, y);
});
self.smoothCurve = act('s', oneCP);
self.quad = act('q', oneCP);
self.smoothQuad = act('t', xy);
self.arc = act('a', function(rx, ry, angle, hand, large, x, y) {
xy(rx, ry);
sp();
data += angle;
sp();
data += hand + ',' + large;
sp();
xy(x, y);
});
self.close = act('z', function() {});
// enables relative coordinate space.
self.rel = function rel() {
isRelative = true;
return self;
}
// enables absolute coordinate space.
self.abs = function abs() {
isRelative = false;
return self;
}
self.data = function() {
return data;
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment