Infinite argyle with random ColorBrewer and d3 4.0 color scales.
Created
July 11, 2016 03:05
-
-
Save veltman/f24fba4f6549639cacfd4d0a50e9d4b8 to your computer and use it in GitHub Desktop.
Argyle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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(); | |
}; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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