CUT OUT HEXAGONS
Canvas, 113 112 bytes
«/*α_×-/═↔⁸3+ *-╶R⇵{;X:«w╷/###/*Kky┤_×+-y#×/×-y_×-/═ŗ∙ #⟳#¶#╋# ⟳_╋↔x“kyv╴e┌/i┴⁰U<alissfr↖;,imFE!↷qB╪²-‟#ŗ}#∙ ╋↔║
Try it here!
A part of the answer is literally evaluating the JavaScript p.p.overlap(p.p,0,p.p,(a,b)=>b==0?a:b)
..
Charcoal, 80 bytes
F⮌…·¹N«→↘F²«M⁺³×⁴ι↑P×_⊗ι↙F⊗ι«FκP× ⁴↙¹»→Fι«FκP× ⁴↘¹»F⊖ι«FκP× ⁻׳ιλ↘¹»\P×_⊗ι»»‖BO²
Try it online! Link is to verbose version of code. Explanation:
F⮌…·¹N«
Loop from the largest to the smallest hexagon.
→↘F²«M⁺³×⁴ι↑
Draw each hexagon twice in the desired location.
P×_⊗ι
Draw half of the top line of the hexagon.
↙F⊗ι«FκP× ⁴↙¹»
Draw the top left diagonal, but for each row erase 4 spaces first if this is the second hexagon.
→Fι«FκP× ⁴↘¹»
Draw half of the bottom left diagonal, still erasing 4 spaces first if this is the second hexagon.
F⊖ι«FκP× ⁻׳ιλ↘¹»
Draw almost all of the rest of the bottom left diagonal, erasing until the middle of the hexagon if this is the second hexagon.
\P×_⊗ι
Finish the bottom left diagonal and draw half of the bottom line of the hexagon.
»»‖BO²
Mirror everything at the end.
Alternative approach, also 80 bytes:
F⮌…·¹N«→↘F²«M⁺³×⁴ι↑FκG→⊗ι↓³←⊖⊗ι↙⊖⊗ι↘⊖⊗ι→⊖⊗ι↓³←⊗ι↖⊕⊗ι↗⊕⊗ι P×_⊗ι↙↙⊗ι→↘⊗ι↑P×_⊗ι»»‖M
Try it online! Link is to verbose version of code. Explanation:
F⮌…·¹N«
Loop from the largest to the smallest hexagon.
→↘F²«M⁺³×⁴ι↑
Draw each hexagon twice in the desired location.
FκG→⊗ι↓³←⊖⊗ι↙⊖⊗ι↘⊖⊗ι→⊖⊗ι↓³←⊗ι↖⊕⊗ι↗⊕⊗ι
Before drawing the second hexagon, erase the area between it and the next smaller hexagon.
P×_⊗ι↙↙⊗ι→↘⊗ι↑P×_⊗ι»»‖M
Draw the left half of each hexagon, and then mirror everything at the end.
C (clang), 374 bytes
#define F(X,R)*d=*(d=o+x*(l*3+35-g)+x/2+X+R)-32?*d:R?
i,z,x,l,w,r,g,m;f(n){int*d,o[i=z=(x=n*8+1)*(n*5+4)],*c=o;for(;l=i--;)*c++=i%x?32:10;for(;l++<n;)for(g=35;i=g-29;g-=3){for(r=m+=m=w=l*2;r--;F(~-w*x-w,r)g:g)F(w*~x,r)95:95,F(w*x-w,r)95:95;for(;w--;)for(r=4;r--;F(m-~w*~-x,-r)g:47)F(w-m-w*x,r)g:47,F(w-m-~w*x,r)g:92,F(m+~w-w*x,-r)g:92;}for(;i<z;++i)printf(o[i]-35?o+i:" ");}
Try it online!
-33 more saved by @ceilingcat improvement
C (clang), 433 412 407 bytes
#define F(Y,X,W,R)*d=*(d=c-Y+X+W+R)-32?*d:R?
i,z,x,l,w,r,d,G,g,m;f(n){int*c,*d,o[i=z=(x=n*8+1)*(n*5+4)];for(c=o;l=i--;)*c++=i%x?32:10;for(;l++<n;)for(G=0;i=G<4;G+=3){g=35-G;c=o+x*l*3+G*x+x/2;for(r=m+=m=w=l*2;r--;F(x-w*x,-w,0,r)g:g)F(w*x,-w,0,r)95:95,F(-w*x,-w,0,r)95:95;for(;w--;)for(r=4;r--;F(~w*x,m,~w,-r)g:47)F(w*x,-m,w,r)g:47,F(~w*x,-m,w,r)g:92,F(w*x,m,~w,-r)g:92;}for(;i<z;)i+=printf(o[i]-35?o+i:" ");}
Try it online!
-21 thanks to @ceilingcat -5 @ceilingcat + y removed
###\
###\ <= we draw only on spaces ' '
\###
\### first time we draw '#' for collisions
/####\
/######\
\######/
\####/
____
/####\ then we move our drawing pointer
/######\ 3 lines below
\######/
\____/ and we draw spaces
/ \ instead of #
\ /
\____/
____
_/####\
//######\ and we repeat for bigger donuts
/#\######/ moving our drawing pointer
/###\____/
/###/ \
\###\ /
\###\____/
\####??####/
\________/