选择一个六边形附近的六边形

selecting nearby hexes around a hex

本文关键字:六边形 一个 选择      更新时间:2023-09-26

我正在尝试选择给定范围内的所有六进制。然而,在实现Amit Patel页面上的代码时,我得到了奇怪的结果。

var results = []
for each -N ≤ dx ≤ N:
    for each max(-N, -dx-N) ≤ dy ≤ min(N, -dx+N):
        var dz = -dx-dy
        results.append(cube_add(center, Cube(dx, dy, dz))) 

这就是我目前所拥有的:

    var center = this._cel.copy( hex.coords );
    var dx = range - center.q;
    var dy = range - center.r;
    var results = [];
    for (var q = -range; q <= dx; q++ ) {
        var r1 = Math.max(-range, -q - range);
        var r2 = Math.min(range, -q + range);
        for ( var r = r1; r <= r2; r++ ) {
            //console.log( q, r, -q-r )
            var c = new Cell(q, r, -q-r) 
            results.push( c.add( center ) );
        }
    }

我想循环约束对我来说需要稍微修改一下,并利用dx,dy值。

<body>
	<canvas width="420px" height="420px" id="myCanvas" style="margin:0; padding:0; border:1px solid #d3d3d3;"></canvas>
</body>
<script id="hexagon">
function Point( pos ) {
    this.x = 0;
	this.y = 0;
	if( typeof( pos ) !== "undefined" ){
		this.x = pos[0];
		this.y = pos[1];
	}
};
function Cell( _q, _r, _s ){ //// direction ///
	this.q = _q;
	this.r = _r;
	this.s = _s;
	this._hashID = null;
	this.generateHashID();
}
Cell.prototype = {
	constructor: Cell,
	add: function( d ){
		this.q += d.q;
		this.r += d.r;
		this.s += d.s;
		this.generateHashID();
		return this;
	},
	copy: function( c ){
		this.set( c.q, c.r, c.s );
		return this;
	},
	set: function( _q, _r, _s ){
		this.q = _q;
		this.r = _r;
		this.s = _s;
		this.generateHashID();
		return this;
	},
	generateHashID: function(){
		this._hashID = this.q+"."+this.r+"."+this.s;
	},
	getHashID: function(){
		return this._hashID;
	},
	round: function(){
		var q = Math.trunc(Math.round(this.q));
		var r = Math.trunc(Math.round(this.r));
		var s = Math.trunc(Math.round(this.s));
		var q_diff = Math.abs(q - this.q);
		var r_diff = Math.abs(r - this.r);
		var s_diff = Math.abs(s - this.s);
		if (q_diff > r_diff && q_diff > s_diff){
			q = -r - s;
		}else if (r_diff > s_diff){
			r = -q - s;
		}else{
			s = -q - r;
		}
		
		return this.set( q, r, s );
	}
}
var Hex = function( coords, l_ ){ //// [axial], [cartesian] , layout
	this.coords = new Cell( coords[0], coords[1], coords[2] );
	
	this.content = -2;
	
	this.pos = this.coords; //// set primary coorinate type ///
	
	this.neighbors = [];
	
	this.layout = l_;
	this.corners = [];
	
	this.center = this.get_center_p();
	
	//this.id = this.generate_id( cart_coord );
	this.colors = {
		"base" : {
			filling : "#008844",
			border : "#FFDD88",
		},
		"selected": {
			filling: "#00cc00"
		},
		"hovered": {
			filling: "#006600"
		},
		"path" : {
			filling: "#80ff00"
		},
		"obstacle" : {
			filling: "#86592d"
		},
		"neighbor": {
			filling: "#ffbf00"
		}
	}
	
	this.states = {
		"selected" : false,
		"hovered" : false,
		"isPath": false,
		"isObstacle": false,
		"isNeighbor": false
	}
	this.generate_corners();
};
Hex.prototype = {
	constructor: Hex,
	get_corner_offset: function( corner ){
		var angle = 2.0 * Math.PI * (corner + this.layout.orientation.start_angle) / 6;
		return new Point( [ size.x * Math.cos(angle), size.y * Math.sin(angle) ] );
	},
	
	generate_corners: function( h ){
		var offset = null, angle = 0;
		var size = this.layout.size;
		for (var i = 0; i < 6; i++) {
			angle = 2.0 * Math.PI * (i + this.layout.orientation.start_angle) / 6;
			offset = new Point( [ size.x * Math.cos(angle), size.y * Math.sin(angle )] );
			
			this.corners.push( 
				new Point( [ this.center.x + offset.x, this.center.y + offset.y ] )
			);
		}
	},
	
	draw: function( ctx ){
		var points = this.corners;
		ctx.beginPath();
		ctx.moveTo( points[0].x, points[0].y );
		for(var i = 1; i < points.length; i++){
			var p = points[i];
			ctx.lineTo(p.x, p.y);
		}
		ctx.closePath();
		////  fill Hex ///
		if( this.checkState("selected") ){
			ctx.fillStyle = this.colors.selected.filling;
		}else if(  this.checkState("hovered") ){
			ctx.fillStyle = this.colors.hovered.filling;
		}else if(  this.checkState("isPath") ){
			ctx.fillStyle = this.colors.path.filling;
		}else if(  this.checkState("isNeighbor") ){
			ctx.fillStyle = this.colors.neighbor.filling;
		}else if(  this.checkState("isObstacle") ){
			ctx.fillStyle = this.colors.obstacle.filling;
		}else{
			ctx.fillStyle =  this.colors.base.filling;
		}
		ctx.fill();
		//// draw border ///
		ctx.lineWidth = 1;
		ctx.strokeStyle = "#19334d";
		ctx.stroke();
		
		this.draw_coords( ctx );
		
		this.draw_center_point( ctx );
	},
	
	add_neighbor: function( neighbor ){
		this.neighbors.push( neighbor );
	},
	
	show_neighbors: function(){
		for( var nb = 0, nb_l = this.neighbors.length; nb < nb_l; nb++ ){
			this.neighbors[nb].changeState("isNeighbor", true);
		}
	},
	
	hide_neighbors: function(){
		for( var nb = 0, nb_l = this.neighbors.length; nb < nb_l; nb++ ){
			this.neighbors[nb].changeState("isNeighbor", false);
		}
	},
	
	draw_coords: function( ctx ){
		var text = this.coords.q+" : "+ this.coords.s;
		var text_z =  this.coords.r;
		var metrics1 = ctx.measureText(text);
		var metrics2 = ctx.measureText(text_z);
		var w1 = metrics1.width;
		var w2 = metrics2.width;
		var h = 8;
		ctx.font = h+'pt Calibri bold';
		ctx.textAlign = 'center';
		ctx.fillStyle = '#FFFFFF';
		ctx.fillText(text, this.center.x, this.center.y + (h/2) - 5 );
		ctx.fillText(text_z, this.center.x, this.center.y + (h/2) + 7 );
	},
	
	get_center_p: function(){
		var M = this.layout.orientation;
		var x = ( M.f0 * this.pos.q + M.f1 * this.pos.r ) * this.layout.size.x;
		var y = ( M.f2 * this.pos.q + M.f3 * this.pos.r ) * this.layout.size.y;
		return new Point([
			x + this.layout.origin.x, 
			y + this.layout.origin.y 
		]);
	},
	
	draw_center_point: function( ctx ){
		ctx.beginPath();
		ctx.lineWidth="1";
		ctx.fillStyle="red";
		ctx.arc( this.center.x , this.center.y , 2, 0 ,2*Math.PI);
		ctx.closePath();
		ctx.stroke();
		ctx.fill();
	},
	
	generate_id: function( coords ){
		return parseInt( coords[0]+''+coords[1] );
	},
	
	checkState: function( state ){
		return this.states[ state ];
	},
	
	changeState: function( state , value){
		this.states[ state ] = value;
	},
	
	trigger: function( ev_name ){
		if( this.events[ ev_name ] ){
			this.events[ ev_name ].call( this );
		}
	},
	
	setContent: function( type ){
		this.content = type;
		this.changeState( "isObstacle" , true );
	},
	
	hover: function(){
		if( ! this.checkState("isPath") ){
			this.trigger("hover");
		}
	},
	
	clear_hover: function(){
		if( ! this.checkState("isPath") ){
			this.trigger("clear_hover");
		}
	},
	
	select: function(){
		this.trigger("select");
		//this.show_neighbors();
	},
	
	unselect: function(){
		this.trigger("unselect");
	},
	
	events: {
		select: function(){
			this.changeState("selected", true);
			this.changeState("hovered", false);
		},
		unselect: function(){
			this.changeState("selected", false);
		},
		hover: function(){
			this.changeState("hovered", true);
		},
		clear_hover: function(){
			this.changeState("hovered", false);
		}
	}
};
</script>
<script id="grid">
var Grid = function( size, hex_size, origin, ctx_pos, layout_type ){
	this.size = size;
	this.grid_r = size/2;
	
	this.layout_type = layout_type;
	this.layout = this.set_layout( this.layout_types[this.layout_type], hex_size, origin );
	
	this.hexes = [];
	
	this.hovered = [null, null]; //// [cur, prev] ///
	this.selected = [null, null]; ///// [cur , prev] ///
	
	this.dots = [];
	
	this._list = [];
	this._cel = new Cell();
	
	this._directions = [new Cell(+1, 0, -1), new Cell(+1, -1, 0), new Cell(0, -1, +1),
						new Cell(-1, 0, +1), new Cell(-1, +1, 0), new Cell(0, +1, -1)];
	
	this.generate();
	this.add_neighbors();
	
	this.mouse = new Point();
	this.mouse_events( new Point( ctx_pos ) );
}
Grid.prototype = {
	constructor: Grid,
	layout_types: {
		"pointy": [ 
			[ Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0], //// 2x2 forward matrix  
			[ Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0], ///// 2x2 inverse matrix 
			0.5
		], //// starting angle in multiples of 60° /////
		"flat": [ 
			[3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0)], //// 2x2 forward matrix  
			[2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0], ///// 2x2 inverse matrix 
			1.0
		]
	},
	set_layout: function( orn_type , hex_s_, ogn_  ){
		return {
			orientation: this.set_orientation( orn_type ), ///// orientation type ///
			size: new Point( [ hex_s_ , hex_s_ ] ), ///// hex size ///
			origin: new Point( ogn_ ) //// Grid center /////
		}
	},
	set_orientation: function( opts ){ /// [0] : forward_matrix, [1] : inverse_matrix, [2] : starting_angle
		return {
			f0: opts[0][0], f1: opts[0][1], f2: opts[0][2], f3: opts[0][3], b0: opts[1][0], b1: opts[1][1], b2: opts[1][2], b3: opts[1][3], start_angle: opts[2]
		}
	},
	
	get_hex_at_p: function( p ){ //// point ///
		var M = this.layout.orientation;
		var pt = new Point( [ (p.x - this.layout.origin.x) / this.layout.size.x,  (p.y - this.layout.origin.y) / this.layout.size.y ] );
		var q = M.b0 * pt.x + M.b1 * pt.y;
		var r = M.b2 * pt.x + M.b3 * pt.y;
		var c = this._cel.set( q, r, -q-r );
		return c.round();
	},
	
	generate: function(){
		var n_hex = null; 
		for (var q = -this.grid_r; q <= this.grid_r; q++) {
			var r1 = Math.max(-this.grid_r, -q - this.grid_r);
			var r2 = Math.min(this.grid_r, -q + this.grid_r);
			for (var r = r1; r <= r2; r++) {
				n_hex = new Hex( [ q, r, -q-r ], this.layout );
				this.hexes[ n_hex.coords.getHashID() ] = n_hex;
			}
		}
	},
	
	_selectHexesInRange: function( hex,  range ){
		var center = this._cel.copy( hex.coords );
		var dx = range - center.q;
		var dy = range - center.r;
		
		var results = [];
		for (var q = -range; q <= dx; q++ ) {
			var r1 = Math.max(-range, -q - range);
			var r2 = Math.min(range, -q + range);
			for ( var r = r1; r <= r2; r++ ) {
				var c = new Cell(q, r, -q-r) 
				results.push( c.add( center ) );
			}
		}
		
		for( var h in results){
			if( typeof( this.hexes[results[h].getHashID()]) !== "undefined" ){
				 this.hexes[results[h].getHashID()].select()
			}
		}
		//console.log( results )
	},
	
	hex_corner_offset : function ( corner ) {
		var size = this.layout.size;
		var angle = 2.0 * Math.PI * (this.layout.orientation.start_angle - corner) / 6;
		return new Point([size.x * Math.cos(angle), size.y * Math.sin(angle)]);
	},
	
	point_add : function(p, q) {
		return new Point([p.x + q.x, p.y + q.y]);
	},
	add_neighbors: function(){
		var nbor = null, hex = null;
		for( var h in this.hexes ){
			hex = this.hexes[h];
			var i, n, l = this._directions.length;
			this._list.length = 0;//// reset array ///
			for ( i = 0; i < l; i++ ) {
				this._cel.copy( hex.coords );
				this._cel.add( this._directions[i] );
				n = this.hexes[ this._cel.getHashID() ];
				if (typeof(n) == "undefined") { ///// if doesn't exists ////
					this._list.push( null );
					continue;
				}
				this._list.push(n);
			}
			
			hex.neighbors = this._list.slice(); //// take copy of the array ////
		}
	},
	
	draw: function( ctx ){
		for( var h in this.hexes ){
			this.hexes[h].draw( ctx );
		}
	},
	
	checkCollisions: function(){
		var h_pos = this.get_hex_at_p( this.mouse );
		var hex = this.hexes[ h_pos.getHashID() ];
		if( typeof(hex) !== "undefined" ){
			if( this.hovered[0] == null ){ //// cur 
				this.hovered[0] = hex;
				this.hovered[0].hover();
			}else{
				this.hovered[1] = this.hovered[0];
				this.hovered[0] = hex;
				if( this.hovered[0].coords._hashID != this.hovered[1].coords._hashID ){
					this.hovered[1].clear_hover();
					this.hovered[1] = null;
				}
			}
			this.hovered[0].hover();
		}
	},
	
	mouse_events: function( ctx_pos ){
		var self = this;
		
		window.addEventListener( 'mousemove', function(e){
			self.mouse.x = ( e.clientX - ctx_pos.x );
			self.mouse.y = ( e.clientY - ctx_pos.y );
		});
		
		window.addEventListener( 'mousedown', function(e){
			//console.log( "neighbors : ",self.hovered[0].neighbors )
			self._selectHexesInRange( self.hovered[0], 2);
		});
	}
}
</script>
<script id="main">
var c_el = document.getElementById("myCanvas");
var ctx = c_el.getContext("2d");
var nGrid = new Grid( 6, 25, [ c_el.width / 2, c_el.height / 2 ], [c_el.getBoundingClientRect().left, c_el.getBoundingClientRect().top], "pointy" );
function animate(){
	window.requestAnimationFrame( animate );
	ctx.clearRect(0, 0, c_el.width, c_el.height);
	nGrid.checkCollisions();
	nGrid.draw( ctx);
}
animate();
</script>

只需获取坐标x、y、z,然后搜索所有字段以进行以下计算之一:

  1. z是常量,x是正/负1,y是负/正1
  2. x是常量,z是正/负1,y是负/正1
  3. y是常量,z是正/负1,x是负/正1

这应该是所有邻居

示例:归档0,1,-1(x,y,z)

  1. z是常量,x是加/减1=>[-1,2,-1],[1,0,-1]
  2. x是常量,z是正/负1=>[0,2,-2],[0,0,0]
  3. y是常量,z是加/减1=>[1,1,-2],[-1,1,0]

为了更快的搜索,你可以将你的单元格存储在这样的对象中:

var cells = new Object();
for(x=-2; x<3; x++){
    cells[x] = new Object();
    for(y=-2; y<3; y++){
        cells[x][y] = new Object();
        for(z=-2; z<3; z++){
            cells[x][y][z] = cell;
        }
    }
}

然后你可以通过坐标访问单元格:

var x = 0;
var y = 1;
var z = -1;
var neighbors = new Array();
neighbors.push(cells[x-1][y+1][z]);
neighbors.push(cells[x+1][y-1][z]);
neighbors.push(cells[x][y+1][z-1]);
neighbors.push(cells[x][y-1][z+1]);
neighbors.push(cells[x+1][y][z-1]);
neighbors.push(cells[x-1][y][z+1]);