用六边形填充圆(不同的方法)

时间:2016-02-26 10:48:08

标签: javascript html5 geometry

由于我以前的approach似乎不起作用且解决方案相当复杂,我决定尝试另一种可能稍微简单一点的方法。

这次,在代码绘制任何六边形之前,它必须确定预定义圆圈中可以容纳多少行和列,然后根据此结果开始绘制所有六边形。

到目前为止它有点工作,但与我之前的方法一样,有时候六边形重叠,或者在圆形的下半部分留下很大的间隙。

另一个问题是,如何将这些六边形格式化为网格?

请注意,画布下方有一个小滑块,可让您增加/减少圆的半径并重绘六边形。



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 circle = {
	r: 120, /// radius 
	pos: {
		x: (canvas_width / 2),
		y: (canvas_height / 2)
	}
}

var hexagon = {
	r: 20,
	pos:{
		x: 0,
		y: 0
	}
}

var hex_w = hexagon.r * 2;
var hex_h = Math.floor( Math.sqrt(3) * hexagon.r );
var hex_s =  (3/2) * hexagon.r;

fill_CircleWithHex( circle );

function fill_CircleWithHex(circle){
	drawCircle( circle );
	
	var c_h = circle.r * 2; /// circle height ////
	var c_w = c_h; //// circle width /////
	
	var max_hex_H = Math.round( c_h /  hex_h );
	
	var row_sizes = []
	for(var row= 0; row< max_hex_H; row++){
		
		var d = circle.r - ( row* hex_h);  //// distance from circle's center to the row's chord ////
		var c = 2 * (Math.sqrt((circle.r*circle.r) - (d * d))); ///  length of the row's chord ////
		var row_length = Math.floor(c / (hexagon.r * 3));
		row_sizes.push( row_length  )
	}
	
	console.log("circle_r : "+circle.r);
	console.log("hex_r : "+hexagon.r);
	console.log("max_hex_H : "+max_hex_H);
	console.log("max_hex_W : ", row_sizes)

	for(var row = 0; row < row_sizes.length; row++){
		var max_hex_W = row_sizes[row];
		
		var x_offset = Math.floor((c_w - (max_hex_W * hex_w)) / 2);
		
		for(var col = 1; col < max_hex_W; col++){
			hexagon.pos.x =  (col * hex_w) + (circle.pos.x - circle.r) + x_offset ;
			hexagon.pos.y =  (row * hex_h) + (circle.pos.y - circle.r);
			ctx.fillText(row+""+col, hexagon.pos.x - 6, hexagon.pos.y+4);
			drawHexagon(hexagon)
		}
	}
}

function drawHexagon(hex){
	var angle_deg, angle_rad, cor_x, cor_y;
	ctx.beginPath();
	for(var c=0; c <= 5; c++){
		angle_deg = 60 * c;
		angle_rad = (Math.PI / 180) * angle_deg;
		cor_x = hex.r * Math.cos(angle_rad); //// corner_x ///
		cor_y = hex.r* Math.sin(angle_rad); //// corner_y ///
		if(c === 0){
			ctx.moveTo(hex.pos.x+ cor_x, hex.pos.y+cor_y);
		}else{
			ctx.lineTo(hex.pos.x+cor_x, hex.pos.y+cor_y);
		}
	}
	ctx.closePath();
	ctx.stroke();
}

function drawCircle( circle ){
	ctx.beginPath();
	ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
	ctx.stroke();
}


  $(function() {
    $( "#slider" ).slider({
		max: 200,
		min:0,
		value:100,
		create: function( event, ui ) {
			$("#value").html( $(this).slider('value') );
		},
		change: function( event, ui ) {
			$("#value").html(ui.value);
		},
		slide: function( event, ui){
			$("#value").html(ui.value);
			circle.r = ui.value;
			ctx.clearRect(0,0, canvas_width, canvas_height);
			fill_CircleWithHex(circle);
		}
	});
  });
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
	<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:6)

以下解决了以圆的中点为中心的常规蜂窝结构的包装问题。常规意味着:

  • 所有六边形的集合在围绕圆心的60度旋转下是对称的。

各个六边形的坐标代表从中心反击的六边形壳的序号和从中午开始的顺时针序列号。

随着圆圈变宽,新的六边形壳体不一定会被填满。尽管填充外壳的自由度部分地产生了改进的解决方案,但它仍然不是最佳的。将旋转对称性的规律性放宽到60度(即120度和180度)以外的其他角度将允许更高的圆圈内部覆盖。

我将研究这个代码的下一个版本背后的数学(并且可能找到一个定理来证明圆周围的旋转对称性; smidpoint是最优性的必要条件)。

&#13;
&#13;
var c_el;
var ctx;
var canvas_width;
var canvas_height;
var circle;
var hexagon;
var hex_w;
var hex_h;
var hex_s;
var delta;

function drawHexagonAt ( po_ctr_hex, pn_circle, pn_sector ) {
    var cur
      ;
      
    cur = { x: po_ctr_hex.x - 0.5 * hexagon.r, y: po_ctr_hex.y - delta };
    
    ctx.beginPath();
    ctx.moveTo(cur.x, cur.y);
    cur.x = cur.x + hexagon.r;
    cur.y = cur.y;
    ctx.lineTo(cur.x, cur.y);
    cur.x = cur.x + hexagon.r / 2;
    cur.y = cur.y + delta;
    ctx.lineTo(cur.x, cur.y);
    cur.x = cur.x - hexagon.r / 2;
    cur.y = cur.y + delta;
    ctx.lineTo(cur.x, cur.y);
    cur.x = cur.x - hexagon.r;
    cur.y = cur.y;
    ctx.lineTo(cur.x, cur.y);
    cur.x = cur.x - hexagon.r / 2;
    cur.y = cur.y - delta;
    ctx.lineTo(cur.x, cur.y);
    cur.x = cur.x + hexagon.r / 2;
    cur.y = cur.y - delta;
    ctx.lineTo(cur.x, cur.y);
	ctx.closePath();
	ctx.stroke();

    cur.x = cur.x + hexagon.r / 2;
    cur.y = cur.y + delta;
	ctx.fillText(pn_circle + "/" + pn_sector, cur.x-6, cur.y+4);
} // drawHexagonAt

function fill_CircleWithHex(circle){
	drawCircle( circle );
	
	var radacc2;
	var iter    = 0;
	var sector  = 0;
	var i, j;
	var ctr     = { x: circle.pos.x , y: circle.pos.y };
	var cur     = { x: 0            , y: 0 };
	
	delta   = Math.floor(Math.sqrt(3) * 0.5 * hexagon.r);
    radacc2 = hexagon.r * hexagon.r;
	while ( (radacc2 < circle.r * circle.r) ) {
	    cur.x = ctr.x;
	    cur.y = ctr.y - iter * 2 * delta;
	    
	    if (iter === 0) {
	        drawHexagonAt ( cur, 0, 0 );
	    }
	    else {
    	    for ( var i=0; i < 6; i++ ) {
    	        // j-loops -- next honeycomb
    	        sector = 0;
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x + 1.5 * hexagon.r;
    	            cur.y = cur.y + delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        }
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x;
    	            cur.y = cur.y + 2 * delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        } 
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x - 1.5 * hexagon.r;
    	            cur.y = cur.y + delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        } 
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x - 1.5 * hexagon.r;
    	            cur.y = cur.y - delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        } 
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x;
    	            cur.y = cur.y - 2 * delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        } 
    	        for ( var j=0; j < iter; j++ ) {
    	            cur.x = cur.x + 1.5 * hexagon.r;
    	            cur.y = cur.y - delta;
    	            drawHexagonAt ( cur, iter, sector++ );
    	        }
    	    } // i-loop -- meta-honeycomb
    	} // if -- Iteration 1 vs. n > 1
 	    
 	    // radacc update
 	    iter++;
        radacc2 = ((2*iter + 1) * delta) * ((2*iter + 1) * delta) + hexagon.r * hexagon.r / 4;
    } // while -- komplette Shells
    
    //
    //  Partielle Shells
    //
    var proceed;
    do {
	    cur.x   = ctr.x;
	    cur.y   = ctr.y - iter * 2 * delta;
        proceed = false;

	    for ( var i=0; i < 6; i++ ) {
	        // j-loops -- next honeycomb
	        sector = 0;
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x + 1.5 * hexagon.r;
	            cur.y = cur.y + delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        }
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x;
	            cur.y = cur.y + 2 * delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        } 
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x - 1.5 * hexagon.r;
	            cur.y = cur.y + delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        } 
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x - 1.5 * hexagon.r;
	            cur.y = cur.y - delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        } 
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x;
	            cur.y = cur.y - 2 * delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        } 
	        for ( var j=0; j < iter; j++ ) {
	            cur.x = cur.x + 1.5 * hexagon.r;
	            cur.y = cur.y - delta;
	            sector++
	            if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
	                drawHexagonAt ( cur, iter, sector );
	                proceed = true;
	            }
	        }
	    } // i-loop -- meta-honeycomb
	    
	    iter++;
    } while (proceed && (iter < 15));       
    
} // fill_CircleWithHex


function drawCircle( circle ){
	ctx.beginPath();
	ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
	ctx.stroke();
}


  $(function() {
    $( "#slider" ).slider({
		max: 200,
		min:0,
		value:100,
		create: function( event, ui ) {
			$("#value").html( $(this).slider('value') );
		},
		change: function( event, ui ) {
			$("#value").html(ui.value);
		},
		slide: function( event, ui){
			$("#value").html(ui.value);
			circle.r = ui.value;
			ctx.clearRect(0,0, canvas_width, canvas_height);
			fill_CircleWithHex(circle);
		}
	});
  });
  
$(document).ready(function () {
    c_el = document.getElementById("myCanvas");
    ctx = c_el.getContext("2d");
    
    canvas_width = c_el.clientWidth;
    canvas_height = c_el.clientHeight;

    circle = {
    	r: 120, /// radius 
    	pos: {
    		x: (canvas_width / 2),
    		y: (canvas_height / 2)
    	}
    };
    
    hexagon = {
    	r: 20,
    	pos:{
    		x: 0,
    		y: 0
    	}
    };
    
    hex_w = hexagon.r * 2;
    hex_h = Math.floor( Math.sqrt(3) * hexagon.r );
    hex_s =  (3/2) * hexagon.r;
    
    fill_CircleWithHex( circle );
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

花了一些时间在你的代码上打包十六进制。它并不完美,我相信有更好的方法可以做到这一点。检查它是否有帮助,或者你是否可以修复十六进制踩出圆圈[现在,计算row_sizes存在问题]。也许只要我有时间,我就可以再看一遍,或者我们可以看看其他方法来做到这一点。

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 circle = {
	r: 120, /// radius 
	pos: {
		x: (canvas_width / 2),
		y: (canvas_height / 2)
	}
}

var hexagon = {
	r: 20,
	pos:{
		x: 0,
		y: 0
	}
}

var hex_w = hexagon.r * 3; /// added spacing
var hex_h = Math.floor( Math.sqrt(3) * hexagon.r / 2 ); /// added spacing 
var hex_s =  (3/2) * hexagon.r;

var hex_width = 33.4; //based on r = 20

fill_CircleWithHex( circle );

function fill_CircleWithHex(circle){
	drawCircle( circle );
	
	var c_h = circle.r * 2; /// circle height ////
	var c_w = c_h; //// circle width /////

	var max_hex_H = Math.round( c_h /  ( hex_h  ));
	var row_sizes = []
  
	for(var col= 0; col < max_hex_H; col++){
		
		var d = circle.r - ( col * hex_h );  //// distance from circle's center to the row's chord ////
		var c = 2 * (Math.sqrt((circle.r*circle.r) - (d * d))); ///  length of the row's chord ////
		
		row_sizes.push( Math.ceil(c / (hexagon.r * 3)) )
	}

	for(var row = 0; row < row_sizes.length; row++){
		var max_hex_W = row_sizes[row];
    console.log(hex_w);
		var x_offset =  Math.floor((c_w - (max_hex_W * hex_w))) + row%2 * hex_width - hex_width/2; // changed offset to define a zig zag
		
		for(var col = 1; col < max_hex_W; col++){
			hexagon.pos.x =  (col * hex_w) + (circle.pos.x - circle.r) + x_offset ;
			hexagon.pos.y =  (row * 17.3) + (circle.pos.y - circle.r) ;
			ctx.fillText(row+""+col, hexagon.pos.x - 6, hexagon.pos.y+4);
			drawHexagon(hexagon)
		}
	}
}

function drawHexagon(hex){
	var angle_deg, angle_rad, cor_x, cor_y;
	ctx.beginPath();
 
	for(var c=0; c <= 5; c++){
		angle_deg = 60 * c;
		angle_rad = (Math.PI / 180) * angle_deg;
		cor_x = hex.r * Math.cos(angle_rad); //// corner_x ///
		cor_y = hex.r* Math.sin(angle_rad); //// corner_y ///
		if(c === 0){
			ctx.moveTo(hex.pos.x+ cor_x, hex.pos.y+cor_y);
		}else{
			ctx.lineTo(hex.pos.x+cor_x, hex.pos.y+cor_y);
		}
	}
	ctx.closePath();
	ctx.stroke();
}

function drawCircle( circle ){
	ctx.beginPath();
	ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
	ctx.stroke();
}


  $(function() {
    $( "#slider" ).slider({
		max: 200,
		min:0,
		value:100,
		create: function( event, ui ) {
			$("#value").html( $(this).slider('value') );
		},
		change: function( event, ui ) {
			$("#value").html(ui.value);
		},
		slide: function( event, ui){
			$("#value").html(ui.value);
			circle.r = ui.value;
			ctx.clearRect(0,0, canvas_width, canvas_height);
			fill_CircleWithHex(circle);
		}
	});
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
	<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>

答案 2 :(得分:0)

简短的回答:没有简单的方法可以做到这一点。你有太多特殊情况。为了说明这一点,从1个,2个,3个,4个,6个和7个六边形开始,绘制适合它们的最小圆,并记下圆的中心最终的位置。

正如你所看到的,圆圈的中心移动了很多。它可以最终位于六边形的中间,顶点或交叉点上。

从那以后它只会变得更糟。

我能找到的最接近这个问题的是this page

编辑:您可能需要查看以下博客页面,以便对编程中的六边形进行非常全面的处理。

http://www.redblobgames.com/grids/hexagons/