2017-06-17 115 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 
*/