Rotate indices by hexagon

I have hexagons of small hexagons. Each hexagon has a hexadecimal number of hexagon units. The first few are numbered as:

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

(the latter is in hexadecimal format).

You can rotate this multiple of 60 degrees to match each index with the rotated index. That they rotate 60 degrees clockwise.

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

My question is: how? I have two functions for the hex function and the inverse hex function:

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;
}

I can easily rotate 0 degrees and 180 degrees. I can get other multiples of 60 degrees just by rotating 60 degrees several times.

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;
    }
}

But I can’t come up with an algorithm for this.

+4
4

, , , 1, 6, . , . , 4, 3 , 2 ..

2D. , . . , , 2, , pos [2] [4], . , , .

5, , . 60deg.

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>
+2

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
*/
+1
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.

2D- :

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
  },[]);

, ... ( )

0

: 0 :

[1]

, , :

[1]

1 :

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

, :

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

. X 0 × 6. , n :

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;
}

Live example: http://jsbin.com/dalapocelo/edit?console (size 1) http://jsbin.com/zabewopuze/1/edit?console (size 2)

0
source

Source: https://habr.com/ru/post/1679524/


All Articles