2017-06-17 44 views
4

我有用六角小单元制成的六边形。每个六边形都有一个单位六角形的hex number。前几个编号,如:在六角形上旋转指标

Size 1: 
0 
Size 2: 
    0 1 
2 3 4 
    5 6 
Size 3: 
    0 1 2 
    3 4 5 6 
7 8 9 A B 
    C D E F 
    101112 

(最后一个是十六进制)。

您可以将此旋转60度的倍数,以将每个索引映射到旋转索引。这是他们顺时针旋转60度。

Size 1: 
0 
Size 2: 
    2 0 
5 3 1 
    6 4 
Size 3: 
    7 3 0 
    C 8 4 1 
10 D 9 5 2 
11 E A 6 
    12 F B 

我的问题是怎么样?我有这两个函数的十六进制功能和反向十六进制功能:

function hex(n) { 
    return 3 * +n * (+n + 1) + 1; 
} 

function reverse_hex(n) { 
    n = (+n - 1)/3; 
    var i = Math.floor(Math.sqrt(n)); 
    // null if not a hex number 
    return i * (i + 1) === n ? i : null; 
} 

我可以很容易地做0度和180度的旋转。我可以从60度旋转几次后得出60度的其他倍数。

function rotate(index, direction, size) { 
    // The unit of direction is 60 degrees. So "1" == rotate by 60 degrees. 
    direction = ((+direction % 6) + 6) % 6; 
    switch (direction) { 
     case 0: 
      return index; 
     case 1: 
      // Something? 
      return transformed_index; 
     case 2: 
      return rotate(rotate(index, 1, size), 1, size); 
     case 3: 
      return hex(size) - index - 1; 
     case 4: 
      return rotate(rotate(index, 3, size), 1, size); 
     case 5: 
      return rotate(rotate(index, 3, size), 2, size); 
     default: // (NaN or +/-Infinity) % 6 is NaN 
      return null; 
    } 
} 

但我想不出一个算法来做到这一点。

回答

2

的一种方式是安排十六进制在环,每个环作为一个数组,从1到6的环中,依此类推。要旋转,请从每个环阵列的顶部移至底部。所以如果你有一个4号的六角形,那么外圈从外圈的顶部向底部移动3,然后从下一圈进入2,依此类推。

这确实使得获取2D索引变得非常棘手。你可以通过创建第二行数组来解决这个问题。每一行都是环形结构中的索引数组。因此,如果您希望第2行第4行的单元格位于左侧,请查找数组pos [2] [4]以获取环索引。在这个例子中,我编码了环索引,所以你只需要一个数字来查找环,然后在环中定位。

该示例显示了一个十六进制的大小5,其编号为从左到右,然后是从左到右的下一行。六角旋转了60度。

const ctx = canvas.getContext("2d"); 
 
const font = "arial"; 
 
const fontSize = 14; 
 

 
function createHex(size) { 
 
    // create object to hold a hexagon 
 
    const hexagon = { 
 
    count: 0, 
 
    hex: [], 
 
    }; 
 
    // do first two rows manualy 
 
    if (size >= 1) { 
 
    hexagon.hex.push([0]); 
 
    hexagon.count += 1; 
 
    } 
 
    if (size >= 2) { 
 
    hexagon.hex.push([0, 1, 2, 3, 4, 5]); 
 
    hexagon.count += 6; 
 
    } 
 
    // keep adding rings until correct size 
 
    for (var i = 3; i <= size; i++) { 
 
    const ring = []; 
 
    for (var j = 0; j < i * 2 + 2 + (i - 2) * 4; j++) { 
 
     ring.push(j); 
 
    } 
 
    hexagon.hex.push(ring); 
 
    hexagon.count += ring.length; 
 
    } 
 
    // get the max rign size to use as modulo for row column lookup 
 
    hexagon.maxRingLen = size * 2 + 2 + (size - 2) * 4 
 
    // create an array for row column lookup 
 
    hexagon.pos = []; 
 
    // pos to prevent the array from becoming a sparse array 
 
    // create each row array and fill with dummy data 
 
    for (var i = 0; i < size + size - 1; i++) { 
 
    const row = []; 
 
    for (var j = 0; j < ((size + size - 1) - (Math.abs((size - 1) - i) - 1)) - 1; j++) { 
 
     row.push(0); // add dummy data 
 
    } 
 
    hexagon.pos.push(row); 
 
    } 
 
    // this array contains row, column steps for the six ring sizes 
 
    const steps = [1, 0, 1, 1, -1, 1, -1, 0, 0, -1, 0, -1]; 
 
    // each ring starts at the top left and goes round clockwise 
 
    for (var i = 0; i < size; i++) { 
 
    const ringIndex = size - 1 - i 
 
    const ring = hexagon.hex[ringIndex]; 
 
    var x = size - 1 - ringIndex; 
 
    var y = size - 1 - ringIndex; 
 
    for (var j = 0; j < ring.length; j++) { 
 
     // add the ring position index 
 
     hexagon.pos[y][x] = ringIndex * hexagon.maxRingLen + j 
 
     // find the next row column pos 
 
     const side = Math.floor(j/ringIndex) * 2; 
 
     x += steps[side]; 
 
     y += steps[side + 1]; 
 
    } 
 
    } 
 
    // now that we have the row column lookup you can 
 
    // create the correct sequence of numbers in the hexagon 
 
    // starting at top left moving from left to right all the way to the 
 
    // bottom right last number 
 
    var count = 0; 
 
    for (var i = 0; i < hexagon.pos.length; i++) { 
 
    const row = hexagon.pos[i]; 
 
    for (var j = 0; j < row.length; j++) { 
 
     const ringPos = row[j] % hexagon.maxRingLen; 
 
     const ringIndex = Math.floor(row[j]/hexagon.maxRingLen); 
 
     hexagon.hex[ringIndex][ringPos] = count++; 
 
    } 
 

 
    } 
 
    return hexagon; 
 
} 
 
// rotates a hexagon 60deg 
 
function rotateHex(hexagon) { 
 
    const size = hexagon.hex.length; 
 
    for (var i = 1; i < size; i++) { // from inner ring do each ring 
 
    const ring = hexagon.hex[i]; 
 
    for (var j = 0; j < i; j++) { 
 
     // move the top to bottom of ring array 
 
     ring.unshift(ring.pop()); 
 
    } 
 
    } 
 
} 
 

 
// just renders for testing. 
 
function renderHex(hexagon, pos) { 
 
    const steps = [1, 0, 0.5, 1, -0.5, 1, -1, 0, -0.5, -1, 0.5, -1] 
 
    ctx.font = (fontSize-4) + "px " + font; 
 
    ctx.textAlign = "center"; 
 
    ctx.textBaseline = "middle"; 
 
    const size = hexagon.length; 
 

 
    for (var i = 0; i < size; i++) { 
 
    const ringIndex = size - 1 - i 
 
    const ring = hexagon[ringIndex]; 
 
    var x = pos.x - (ringIndex * fontSize * 0.5); 
 
    var y = pos.y - (ringIndex * fontSize); 
 
    for (var j = 0; j < ring.length; j++) { 
 
     ctx.fillText(ring[j].toString(36), x, y); 
 
     const side = Math.floor(j/ringIndex) * 2; 
 
     x += steps[side] * fontSize; 
 
     y += steps[side + 1] * fontSize; 
 

 
    } 
 

 
    } 
 
} 
 

 
var h = createHex(5); 
 
renderHex(h.hex, { 
 
    x: canvas.width * (1/4), 
 
    y: canvas.height * (2/4) 
 
}); 
 
rotateHex(h); 
 
renderHex(h.hex, { 
 
    x: canvas.width * (3/4), 
 
    y: canvas.height * (2/4) 
 
});
<canvas id="canvas"></canvas>

0
function rotatepos(rotations,size){ 
size=1 /*the size of resolve */ -size; 
return function(x,y){ 
    for(var i=0;i<rotations;i++){ 
    var resolve=[ 
     [[0,1],[1,2]], 
    [[0,0],[1,1],[2,1]],//thats just working for size 1, may extend this 
     [[1,0],[2,0]] 
    ]; 

    [x,y]= resolve[x+size][y+size]; 
    x-=size; 
    y-=size; 
    } 
return [x,y]; 
} 

所以,你可以这样做:

var rotator=rotatepos(1,1); 
var [x,y]=rotator(1,1);//1,1 as the middle one does not change its position. 

要旋转你的六边形二维数组:

var rotator=rotatepos(1,arr[0].length-1); 
var rotated=arr.reduce(function(newone,inner,x){ 
inner.forEach(function(v,y){ 
    var [newx,newy]=rotator(x,y); 
    (newone[newx]=newone[newx]||[])[newy]=v; 
    }); 
    return newone 
    },[]); 

我承认这个心不是在elegantest解决方案...(因为它需要你建立一个最大的六角大小lookuptable)

0

另一种方法: 大小0厕所KS这样的:

[1] 

所以howeger我们旋转,其结果是:

[1] 

尺寸1看起来是这样的:

[1,2] 
[6,X,3] 
[5,4] 

我们可以通过创建一个数组旋转外值并将它们移位:

[1,2,3,4,5,6]=>[6,1,2,3,4,5] 

并将它们重新分配给我们的六边形。可以简单地将X传递给六角形尺寸0旋转变压器。这可以被堆叠,所以我们的大小Ñ溶液完成:

function rotate(rows,rotations){ 
if(rows.length==1) return rows;//the size 0 resolver 
var sidelength=rows[0].length-1; 
var splitting=sidelength*rotations; 
var inner=[]; 
var around=rows[0]; 
var leftside=[]; 
for(var y=1;y<rows.length-1;y++){ 
    var row=rows[y]; 
    leftside.push(row[0]); 
    around.push(row[row.length-1]); 
    inner.push(row.slice(1,row.length-1)); 
} 
around=around.concat(rows[rows.length-1]).concat(leftside.reverse()); 
inner=rotate(inner,rotations); 
around.unshift(...around.splice(around.length-splitting)); 
//reassemble 
inner.unshift(around.splice(0,sidelength-1)); 
for(var y=0;y<inner.length;y++){ 
    inner[y].unshift(around.pop()); 
    inner[y].push(around.shift(1)); 
} 
inner=inner.concat([around]); 
return inner; 
} 

直播例如: http://jsbin.com/dalapocelo/edit?console(尺寸1) http://jsbin.com/zabewopuze/1/edit?console(尺寸2)

1

我们可以用三角在O(1)空间来计算旋转。下面的方法是关于中心的;要使用它,您可能需要偏移变量或可能重新定义索引概念。

例如:

// Return height, given number of units extending in a 60 deg angle 
function h(units){ 
    return units * Math.sqrt(3)/2; 
} 

// Return units extending in a 60 deg angle, given height 
function u(height){ 
    return height * 2/Math.sqrt(3); 
} 

// Return new x position and number of diagonal vertical units offset 
// after rotating 'num_rotations' * 60 degrees counter-clockwise, 
// given horizontal position and vertical unit. 
// (All in relation to the centre.) 

/* For example, 'rotate(3,1,1)', 
    where 'S' would be the starting position, '1' the ending position after 
    one rotation, '2' the ending position after two rotations, '3' the ending 
    position after three rotations, and 'C' the centre. 

    * * * * * 
    * * * 1 * * 
    * 2 * * * * * 
* * * * * * S * 
* * * * C * * * * 
* 3 * * * * * * 
    * * * * * * * 
    * * * * * * 
    * * * * * 
*/ 

function rotate(ring, vertical_units, num_rotations){ 
    let x = ring * 2, 
     y = h(vertical_units * 2), 
     _x = x - y/2, 
     r = Math.sqrt(Math.pow(_x, 2) + Math.pow(y, 2)), 
     theta = Math.atan2(y, _x), 
     new_x = r * Math.cos(theta + num_rotations * Math.PI/3), 
     new_y = r * Math.sin(theta + num_rotations * Math.PI/3), 
     new_x_pos = Math.round(new_x)/2, 
     new_vertical_units = Math.round(u(new_y))/2; 

    return {starting_x_pos: ring, 
      starting_vertical_units: vertical_units, 
      rotate: num_rotations * 60 + ' degrees', 
      new_x_pos: new_x_pos, 
      new_vertical_units: new_vertical_units}; 
} 

结果:

var result1 = rotate(3,1,1); 
for (var i in result1) 
    console.log(i + ': ' + result1[i]); 

console.log('') 

var result2 = rotate(3,1,2); 
for (var i in result2) 
    console.log(i + ': ' + result2[i]); 

console.log('') 

var result3 = rotate(3,1,3); 
for (var i in result3) 
    console.log(i + ': ' + result3[i]); 

/* 
starting_x_pos: 3 
starting_vertical_units: 1 
rotate: 60 degrees 
new_x_pos: 0.5 
new_vertical_units: 3 

starting_x_pos: 3 
starting_vertical_units: 1 
rotate: 120 degrees 
new_x_pos: -2 
new_vertical_units: 2 

starting_x_pos: 3 
starting_vertical_units: 1 
rotate: 180 degrees 
new_x_pos: -2.5 
new_vertical_units: -1 
*/