Built with blockbuilder.org
forked from tonyhschu's block: Generating hex grid positions
license: mit |
Built with blockbuilder.org
forked from tonyhschu's block: Generating hex grid positions
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
</style> | |
</head> | |
<body> | |
<script> | |
console.clear() | |
var SQRT_3_2 = Math.sqrt(3)/2 | |
var SCALE = 10 | |
var toScreen = function(cube) { | |
var q = cube[0] | |
var r = cube[1] | |
var s = cube[2] | |
return { | |
x: SCALE * (r-q) * SQRT_3_2, | |
y: SCALE * (0.5*(r+q) - s) | |
} | |
} | |
// Feel free to change or delete any of the code you see in this editor! | |
var svg = d3.select("body").append("svg") | |
.attr("width", 960) | |
.attr("height", 500) | |
var g = svg.append('g') | |
.attr('transform', 'translate(480, 250)') | |
var constrainedHexPositionFactory = function() { | |
const SQRT_3_2 = Math.sqrt(3)/2 | |
const SCALE = 10 | |
let candidates = [] | |
let occupied = {} | |
let beenCanned = {} | |
let directions = [ | |
[+1, -1, 0], [+1, 0, -1], [0, +1, -1], | |
[-1, +1, 0], [-1, 0, +1], [0, -1, +1] | |
] | |
let move = function(cur, dir) { | |
return [cur[0] + dir[0], cur[1] + dir[1], cur[2] + dir[2]] | |
} | |
let positionDistance = function(cube) { | |
var q = cube[0] | |
var r = cube[1] | |
var s = cube[2] | |
var x = SCALE * (r-q) * SQRT_3_2 | |
var y = SCALE * (0.5*(r+q) - s) | |
return Math.sqrt(x * x + y * y) | |
} | |
let computeNewCandidates = function(position) { | |
return directions.map((dir) => { | |
return move(position, dir) | |
}) | |
.filter((c) => { | |
let isOccupied = checkHash(occupied, c) | |
let hasAlreadyBeenCandidate = checkHash(beenCanned, c) | |
return !isOccupied && !hasAlreadyBeenCandidate | |
}) | |
.map((c) => { | |
addPositionToHash(beenCanned, c) | |
return { | |
distance: positionDistance(c), | |
position: c | |
} | |
}) | |
} | |
let addPositionToHash = function(hash, position) { | |
let x = hash[position[0]] | |
if (!x) { | |
x = {} | |
hash[position[0]] = x | |
} | |
let y = x[position[1]] | |
if (!y) { | |
y = {} | |
x[position[1]] = y | |
} | |
let z = y[position[2]] | |
if (!z) { | |
y[position[2]] = position | |
} | |
} | |
let checkHash = function(hash, position) { | |
let x = hash[position[0]] | |
if (!x) { return false } | |
let y = hash[position[0]][position[1]] | |
if (!y) { return false} | |
let z = hash[position[0]][position[1]][position[2]] | |
return (z) ? true : false | |
} | |
return function() { | |
let newPosition | |
if (candidates.length <= 0) { | |
newPosition = [0, 0, 0] | |
} else { | |
let candidate = candidates.shift() | |
newPosition = candidate.position | |
} | |
let newCandidates = computeNewCandidates(newPosition) | |
candidates = candidates.concat(newCandidates) | |
.sort((a, b) => { | |
return a.distance - b.distance | |
}) | |
addPositionToHash(occupied, newPosition) | |
candidates.forEach((c) => { | |
addPositionToHash(beenCanned, c) | |
}) | |
return newPosition | |
} | |
} | |
let mouseDistance = 0 | |
svg | |
.on("mousemove", function(e) { | |
let m = d3.mouse(this) | |
let dx = 480 - m[0] | |
let dy = 250 - m[1] | |
mouseDistance = Math.sqrt(dx * dx + dy * dy) | |
}) | |
let paint = function() { | |
let hexPosition = constrainedHexPositionFactory() | |
let numberOfCircles = Math.floor(mouseDistance) * 4 | |
let coordinates = d3.range(numberOfCircles).map((i) => { | |
return hexPosition() | |
}).map((cube, i) => { | |
return { | |
key: i, | |
cube: cube, | |
screen: toScreen(cube) | |
} | |
}) | |
let points = g.selectAll('circle') | |
.data(coordinates, function(d) { return d.key }) | |
points | |
.enter() | |
.append('circle') | |
.attr('r', 6) | |
.merge(points) | |
.attr('fill', function(d) { | |
let r = d.cube[0] * 5 + 150 | |
let g = d.cube[1] * 5 + 150 | |
let b = d.cube[2] * 5 + 150 | |
return 'rgb(' + r + ', ' + g + ', ' + b + ')' | |
}) | |
.attr('cx', function(d) { | |
return d.screen.x | |
}) | |
.attr('cy', function(d) { | |
return d.screen.y | |
}) | |
points.exit().remove() | |
window.requestAnimationFrame(paint) | |
} | |
paint() | |
</script> | |
</body> |