Skip to content

Instantly share code, notes, and snippets.

@nbremer
Last active June 19, 2016 13:41
Show Gist options
  • Save nbremer/7ab7995a7ebd48ad424f218a5b01d30a to your computer and use it in GitHub Desktop.
Save nbremer/7ab7995a7ebd48ad424f218a5b01d30a to your computer and use it in GitHub Desktop.
Random gradients and orientations - Hexagons - 1 palette
height: 960

An experiment where I wanted to randomly fill a page with hexagons. Each hexagon has its own random gradient that is randomly oriented along 3 possible angles. The colors at both ends are then also randomly chosen.

This example uses the dark rainbow color palette. Another version has the option to choose between the dark rainbow and a green color palette

Built with blockbuilder.org

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hexagonal SVG gradient experiment</title>
<style>
body {
/*background-color: #262626;*/
text-align: center;
}
</style>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<div id="hexmap"></div>
<script>
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var hexRadius = 10,
SQRT3 = Math.sqrt(3),
hexWidth = SQRT3 * hexRadius,
hexHeight = 2 * hexRadius;
var margin = {
top: hexHeight / 2 + 5,
right: 0,
bottom: 0,
left: hexWidth + 5
};
var width = 960 - margin.left - margin.right - 10;
var height = 960 - margin.top - margin.bottom - 20;
//SVG container
var svg = d3.select('#hexmap')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var defs = svg.append("defs");
var colors = ["#EFB605", "#E9A501", "#E48405", "#E34914", "#DE0D2B", "#CF003E", "#B90050", "#A30F65", "#8E297E", "#724097", "#4F54A8", "#296DA4", "#0C8B8C", "#0DA471", "#39B15E", "#7EB852"];
var gradRotations = [0, 60, 120];
var animationID,
animationLoop = 1,
extraStep = 0,
alpha = 1,
reduceAlpha = false,
keepGrowing = true;
///////////////////////////////////////////////////////////////////////////
/////////////////////// Calculate hexagon variables ///////////////////////
///////////////////////////////////////////////////////////////////////////
var SQRT3 = Math.sqrt(3),
hexWidth = SQRT3 * hexRadius,
hexHeight = 2 * hexRadius;
var hexagonPoly = [[0,-1],[SQRT3/2,0.5],[0,1],[-SQRT3/2,0.5],[-SQRT3/2,-0.5],[0,-1],[SQRT3/2,-0.5]];
var hexagonPath = "m" + hexagonPoly.map(function(p){ return [p[0]*hexRadius, p[1]*hexRadius].join(','); }).join('l') + "z";
var mapColumns = Math.floor(width/(SQRT3 * hexRadius)) + 1,
mapRows = Math.floor(height/(2 * hexRadius * 0.75));
///////////////////////////////////////////////////////////////////////////
//////////////// Calculate hexagon centers and put into array /////////////
///////////////////////////////////////////////////////////////////////////
var points = [];
var pointsByIndex = [];
var counter = 0;
for (var i = 0; i < mapRows; i++) {
for (var j = 0; j < mapColumns; j++) {
var a;
var b = (3 * i) * hexRadius / 2;
if (i % 2 === 0) {
a = Math.sqrt(3) * j * hexRadius;
} else {
a = Math.sqrt(3) * (j - 0.5) * hexRadius;
}//else
points.push({
x: a, y: b, rowIndex: i, colIndex: j, fill: false
});
pointsByIndex[i + "-" + j] = counter;
counter += 1;
}//for j
}//for i
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Place hexagons //////////////////////////////
///////////////////////////////////////////////////////////////////////////
svg.append("g")
.selectAll(".hexagon")
.data(points)
.enter().append("path")
.attr("class", "hexagon")
.attr("d", function (d) { return "M" + d.x + "," + d.y + hexagonPath; })
.style("stroke", "none")
.style("fill", "none")
.style("stroke-opacity", 0)
.style("fill-opacity", 0);
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Fill hexagons ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Start it off in the near middle
var centerRow = Math.round(mapRows/2),
centerCol = Math.round(mapColumns/2),
filledHexs = [];
filledHexs[centerRow+"-"+centerCol] = {x: centerRow, y: centerCol};
createGradient(filledHexs[centerRow+"-"+centerCol]);
fillHex(filledHexs[centerRow+"-"+centerCol]);
function animate() {
//Loop over all already filled hexagons
for(var hex in filledHexs) {
//Find its neighbours and append them to the already existing ones
//make sure to have only the unique one
var newNeighbours = findNeighbours(filledHexs[hex]);
newNeighbours.forEach(function(d) {
//With a probability add the hexagon to the filled set
if(Math.random() < 0.05) {
if(d.x >= 0 && d.x < mapRows && d.y >= 0 && d.y < mapColumns) {
if(filledHexs[d.x+"-"+d.y] === undefined) {
filledHexs[d.x+"-"+d.y] = d;
//Create a gradient for the hexagon
createGradient(d);
fillHex(d);
}//if
//If the growth is almost at the outside, let the alpha shrink
if( d.x < mapRows*0.25 || d.x > mapRows*0.75 || d.y < mapColumns*0.25 || d.y > mapColumns*0.75 ) {
reduceAlpha = true;
}//if
} else {
//Stop growing once one hexagon neighbour would lie outside the box
keepGrowing = false;
}//else
}//if
});
}//for hex
//Exponential decay of alpha
if(reduceAlpha) {
extraStep = extraStep + 0.075;
alpha = Math.min(1, (1 - Math.exp(-5+extraStep)) );
}
//Set the found neighbours to have a fill
for(var item in filledHexs) {
points[pointsByIndex[item]].fill = true;
}//for item
if(keepGrowing) {
animationID = requestAnimationFrame(animate);
}//if
}//animate
//Start the run
requestAnimationFrame(animate);
///////////////////////////////////////////////////////////////////////////
////////////////////////// Helper functions ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Create a linear gradient for the hexagon d
function createGradient(d) {
//Create a gradient
var linearGradient = defs.append("linearGradient")
.attr("id", "gradient-"+d.x+"-"+d.y)
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "0%").attr("y2", "100%")
.attr("gradientTransform", "rotate("+ (gradRotations[Math.round(Math.random()*(gradRotations.length-1))]) +" "+ 0.5 +" "+ 0.5 +")");
linearGradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", colors[Math.round(Math.random()*(colors.length-1))] )
.attr("stop-opacity", alpha );
linearGradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", colors[Math.round(Math.random()*(colors.length-1))])
.attr("stop-opacity", alpha );
}//createGradient
function fillHex(d) {
//Fill the chosen hexagon with its color
d3.selectAll(".hexagon")
.filter(function(h,i) { return h.rowIndex === d.x && h.colIndex === d.y; })
.style("fill", "url(#gradient-"+d.x+"-"+d.y+")" )
.style("stroke", "url(#gradient-"+d.x+"-"+d.y+")" )
.transition().duration(500)
.style("fill-opacity", 1)
.style("stroke-opacity", alpha > 0.9 ? 1 : alpha*0.35 );
}//fillHex
//Find the row and column locations of the 6 neighbours around a hexagon
function findNeighbours(currentHex) {
var i = currentHex.y,
j = currentHex.x;
//http://stackoverflow.com/questions/6661169/finding-adjacent-neighbors-on-a-hexagonal-grid
if(j % 2 === 0) {
//For a cell (X,Y) where Y is even, the neighbors are
var neighbours = [
{y: i, x: (j-1)},
{y: (i+1), x: (j-1)},
{y: (i-1), x: j},
{y: (i+1), x: j},
{y: i, x: (j+1)},
{y: (i+1), x: (j+1)} ];
} else {
//For a cell (y,x) where x is odd, the neighbors are
var neighbours = [
{y: (i-1), x: (j-1)},
{y: i, x: (j-1)},
{y: (i-1),x: j},
{y: (i+1), x: j},
{y: (i-1), x: (j+1)},
{y: i, x: (j+1)} ];
}//else
return neighbours;
}//findNeighbours
//http://stackoverflow.com/questions/3689903/how-to-create-a-2d-array-of-zeroes-in-javascript
function zeros(dimensions) {
var array = [];
for (var i = 0; i < dimensions[0]; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
}
return array;
// zeros([5, 3]);
// [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
}//zeros
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment