Hexplosive ASCII-art challenge

JavaScript (ES6), 118 117 bytes

n=>[...Array(m=n+--n)].map((_,i,a)=>a.map((_,j)=>(k=j-(i>n?i-n:n-i))<0?``:k&&++j<m?i/2%n?6:4:3+!i%n).join` `).join`\n`

Where \n represents a literal newline character. Explanation: Suppose n=4. We start with the following space-separated digit square:

0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0

The first |n-i| 0s are deleted, but the spaces remain:

   0 0 0 0
  0 0 0 0 0
 0 0 0 0 0 0
0 0 0 0 0 0 0
 0 0 0 0 0 0
  0 0 0 0 0
   0 0 0 0

Instant hexagon! It then suffices to calculate the appropriate value in place of each 0 by checking whether we're on the first or last row and/or column. Edit: Saved 1 byte thanks to @Arnauld.


MATL, 39 37 bytes

4*3-:!G:+o~YRtP*!tPw4LY)vtI5&lZ+47+*c

Try it online! Or verify all test cases.

Explanation

I get to use convolution again!

Consider input n = 3. The code first builds a matrix of size 4*n-3×n by adding the column vector [1; 2; ...; 9] to the row vector [1, 2, 3] with broadcast. This means computing a 2D array array of all pairwise additions:

 2  3  4
 3  4  5
 4  5  6
 5  6  7
 6  7  8
 7  8  9
 8  9 10
 9 10 11
10 11 12

Replacing even numbers by 1 and odd numbers by 0 gives the checkerboard pattern

1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1

This will be used for generating (part of) the hexagonal grid. Ones will represent points in the grid, and zeros will represent spaces.

The upper-right corner is removed by zeroing out all entries above the main "diagonal" of the matrix:

1 0 0
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1

Element-wise multiplying this matrix by a vertically flipped version of itself removes the lower-right corner as well. Transposing then gives

1 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 1 0 0

This begins to look like a hexagon. Using symmetry, the grid is extended to produce the upper half:

0 0 1 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
1 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 1 0 0

Now we need to replace each entry equal to one by the number of neighbours. For this we use convolution with a 3×5 neighbourhood (that is, the kernel is a 3×5 matrix of ones). The result,

2 3 4 5 5 5 4 3 2
4 5 7 7 8 7 7 5 4
4 6 7 8 7 8 7 6 4
4 5 7 7 8 7 7 5 4
2 3 4 5 5 5 4 3 2

has two issues (which will be solved later):

  1. The values have been computed for all positions, whereas we only need them at the positions of the ones in the zero-one grid.
  2. For each of those positions, the neighbour count includes the point itself, so it is off by 1.

The code now adds 47 to each computed value. This corresponds to subtracting 1 to solve issue (2) and adding 48 (ASCII for '0'), which converts each number to the code point of its corresponding char.

The resulting matrix is then multiplied by a copy of the zero-one grid. This solves issue (1) above, making the points that are not part of the hexagonal grid equal to zero again:

 0  0 51  0 52  0 51  0  0
 0 52  0 54  0 54  0 52  0
51  0 54  0 54  0 54  0 51
 0 52  0 54  0 54  0 52  0
 0  0 51  0 52  0 51  0  0

Finally, this array of numbers is cast to a char array. Zero chars are displayed as space, which gives the final result:

  3 4 3  
 4 6 6 4 
3 6 6 6 3
 4 6 6 4 
  3 4 3  

Python 2, 125 123 bytes

def h(n):m=n-1;t=[' '*(m-r)+' '.join(('46'[r>0]*(r+m-1)).join('34'[r%m>0]*2))for r in range(n)];print'\n'.join(t+t[-2::-1])

Tests are on ideone

Runs through the top to the middle rows, for r in range(n), constructing strings:
- making two corners or two edges, '34'[r%m>0]*2;
- filling by joining them with repeated '6' or '4', '46'[r>0]*(r+m-1);
- joining the corners and edges with ' ';
- prepending with spaces, ' '*(m-r);

Then prints this and it's reflection in the middle row joined by new lines, print'\n'.join(t+t[-2::-1])