Skip to content

Instantly share code, notes, and snippets.

@veltman
Created July 11, 2016 03:05
Show Gist options
  • Save veltman/f24fba4f6549639cacfd4d0a50e9d4b8 to your computer and use it in GitHub Desktop.
Save veltman/f24fba4f6549639cacfd4d0a50e9d4b8 to your computer and use it in GitHub Desktop.
Argyle

Infinite argyle with random ColorBrewer and d3 4.0 color scales.

(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();
};
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
path {
stroke: none;
}
line {
stroke: #444;
stroke-width: 1px;
stroke-linecap: round;
opacity: 0.8;
}
@media (-webkit-min-device-pixel-ratio: 1.5),
(min-resolution: 1.5dppx) {
line {
stroke-width: 0.6px;
opacity: 0.95;
}
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="argyle.js"></script>
<script>
var angle = Math.PI / 6,
rx = 30,
ry = rx / Math.tan(angle),
diagonal = Math.sqrt(rx * rx + ry * ry);
width = 960,
height = Math.ceil(500 / ry) * ry;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var diamond = d3.symbol()
.type(d3.symbolDiamond)
.size(ry * rx * 2);
// Array of [row, column] positions
var positions = d3.merge(d3.range(Math.ceil(height / ry) + 1).map(function(r){
return d3.range(Math.ceil(width / (rx * 2)) + 1).map(function(c){
return [r, c];
});
}));
var g = svg.selectAll(".diamond")
.data(positions)
.enter()
.append("g")
.attr("transform", function(d){
// Odd-r coordinates
var x = (d[0] % 2 ? rx : 0) + d[1] * rx * 2,
y = d[0] * ry;
return "translate(" + x + " " + y + ")";
});
var background = g.append("path")
.attr("d", diamond);
g.append("line")
.attr("x1", rx / 2)
.attr("x2", -rx / 2);
g.append("line")
.attr("x1", -rx / 2)
.attr("x2", rx / 2);
// Separate lines + calculated dasharray to prevent cuts
var stitch = g.selectAll("line")
.attr("y1", -ry / 2)
.attr("y2", ry / 2)
.attr("stroke-dasharray",(diagonal / 30) + "," + (diagonal / 30))
.attr("stroke-dashoffset",(-diagonal / 60));
recolor(true);
function recolor(first) {
var pattern = argyle();
var t = d3.transition()
.delay(first ? 0 : 1000)
.duration(first ? 0 : 1000)
.on("end", recolor);
background.transition(t)
.styleTween("fill", function(d){
return d3.interpolateLab(getComputedStyle(this).getPropertyValue("fill"), pattern.color(d));
});
stitch.transition(t)
.styleTween("stroke", function(d){
return d3.interpolateLab(getComputedStyle(this).getPropertyValue("stroke"), pattern.stitch[pattern.color(d)]);
});
}
d3.select(self.frameElement).style("height", height + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment