Desenhar círculo compactando com Tikz?

Oct 08 2020

É possível desenhar a imagem anexada?

Minha tentativa:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{fit,backgrounds}
\begin{document}
\begin{tikzpicture}[cable/.style={circle, fill=cyan!70!black, minimum size=5mm, inner sep=0pt, outer sep=0pt}]
\node[cable] (center) at (0,0) {};
\foreach \i in {0,1,...,6}
    \node[cable] (1-\i) at (60*\i:5mm) {};
    \fill[red!20] circle (0.26);
\end{tikzpicture}
\end{document}

Respostas

7 SandyG Oct 30 2020 at 02:54

Aqui está uma solução automatizada. Definimos um comando \circlesqueezeque leva dois argumentos, um opcional. O argumento obrigatório é uma lista de círculos separados por vírgulas. O comando

\circlesqueeze{1/-1/.5,0/-1/.85,.3/.5/.7,1.3/1.2/.6,1/0/.5}

dentro de um tikzpictureespremerá 5 círculos. A sintaxe para cada círculo é x/y/ronde (x,y)está o centro e ré o raio. Portanto, no exemplo acima, o primeiro círculo é centralizado (1,-1)com o raio .5. O segundo é centrado em (0,-1)com raio .85e assim por diante. As unidades são centímetros.

O argumento opcional é o espaço mínimo entre os círculos. O padrão é .05cm. Aqui está um exemplo com o espaço reduzido a .03cm.

\circlesqueeze[.03]{0/0/1,1/1/.5,0/1.5/.7,1.6/1.4/.25,1.6/0/.8,2/1/.5,1/2/.5}

Se você deseja alterar o line width, deve ser feito globalmente usando o comando \lw, inicialmente definido como .4. Você pode mudar isso com \renewcommand. Você pode usar o \clipcomando para obter uma imagem semelhante à original. Aproximei os centros e raios:

\renewcommand{\lw}{1}
\begin{tikzpicture}[line width=\lw]
\clip (-4.5,-4.5) rectangle (6,5);
\circlesqueeze[.1]{.2/0/3,2/5/3,-2.6/3/2.1,-3.7/-2.3/3.1,1.5/-4/2.2,6.4/0/4}
\end{tikzpicture}

Aqui está o algoritmo básico:

  • Todos os círculos são desenhados. Queremos excluir os arcos circulares apropriados e substituí-los por segmentos de linha.
  • Para cada par de círculos, a distância entre seus centros é comparada com a soma de seus raios. Se a distância criar uma lacuna suficientemente grande entre os círculos, não faça nada.
  • Se os círculos estão próximos (ou sobrepostos), calcule um ponto (m)na linha que conecta os dois centros (o eixo central), entre os dois arcos circulares que precisarão ser modificados, mas mais perto do centro do círculo menor para que o dois segmentos achatados têm o mesmo comprimento.
  • Encontre os pontos de interseção de cada um dos dois círculos com linhas perpendiculares ao eixo central, igualmente espaçadas de (m).
  • Cubra o arco circular entre esses pontos de interseção com um arco branco espesso para "apagá-lo". Nota: se a cor de fundo não for branca, isso precisa ser alterado.
  • Para que o segmento plano se conecte perfeitamente ao círculo restante, um pequeno \epssegmento (distância = ) é desenhado a partir de uma segunda interseção um pouco mais distante (m), seguindo então o primeiro par de interseções e reconectando ao círculo. Esta é uma linha dupla (branca por fora, preta por dentro) para cobrir quaisquer vestígios indesejáveis ​​do círculo original.

Problemas conhecidos:

  • Um círculo não pode conter o centro de um círculo diferente.
  • Nenhum ponto pode estar no interior de mais de dois círculos.
  • Coisas ruins podem acontecer se não houver arco circular suficiente entre as partes planas. Isso geralmente pode ser corrigido ajustando a espessura e o espaçamento da linha.
  • Coisas ruins podem acontecer se o argumento opcional for muito pequeno ou muito grande. Depende do espaçamento dos círculos.
  • É lento.

Aqui está o código:

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{intersections,calc}
\usepackage{ifthen}

\newcommand{\eps}{.01} % arc-line connector length in cm
\newcommand{\lw}{.4} % line width. Be careful if defining locally.

\newcommand{\circlesqueeze}[2][.05]{
\foreach \xa/\ya/\ra in {#2}
    {
    \draw (\xa,\ya) circle[radius=\ra]; % draw all circles
    }
\foreach [var=\xa, var=\ya, var=\ra, count=\na] in {#2}
\foreach [var=\xb, var=\yb, var=\rb, count=\nb] in {#2}
    {
    \ifthenelse{\na<\nb} % compare each pair of circles once
        {
            \pgfmathparse{#1+\ra+\rb-veclen(\xb-\xa,\yb-\ya)}
            \ifthenelse{\lengthtest{\pgfmathresult pt > 0 pt}} % if circles are too close
            {
            \coordinate(c1) at (\xa,\ya); \coordinate(c2) at (\xb,\yb); % circle centers
            \path[name path=line0] (c1)--(c2); % center axis
            \path[name path=circ1] (c1) circle[radius=\ra];
            \path[name path=circ2] (c2) circle[radius=\rb];
            \path[name intersections={of=line0 and circ1, by={i1}},
                name intersections={of=line0 and circ2, by={i2}}];
            \coordinate (u12) at ($(c1)!1cm!(c2)-(c1)$); % unit vector from c1 to c2
            \path 
                let \n1={max(\ra,\rb)}, \n2={min(\ra,\rb)}, 
                    \n3={(.5*\n2/\n1)*(\ra>=\rb)+(1-.5*\n2/\n1)*(\ra<\rb)} in % n3=.5 would be equally spaced. Otherwise, closer to smaller circle
                coordinate (m) at ($\n3*(i2)+{(1-\n3)}*(i1)$);
            \path[name path=insideline1] % perpendicular to center axis
                let \p0=(u12),
                    \p1=(m)
                in (\x1-\ra*\y0-#1*.5*\x0,\y1+\ra*\x0-#1*.5*\y0)--(\x1+\ra*\y0-#1*.5*\x0,\y1-\ra*\x0-#1*.5*\y0);
            \path[name path=insideline2]
                let \p0=(u12),
                    \p1=(m)
                in (\x1-\ra*\y0+#1*.5*\x0,\y1+\ra*\x0+#1*.5*\y0)--(\x1+\ra*\y0+#1*.5*\x0,\y1-\ra*\x0+#1*.5*\y0);
            \path[name path=outsideline1] % a little farther apart
                let \p0=(u12),
                    \p1=(m)
                in (\x1-\ra*\y0-#1*.5*\x0-\eps*\x0,\y1+\ra*\x0-#1*.5*\y0-\eps*\y0)--(\x1+\ra*\y0-#1*.5*\x0-\eps*\x0,\y1-\ra*\x0-#1*.5*\y0-\eps*\y0);
            \path[name path=outsideline2]
                let \p0=(u12),
                    \p1=(m)
                in (\x1-\ra*\y0+#1*.5*\x0+\eps*\x0,\y1+\ra*\x0+#1*.5*\y0+\eps*\y0)--(\x1+\ra*\y0+#1*.5*\x0+\eps*\x0,\y1-\ra*\x0+#1*.5*\y0+\eps*\y0);
            \path[name intersections={of=circ1 and insideline1, by={a1,a2}}];
            \path[name intersections={of=circ1 and outsideline1, by={b1,b2}}];
            \path[name intersections={of=circ2 and insideline2, by={a3,a4}}];
            \path[name intersections={of=circ2 and outsideline2, by={b3,b4}}];
            \draw[white, line width=2*\lw] % first circle
                let \p1=($(a1)-(c1)$),
                    \p2=($(a2)-(c1)$),
                    \n0= {\ra}, % Radius
                    \n1 = {atan2(\y1,\x1)}, % angle 1
                    \n2 = {atan2(\y2,\x2)}, % angle 2
                    \n3 = {\n2+360*(\n1-\n2>180)-360*(\n1-\n2<-180)} % force shorter arc
                in (a1) arc(\n1:\n3:\n0);
            \draw[white, line width=2*\lw] % second circle
                let \p1=($(a3)-(c2)$),
                    \p2=($(a4)-(c2)$),
                    \n0= {\rb},
                    \n1 = {atan2(\y1,\x1)},
                    \n2 = {atan2(\y2,\x2)},
                    \n3 = {\n2+360*(\n1-\n2>180)-360*(\n1-\n2<-180)}
                in (a3) arc(\n1:\n3:\n0);
            \draw[line join=round, white, double=black, double distance=\lw] (b1)--(a1)--(a2)--(b2);
            \draw[line join=round, white, double=black, double distance=\lw] (b3)--(a3)--(a4)--(b4);
            }{} % if dist >= #1 do nothing
        }{}% if \na >= \nb do nothing (only do each pair of circles once)
    }
}

\begin{document}

\begin{tikzpicture}[line width=\lw]
\circlesqueeze{1/-1/.5,0/-1/.85,.3/.5/.7,1.3/1.2/.6,1/0/.5}
\end{tikzpicture}

\begin{tikzpicture}[line width=\lw]
\circlesqueeze[.03]{0/0/1,1/1/.5,0/1.5/.7,1.6/1.4/.25,1.6/0/.8,2/1/.5,1/2/.5}
\end{tikzpicture}

\renewcommand{\lw}{1}
\begin{tikzpicture}[line width=\lw]
\clip (-4.5,-4.5) rectangle (6,5);
\circlesqueeze[.1]{.2/0/3,2/5/3,-2.6/3/2.1,-3.7/-2.3/3.1,1.5/-4/2.2,6.4/0/4}
\end{tikzpicture}

\end{document}
3 g.kov Oct 09 2020 at 17:31

Usando Asymptote, você pode começar com algo assim:

// circpacking.asy
// 
// run 
//   asy circpacking.asy
//
// to get a standalone circpacking.pdf
//
settings.outformat="pdf";
size(6cm);
pen linePen=darkblue+0.7bp;

pair o=(0.47,0);
real r0=0.2;
pair[] Ok={( 0.53, 0.48),(-0.32, 0.20),( 0.31,-0.55),( 0.78,-0.24),( 0.82, 0.12),};
real[] rk={0.28,0.62,0.37,0.19,0.17,};

int n=Ok.length;
real sq=0.08;

guide g0=circle(o,r0);
guide[] gk; gk.cyclic=true;
transform tr;
for(int i=0;i<n;++i){
  tr=shift(sq*(o-Ok[i])); 
  Ok[i]=tr*Ok[i];
  gk.push(circle(Ok[i],rk[i]));
}

guide trunc(int k){
  pair[] xp;
  guide q;
  xp.append(intersectionpoints(gk[k],gk[k-1]));
  xp.append(intersectionpoints(gk[k],g0));
  xp.append(intersectionpoints(gk[k],gk[k+1]));
  q=xp[0]--xp[1]
    &arc(Ok[k],xp[1],xp[4])--xp[5]
    &arc(Ok[k],xp[5],xp[2])--xp[3]
    &arc(Ok[k],xp[3],xp[0])&cycle;
  tr=shift(sq*0.3*(Ok[k]-o)); 
  q=tr*q;
  return q;
}

guide trunc0(){
  pair[] xp; guide q;
  for(int i=0;i<n;++i){
    xp.append(intersectionpoints(g0,gk[i]));
  }
  q=xp[0]--xp[1]
    &arc(o,xp[1],xp[2])--xp[3]
    &arc(o,xp[3],xp[4])--xp[5]
    &arc(o,xp[5],xp[6])--xp[7]
    &arc(o,xp[7],xp[9])--xp[8]
    &arc(o,xp[8],xp[0])
    &cycle;
  return q;
}
for(int i=0;i<n;++i){
  draw(trunc(i),linePen);
}
draw(trunc0(),linePen);
clip(box(o-2*r0*(1,1),o+2*r0*(1,1)));

A ideia é, partindo de uma cadeia de círculos de beijo de Steiner, mover todos os círculos em uma cadeia em direção ao círculo circundado, encontrar todas as interseções, cortar em linha reta as sobreposições e mover levemente todos os círculos truncados para trás para fazer algumas lacunas.


Editar

E esta é uma tradução do asymptotecódigo acima via .svgformato para TikZpor meio de svg2tikz :

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\begin{document}
\definecolor{c00003f}{RGB}{0,0,63}
\def \globalscale {1.000000}
\begin{tikzpicture}[y=0.80pt, x=0.80pt, yscale=-\globalscale, xscale=\globalscale, inner sep=0pt, outer sep=0pt]
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (121.8530,43.4044) -- (145.4980,24.3523) .. controls (152.8360,14.4281) and (157.1750,2.1513) .. (157.1750,-11.1389) .. controls (157.1750,-44.1405) and (130.4220,-70.8936) .. (97.4204,-70.8936) .. controls (70.8777,-70.8936) and (48.3770,-53.5876) .. (40.5863,-29.6438) -- (62.6986,37.4991) .. controls (65.5025,39.5043) and (68.4870,41.2732) .. (71.6231,42.7764) -- (109.1410,47.4744) .. controls (113.5770,46.5924) and (117.8350,45.2196) .. (121.8530,43.4044) -- cycle;
    \end{scope}
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (58.6962,38.8146) -- (36.5965,-28.3310) .. controls (12.8622,-63.8082) and (-27.5709,-87.1681) .. (-73.4600,-87.1681) .. controls (-146.5380,-87.1681) and (-205.7790,-27.9268) .. (-205.7790,45.1511) .. controls (-205.7790,118.2290) and (-146.5380,177.4700) .. (-73.4600,177.4700) .. controls (-56.3476,177.4700) and (-39.9938,174.2220) .. (-24.9818,168.3080) -- (39.4559,114.1780) .. controls (41.2222,111.2950) and (42.8812,108.3390) .. (44.4273,105.3150) -- (58.8082,48.3489) .. controls (58.8334,47.2860) and (58.8461,46.2200) .. (58.8461,45.1511) .. controls (58.8461,43.0269) and (58.7960,40.9143) .. (58.6962,38.8146) -- cycle;
    \end{scope}
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (42.4082,117.7020) -- (-22.0387,171.8220) .. controls (-24.4716,179.4210) and (-25.7851,187.5210) .. (-25.7851,195.9280) .. controls (-25.7851,239.5460) and (9.5744,274.9060) .. (53.1925,274.9060) .. controls (96.8107,274.9060) and (132.1700,239.5460) .. (132.1700,195.9280) .. controls (132.1700,187.5340) and (130.8610,179.4470) .. (128.4350,171.8580) -- (104.9390,136.2890) .. controls (101.9690,133.7100) and (98.8053,131.3500) .. (95.4719,129.2320) -- (53.3114,116.9720) .. controls (53.2718,116.9720) and (53.2322,116.9720) .. (53.1925,116.9720) .. controls (49.5343,116.9720) and (45.9342,117.2200) .. (42.4082,117.7020) -- cycle;
    \end{scope}
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (107.1520,134.8290) -- (130.6250,170.4120) .. controls (135.8100,172.8190) and (141.5880,174.1620) .. (147.6790,174.1620) .. controls (170.0770,174.1620) and (188.2340,156.0050) .. (188.2340,133.6070) .. controls (188.2340,117.9240) and (179.3330,104.3210) .. (166.3070,97.5734) -- (137.4190,94.3609) .. controls (134.0410,95.2416) and (130.8340,96.5470) .. (127.8590,98.2167) -- (108.4510,123.2800) .. controls (107.5850,126.5770) and (107.1240,130.0380) .. (107.1240,133.6070) .. controls (107.1240,134.0160) and (107.1300,134.4230) .. (107.1520,134.8290) -- cycle;
    \end{scope}
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (137.6100,92.6742) -- (166.4940,95.8951) .. controls (181.2740,91.3063) and (192.0080,77.5236) .. (192.0080,61.2340) .. controls (192.0080,41.1933) and (175.7620,24.9470) .. (155.7210,24.9470) .. controls (152.6640,24.9470) and (149.6950,25.3251) .. (146.8590,26.0369) -- (123.2120,45.0971) .. controls (121.3490,48.8433) and (120.1140,52.9571) .. (119.6460,57.3006) -- (129.6540,86.4646) .. controls (131.9970,88.8845) and (134.6740,90.9791) .. (137.6100,92.6742) -- cycle;
    \end{scope}
    \begin{scope}[cm={{0.99626,0.0,0.0,0.99626,(41.5276,138.898)}}]
      \path[draw=c00003f,line cap=round,line join=round,line width=0.562pt,miter limit=10.04] (108.8560,49.7248) -- (71.3386,45.0229) .. controls (68.2199,46.1067) and (65.2658,47.5420) .. (62.5235,49.2814) -- (48.1261,106.2530) .. controls (49.7824,109.1980) and (51.7779,111.9260) .. (54.0597,114.3850) -- (96.2278,126.6520) .. controls (100.0570,125.6470) and (103.6760,124.1230) .. (107.0050,122.1600) -- (126.4140,97.0975) .. controls (127.3320,93.8808) and (127.8820,90.5090) .. (128.0160,87.0304) -- (118.0060,57.8663) .. controls (115.3660,54.7351) and (112.2850,51.9889) .. (108.8560,49.7248) -- cycle;
    \end{scope}
\end{tikzpicture}
\end{document}