(function(){ var runs = 0, scheme; var colorSchemes = d3.shuffle([ ["#e0f3db", "#a8ddb5", "#43a2ca"], // GnBu ["#ece2f0", "#a6bddb", "#1c9099"], // PuBuGn ["#fde0dd", "#fa9fb5", "#c51b8a"], // RdPu ["#f7fcb9", "#addd8e", "#31a354"], // YlGn ["#edf8b1", "#7fcdbb", "#2c7fb8"], // YlGnBlue ["#ffeda0", "#feb24c", "#f03b20"], // YlOrRd ["#d8b365", "#f6e8c3", "#c7eae5", "#5ab4ac"], // BrBG ["#fc8d59", "#ffffbf", "#99d594"], // Spectral3 ["#d7191c", "#fdae61", "#abdda4", "#2b83ba"], // Spectral4 ["#fc8d59", "#ffffbf", "#91bfdb"], // RdYlBu ["#fc8d59", "#fee090", "#e0f3f8", "#91bfdb"], // RdYlBu d3.interpolateViridis, d3.interpolatePlasma, d3.interpolateWarm, d3.interpolateCool, d3.interpolateRainbow ]); var greys = ["#222", "#666", "#ccc", "#f7f7f7"]; var patterns = [standard, checker, wave, bowtie, columns]; function getPattern() { var r = Math.random(), totalColors = r < 0.1 ? 5 : r < 0.5 ? 4 : 3, colors = scheme.slice(0), stitchColors; // Pad with greys if (colors.length < totalColors) { d3.shuffle(greys); colors = colors.concat(greys.slice(0, totalColors - colors.length)); } else { colors = d3.shuffle(colors).slice(0, totalColors); // Mix in a grey sometimes anyway if (Math.random() < 0.3) { colors[0] = choose(greys); } } stitchColors = getStitchColors(colors); d3.shuffle(colors); return { stitch: stitchColors, color: choose(patterns)(colors), }; } // Set stitch coloring based on highest contrast ratio // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef function getStitchColors(colors) { var l = colors.map(relativeLuminance), stitchColors = {}, splice = colors.length === 5 || (colors.length === 4 && Math.random() < 0.35), primary, ratios; // Calculate contrast ratios for other colors ratios = colors.map(function(color, i){ return colors.map(function(d, j){ return (Math.max(l[i], l[j]) + 0.05) / (Math.min(l[i], l[j]) + 0.05); }); }); // Color with highest average contrast primary = d3.scan(ratios, function(a, b) { return 1 / d3.mean(a) - 1 / d3.mean(b); }); // Dedicated stitch color if (splice) { colors.forEach(function(color, i){ if (i !== primary) { stitchColors[color] = colors[primary]; } }); colors.splice(primary, 1); // Background colors as stitch colors on each other } else { colors.forEach(function(color, i){ var secondary; if (i === primary) { secondary = d3.scan(ratios[i], function(a, b) { return 1 / a - 1 / b; }); stitchColors[color] = colors[secondary]; } else { stitchColors[color] = colors[primary]; } }); } return stitchColors; } // sRGB relative luminance // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef function relativeLuminance(color) { var rgb = d3.rgb(color); var weights = ["r", "g", "b"].map(function(key){ var val = rgb[key] / 255; return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4); }); return weights[0] * 0.2126 + weights[1] * 0.7152 + weights[2] * 0.0722; } function nextColorScheme() { var offset, numColors; // Move first to last colorSchemes.push(scheme = colorSchemes.shift()); // For d3 scales, get random evenly spaced colors if (typeof scheme === "function") { offset = Math.random(); numColors = choose([3, 4]); scheme = d3.range(numColors).map(function(d){ return scheme((offset + d / (numColors)) % 1); }); } } function standard(colors){ return function(d){ return d[0] % 2 ? colors[0] : colors[1 + d[1] % (colors.length - 1)]; }; } function checker(colors){ return function(d) { return d[0] % 2 ? colors[0] : colors[1 + ((d[1] + d[0] / 2) % (colors.length - 1))]; }; } function wave(colors){ return function(d){ return d[0] % 2 ? colors[0] : d[0] % 4 ? colors[2 + Math.floor(d[0] / 4) % (colors.length - 2)] : colors[1]; }; } function bowtie(colors){ if (colors.length < 4) { return standard(colors); } return function(d){ return d[0] % 2 ? colors[d[1] % 2] : colors[2 + (d[0] / 2) % 2]; }; } function columns(colors) { if (colors.length < 4) { return checker(colors); } return function(d) { return d[0] % 2 ? colors[d[1] % 2] : colors[2 + d[1] % 2]; }; } function choose(arr) { return arr[Math.floor(Math.random() * arr.length)]; } window.argyle = function(){ if (runs++ % 3 === 0) { nextColorScheme(); } return getPattern(); }; })();