Skip to content

Instantly share code, notes, and snippets.

@LTLA
Created November 9, 2022 07:05
Show Gist options
  • Save LTLA/946cc6d9de7a4608746db0ae5d0a5849 to your computer and use it in GitHub Desktop.
Save LTLA/946cc6d9de7a4608746db0ae5d0a5849 to your computer and use it in GitHub Desktop.
Manual scatterplot to SVG
function range(x) {
let maxed = Number.NEGATIVE_INFINITY;
let mined = Number.POSITIVE_INFINITY;
for(const y of x) {
if (y > maxed) {
maxed = y;
}
if (y < mined) {
mined = y;
}
}
return [ mined, maxed ];
}
function points(x, y, { col=null, xbound = [0, 100], ybound = [0, 100], radius = 1 } = {}) {
let xrange = range(x);
let xscale = (xbound[1] - xbound[0]) / (xrange[1] - xrange[0]);
let xshift = xbound[0] - xrange[0] * xscale;
let yrange = range(y);
let yscale = (ybound[1] - ybound[0]) / (yrange[1] - yrange[0]);
let yshift = ybound[0] - yrange[0] * yscale;
let elements = [];
for (var i = 0; i < x.length; i++) {
let color = col == null ? "#000000" : col[i];
elements.push(`<circle cx="${x[i] * xscale + xshift}" cy="${y[i] * yscale + yshift}" r="${radius}" style="fill:${color}" />`);
}
return elements;
}
function legendCategorical(text, col, { xleft = 0, ybound = [0, 100] } = {}) {
let nlevels = text.length;
let height = ybound[1] - ybound[0];
let linewidth = height / nlevels;
if (linewidth > 5) {
linewidth = 5;
}
let elements = [];
let linestart = height/2 + linewidth*nlevels/2;
for (var i = 0; i < text.length; i++) {
let ypos = linestart - i * linewidth;
elements.push(`<circle cx="${xleft}" cy="${ypos}" r="${linewidth/3}" style="fill:${col[i]}" />`);
elements.push(`<text x="${xleft + linewidth / 1.5}" y="${ypos}" dominant-baseline="middle" font-family="Arial, Helvetica, sans-serif" font-size="${linewidth}">${text[i]}</text>`);
}
return elements;
}
// Making shit up:
var colors = ['#ff0000', '#00ff00', '#0000ff'];
let text = [ "Aaron", "Jaya", "Ram" ];
let x = [];
let y = [];
let cols = [];
for (var i = 0; i < 100; i++) {
x.push(Math.random());
y.push(Math.random());
cols.push(colors[Math.floor(Math.random() * 3)])
}
// We'll be creating a 100x100 plot with a width-20 legend on the right.
let point_svg = points(x, y, { col: cols, xbound: [ 5, 95 ], ybound: [5, 95] });
let legend_svg = legendCategorical(text, colors, { xleft: 105, ybound: [5, 95] })
let output = `<svg
viewBox="0 0 120 100"
xmlns="http://www.w3.org/2000/svg">
${point_svg.join("\n")}
<rect x="0" y="0" width="100" height="100" stroke="grey" stroke-width="0.1" fill="none" />
${legend_svg.join("\n")}
</svg>`
console.log(output);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment