círculo de relleno con hexágonos

Estoy tratando de encontrar la forma de poner tantos hexágonos en un círculo como sea posible. Hasta ahora, el mejor resultado que he obtenido es generar hexágonos desde el centro hacia afuera en una forma circular.

Pero creo que mi cálculo para get los círculos hexagonales máximos es incorrecto, especialmente la parte donde utilizo las Math.ceil() y Math.Floor para networkingondear hacia arriba / hacia arriba algunos valores.

Al usar Math.ceil() , los hexágonos a veces se superponen al círculo.
Cuando se usa Math.floor() por otro lado, a veces deja demasiado espacio entre el último círculo de hexágonos y el borde del círculo.

 var c_el = document.getElementById("myCanvas"); var ctx = c_el.getContext("2d"); var canvas_width = c_el.clientWidth; var canvas_height = c_el.clientHeight; var PI=Math.PI; var PI2=PI*2; var hexCircle = { r: 110, /// radius pos: { x: (canvas_width / 2), y: (canvas_height / 2) } }; var hexagon = { r: 20, pos:{ x: 0, y: 0 }, space: 1 }; drawHexCircle( hexCircle, hexagon ); function drawHexCircle(hc, hex ) { drawCircle(hc); var hcr = Math.ceil( Math.sqrt(3) * (hc.r / 2) ); var hr = Math.ceil( ( Math.sqrt(3) * (hex.r / 2) ) ) + hexagon.space; // hexRadius var circles = Math.ceil( ( hcr / hr ) / 2 ); drawHex( hc.pos.x , hc.pos.y, hex.r ); //center hex /// for (var i = 1; i<=circles; i++) { for (var j = 0; j<6; j++) { var currentX = hc.pos.x+Math.cos(j*PI2/6)*hr*2*i; var currentY = hc.pos.y+Math.sin(j*PI2/6)*hr*2*i; drawHex( currentX,currentY, hex.r ); for (var k = 1; k<i; k++) { var newX = currentX + Math.cos((j*PI2/6+PI2/3))*hr*2*k; var newY = currentY + Math.sin((j*PI2/6+PI2/3))*hr*2*k; drawHex( newX,newY, hex.r ); } } } } function drawHex(x, y, r){ ctx.beginPath(); ctx.moveTo(x,yr); for (var i = 0; i<=6; i++) { ctx.lineTo(x+Math.cos((i*PI2/6-PI2/4))*r,y+Math.sin((i*PI2/6-PI2/4))*r); } ctx.closePath(); ctx.stroke(); } function drawCircle( circle ){ ctx.beginPath(); ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI); ctx.closePath(); ctx.stroke(); } 
 <canvas id="myCanvas" width="350" height="350" style="border:1px solid #d3d3d3;"> 

Si todos los puntos en el hexágono están dentro del círculo, el hexágono está dentro del círculo. No creo que haya una manera más simple que hacer el cálculo de la distancia.

No estoy seguro de cómo seleccionar el punto de relleno óptimo (pero aquí hay un fragment de js que demuestra que el medio no siempre es así). Es posible que cuando dices "círculo hexagonal" quieras decir hexágono hecho de hexágonos, en cuyo caso el fragment no testing nada 🙂

Hice los lados del hexágono 2/11 del radio del círculo y los espacié un 5% de la longitud del lado.

 var hex = {x:0, y:0, r:10}; var circle = {x:100, y:100, r:100}; var spacing = 1.05; var SQRT_3 = Math.sqrt(3); var hexagon_offsets = [ {x: 1/2, y: -SQRT_3 / 2}, {x: 1, y: 0}, {x: 1/2, y: SQRT_3 / 2}, {x: -1/2, y: SQRT_3 / 2}, {x: -1, y: 0}, {x: -1/2, y: -SQRT_3 / 2} ]; var bs = document.body.style; var ds = document.documentElement.style; bs.height = bs.width = ds.height = ds.width = "100%"; bs.border = bs.margin = bs.padding = 0; var c = document.createElement("canvas"); c.style.display = "block"; c.addEventListener("mousemove", follow, false); document.body.appendChild(c); var ctx = c.getContext("2d"); window.addEventListener("resize", networkingraw); networkingraw(); function follow(e) { hex.x = e.clientX; hex.y = e.clientY; networkingraw(); } function drawCircle() { ctx.strokeStyle = "black"; ctx.beginPath(); ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, true); ctx.closePath(); ctx.stroke(); } function is_in_circle(p) { return Math.pow(px - circle.x, 2) + Math.pow(py - circle.y, 2) < Math.pow(circle.r, 2); } function drawLine(a, b) { var within = is_in_circle(a) && is_in_circle(b); ctx.strokeStyle = within ? "green": "networking"; ctx.beginPath(); ctx.moveTo(ax, ay); ctx.lineTo(bx, by); ctx.closePath(); ctx.stroke(); return within; } function drawShape(shape) { var within = true; for (var i = 0; i < shape.length; i++) { within = drawLine(shape[i % shape.length], shape[(i + 1) % shape.length]) && within; } if (!within) return false; ctx.fillStyle = "green"; ctx.beginPath(); ctx.moveTo(shape[0].x, shape[0].y); for (var i = 1; i <= shape.length; i++) { ctx.lineTo(shape[i % shape.length].x, shape[i % shape.length].y); } ctx.closePath(); ctx.fill(); return true; } function calculate_hexagon(x, y, r) { return hexagon_offsets.map(function (offset) { return {x: x + r * offset.x, y: y + r * offset.y}; }) } function drawHexGrid() { var hex_count = 0; var grid_space = calculate_hexagon(0, 0, hex.r * spacing); var y = hex.y; var x = hex.x; while (y > 0) { y += grid_space[0].y * 3; x += grid_space[0].x * 3; } while (y < c.height) { x %= grid_space[1].x * 3; while (x < c.width) { var hexagon = calculate_hexagon(x, y, hex.r); if (drawShape(hexagon)) hex_count++; x += 3 * grid_space[1].x; } y += grid_space[3].y; x += grid_space[3].x; x += 2 * grid_space[1].x; } return hex_count; } function networkingraw() { c.width = window.innerWidth; c.height = window.innerHeight; circle.x = c.width / 2; circle.y = c.height / 2; circle.r = Math.min(circle.x, circle.y) * 0.9; hex.r = circle.r * (20 / 110); ctx.clearRect(0, 0, c.width, c.height); var hex_count = drawHexGrid(); drawCircle(); ctx.fillStyle = "rgb(0, 0, 50)"; ctx.font = "40px serif"; ctx.fillText(hex_count + " hexes within circle", 20, 40); }