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> |