Draw Polygonal Number Using PSTricks (or TikZ)

Here is a solution using TikZ.

There exist a few keys that change the graphics parameter:

  • sides: the number of sides/corners in the polygon;
  • level: the number of repetitions of the polygon;
  • side length: the length of one side of the polygon;
  • for the “bars”:
    • x padding and
    • y padding as well as
    • the switch bars that can be used to turn off/on the drawing of the bars.

The polygon is drawn with the center node at the point where the path currently is where you use the polynum key. The “polynum” can be rotated by rotating the path. The polygons stand on their center corner if you rotate the path about 360/<corners>/2.

The bar is drawn with an active rotation in mind (that is the reason for the \pgfpointshapeborder), this makes it possible to include the nodes as they are without the need to rotate them or to use transform shape. The bar-drawing algorithm though will create a bad result if the nodes are not of the same size.

The macros

  • \polynumcorner,
  • \polynumlevel,
  • \polynumsublevel and
  • \polynumcorners (i.e. the total number of corners)

can be used to affect the styles, this is used inside the every corner edge style (actually a .code key) that turns the drawing of the edge between certain corner nodes explicitly off.

The nodes are named: pn@<corner>@<level>@<sublevel>. The red center node is named pn@0@1@0, it is the only 0th corner node.

Code

\documentclass[tikz,png={size=315,density=600},convert=false]{standalone}
\pgfkeys{% http://tex.stackexchange.com/a/125700/16595
  /handlers/.tikz/.code=\pgfkeys{\pgfkeyscurrentpath/.code=\tikzset{#1}},%
  /handlers/.append tikz/.code=\pgfkeys{\pgfkeyscurrentpath/.append code=\tikzset{#1}},%
  /handlers/.prefix tikz/.code=\pgfkeys{\pgfkeyscurrentpath/.prefix code=\tikzset{#1}}}
\def\polynumset{\pgfqkeys{/polynum}}
\newif\ifpolynumbars
\polynumset{
  sides/.initial=4,
  levels/.initial=5,
  side length/.initial=+.5cm,
  x padding/.initial=+2pt,
  y padding/.initial=+2pt,
  bars/.is if=polynumbars,
  every bar/.tikz={draw=blue, thick, rounded corners=+1pt},
  %
  every node/.tikz={shape=circle,draw,inner sep=+0pt,minimum size=+4pt},
  center node/.tikz={fill=red},
  level nodes/.tikz={fill=blue},
  sublevel nodes/.tikz={fill=yellow},
  %
  every edge/.tikz={},
  %
  every corner edge/.code={
    \ifnum\polynumcorner>1\relax
      \ifnum\polynumcorner<\polynumcorners\relax
        \tikzset{draw=none}
      \fi
    \fi
  },
}
\makeatletter
\def\polynumutil@firstofone#1{\tikz@scan@next@command#1\pgf@stop}
\def\polynumutil@firstoftwo#1#2{\tikz@scan@next@command#1\pgf@stop}
\def\polynumutil@secondoftwo#1#2{\tikz@scan@next@command#2\pgf@stop}
\polynumset{
  bar path/.style={
    to path={
      [/utils/exec=%
         \pgfmathanglebetweenpoints{\pgfpointanchor{\tikztostart} {center}}
                                   {\pgfpointanchor{\tikztotarget}{center}}%
         \let\polynum@a\pgfmathresult
         \pgftransformrotate{\polynum@a}%
         \pgfpointdiff{\pgfpointanchor{\tikztostart}{center}}
                      {\pgfpointshapeborder{\tikztostart}{\pgfpointadd{\pgfpointanchor{\tikztostart}{center}}{\pgfpointpolar{90}{1pt}}}}%
         \pgfmathveclen{\pgf@x}{\pgf@y}%
         \edef\polynum@ydistance{\pgfmathresult pt}%
         \pgfcoordinate{pn@aux1}
                       {\pgfpointshapeborder
                         {\tikztostart}
                         {\pgfpointadd{\pgfpointanchor{\tikztostart}{center}}
                                      {\pgfpointpolar{180}{1pt}}}}%
         \pgfcoordinate{pn@aux2}
                       {\pgfpointshapeborder
                         {\tikztotarget}
                         {\pgfpointadd{\pgfpointanchor{\tikztotarget}{center}}
                                      {\pgfpointpolar{0}{1pt}}}}%
       ]
             ([shift=(left:\pgfkeysvalueof{/polynum/x padding})]pn@aux1) coordinate (pn@aux)
       -- ++ (up:\polynum@ydistance+\pgfkeysvalueof{/polynum/y padding})
       -|    ([shift=(right:\pgfkeysvalueof{/polynum/x padding})] pn@aux2)
       |-    ([shift=(down:\polynum@ydistance+\pgfkeysvalueof{/polynum/y padding})] pn@aux)
       -- cycle
    }
  }}
\tikzset{
  polynum/.default=,
  polynum/.style={
    insert path={
      node[/polynum/every node/.try, /polynum/inner nodes/.try, /polynum/center node/.try] (pn@0@1@0) {}
      { [/polynum/.cd,#1]
        [/utils/exec=%
          \pgfmathtruncatemacro\polynumcorners{\pgfkeysvalueof{/polynum/sides}-1}%
          \pgfmathtruncatemacro\polynum@levels{\pgfkeysvalueof{/polynum/levels}}%
          \pgfmathsetlengthmacro\polynum@sidelength{\pgfkeysvalueof{/polynum/side length}}%
          \pgfmathsetmacro\polynum@angle{360/(\the\numexpr\polynumcorners+1\relax)}%
        ]
        \foreach \polynumcorner[evaluate={\polynum@@angle={(\polynumcorner-1)*\polynum@angle}}] in {1,...,\polynumcorners} {
          % get to the next corner,
          % level 1
          ++ (\polynum@@angle:\polynum@sidelength)
            node[/polynum/every node/.try, /polynum/level nodes/.try, /polynum/level 1 nodes/.try] (pn@\polynumcorner @1@0) {}
          % last level
          [/utils/exec={%
            \ifnum\polynumcorner=1\relax
              \expandafter\polynumutil@firstoftwo
            \else
              \expandafter\polynumutil@secondoftwo
            \fi
              {+(\polynum@@angle:\the\numexpr\polynum@levels-1\relax*\polynum@sidelength)}
              {   (pn@\the\numexpr\polynumcorner-1\relax @\polynum@levels @0.center)
               ++ (\polynum@@angle:\polynum@levels*\polynum@sidelength)}}]
          % it's only a coordinate because it is used to place everything else
          % including the node at the exact same position
          node[shape=coordinate, alias=pn@\polynumcorner @last] (pn@\polynumcorner @\polynum@levels @0) {}
          % all other levels
          \foreach \polynumlevel[count=\polynum@level from 1, evaluate={\polynum@pos=\polynum@level/\the\numexpr\polynum@levels-1\relax}] in {2,...,\polynum@levels} {
            (pn@\polynumcorner @[email protected])
              edge[draw=none] node[pos=\polynum@pos, /polynum/every node/.try, /polynum/level nodes/.try, style/.expanded={/polynum/level \polynumlevel\space nodes/.try}] (pn@\polynumcorner @\polynumlevel @0) {}
            (pn@\polynumcorner @last.center)
            % the edges between the corner nodes (blue -- blue)
            (pn@\polynumcorner @\the\numexpr\polynumlevel-1\relax @0) edge[/polynum/every edge/.try, /polynum/every corner edge/.try] (pn@\polynumcorner @\polynumlevel @0)
            % now the sublevels (except for the first corner because it has no previous corner)
            [/utils/exec={%
              \ifnum\polynumcorner>1\relax
                \expandafter\polynumutil@firstofone
              \else
                \expandafter\pgfutil@gobble
              \fi
              {\foreach \polynumsublevel[evaluate={\polynum@pos=\polynumsublevel/\polynumlevel}] in {1,...,\the\numexpr\polynumlevel-1\relax}{
                 (pn@\the\numexpr\polynumcorner-1\relax @\polynumlevel @0.center)
                   edge[draw=none] node[pos=\polynum@pos, /polynum/every node/.try, /polynum/sublevel nodes/.try, style/.expanded={/polynum/sublevel \polynumsublevel\space nodes/.try}] (pn@\the\numexpr\polynumcorner-1\relax @\polynumlevel @\polynumsublevel) {}
                 (pn@\polynumcorner @\polynumlevel @0.center)
                 % the edges between sublevel nodes (blue -- yellow and yellow -- yellow)
                 (pn@\the\numexpr\polynumcorner-1\relax @\polynumlevel @\the\numexpr\polynumsublevel-1\relax)
                   edge[/polynum/every edge/.try]
                 (pn@\the\numexpr\polynumcorner-1\relax @\polynumlevel @\polynumsublevel)
               }
               % the edge between the last sublevel node and the next corner node (yellow -- blue)
               (pn@\the\numexpr\polynumcorner-1\relax @\polynumlevel @\the\numexpr\polynumlevel-1\relax)
                 edge[/polynum/every edge/.try]
               (pn@\polynumcorner @\polynumlevel @0)
              }}]
          }
          % the edges between corner nodes (level 1, the smallest polygon: red -- blue and blue -- blue)
           (pn@\the\numexpr\polynumcorner-1\relax @1@0) edge[/polynum/every edge/.try] (pn@\polynumcorner @1@0)
           (pn@\polynumcorner @[email protected])
        }
        % the last edge (blue -- red)
        (pn@\polynumcorners @1@0) edge[/polynum/every edge/.try] (pn@0@1@0)
        % the bars are drawn in an extra loop to make sure they are on top
        [/utils/exec=%
          \ifpolynumbars\expandafter\polynumutil@firstofone\else\expandafter\pgfutil@gobble\fi
            {\foreach \polynumcorner in {1,...,\polynumcorners} {
            (pn@\polynumcorner @1@0) edge[/polynum/bar path/.try, /polynum/every bar/.try] (pn@\polynumcorner @\polynum@levels @0)}}
        ]
      }
    }
  }
}
\makeatother
\polynumset{bars}
\begin{document}
\foreach \sides in {3,...,6}{
\begin{tikzpicture}[/polynum/levels=3]
\path [polynum={/tikz/rotate=360/\sides/2,sides=\sides}];
\end{tikzpicture}}
\foreach \level in {2,...,5,5,4,3,2}{
\begin{tikzpicture}
\path [polynum={levels=\level}];
\useasboundingbox (-8pt,-8pt) rectangle (2.5cm+8pt,2.5cm+8pt);
\end{tikzpicture}}
\end{document}

Output

enter image description hereenter image description hereenter image description hereenter image description herePicture for <code>sides=7</code>enter image description here


enter image description here

This solution is based on the object-oriented feature of the Asymptote. The basic structure struct PolygonalNumber is defined in asydef environment, and can be used later in asy pictures to create and draw objects of this kind. For example, a triangular object named P3 with 5 layers is defined as PolygonalNumber P3=PolygonalNumber(3,5); and command draw(P3); will draw it. PolygonalNumber objects can be transformed as usual, for example draw(shift(6,0)*P4); draws a P4 object shifted to the right. Or they can be used directly in the draw commands: the code to produce the image above was just

size(400);
for(int i=3;i<=7;++i){
  draw(shift((i-3)*(5+i-3),0)*PolygonalNumber(i,5));
};

A complete MWE polynum.tex:

\documentclass{article}
\usepackage[inline]{asymptote}
\begin{asydef}
struct PolygonalNumber{
  int n,m;
  real unitStep;
  real boxW;
  transform t;
  pair O=(0,0);
  pair[][] dots;

  pen originPen=red;
  pen sideLinePen=darkblue;
  pen cornerDotPen=blue;
  pen sideDotPen=orange;
  pen boxPen=blue;

  void drawSides(){
    guide g; 
    pair center,p;
    pair[] skin;    
    for(int i=1;i<=m;++i){
      p=(i*unitStep,0);
      g=O;
      center=i*unitStep/2*(1,1/Tan(180/n));
      for(int j=0;j<n-1;++j){
        g=g--(rotate(j*360.0/n,center)*p);
      }
      g=g--cycle;
      draw(t*g,sideLinePen);
      skin=new pair[];      
      for(int j=0;j<size(g);++j){
        skin.push(point(g,j));  
      }
      dots.push(skin);
    }
  }

  void drawCornerDots(){
    for(int i=0;i<dots.length;++i){
      for(int j=1;j<dots[i].length;++j){
        dot(t*dots[i][j],cornerDotPen);
      }
    }
  }

  void drawSideDots(){
    pair p,q;
    real s;
    for(int i=1;i<dots.length;++i){
      for(int j=1;j<dots[i].length-1;++j){
        p=dots[i][j];
        q=dots[i][j+1];
        for(int k=1;k<i+1;++k){
          s=k/(i+1);
          dot(t*(p*(1-s)+q*s),sideDotPen);
        }  
      }
    }
  }

  void drawOriginDots(){
    for(int i=0;i<dots.length;++i){
      dot(t*O,originPen);      
    }    
  }

  guide buildBox(pair a, pair b){
    pair d,p,q,ds;
    d=dir(b-a);
    ds=boxW*d;
    p=a-ds; q=b+ds; 
    guide[] g=strokepath(p--q,red+boxW);
    return g[0];
  }

  void drawBoxes(){
    pair a,b;
    for(int j=1;j<n;++j){
      a=dots[0][j];
      b=dots[m-1][j];
      draw(t*buildBox(a,b),boxPen);      
    }
  }

  void draw(){
    drawSides();
    drawCornerDots();
    drawSideDots();
    drawOriginDots();
    drawBoxes();
  }

  void operator init(int n=3,int m=6,real unitStep=1,transform t=identity(),real boxW=0.4){
    assert(n>2 && m>0);
    this.n=n; this.m=m; 
    this.unitStep=unitStep;
    this.boxW=boxW;
    this.t=t;
  }
}
PolygonalNumber operator*(transform t=identity(),PolygonalNumber p){p.t=t; return p;}; 
void draw(PolygonalNumber p){p.draw();};
\end{asydef}

\begin{document}
\begin{figure}
\begin{asy}
size(400);
for(int i=3;i<=7;++i){
  draw(shift((i-3)*(5+i-3),0)*PolygonalNumber(i,5));
};
\end{asy}
\end{figure}
\end{document}

To process it with latexmk, create file latexmkrc:

sub asy {return system("asy '$_[0]'");}
add_cus_dep("asy","eps",0,"asy");
add_cus_dep("asy","pdf",0,"asy");
add_cus_dep("asy","tex",0,"asy");

and run latexmk -pdf polynum.tex.


I started with this but then I got bored :) Always nice to steal Jake's ncbarfrom Is there a TikZ equivalent to the PSTricks \ncbar command?

\numofsides and \numofiters define the polygon shape and how many times it is repeated. I don't have Qrrbrbirlbel's patience. So it's just for fun.

\documentclass[tikz,border=4mm]{standalone}
\usetikzlibrary{calc,shapes.geometric,backgrounds}
\begin{document}
\tikzset{
    ncbar angle/.initial=90,
    ncbar/.style={to path=(\tikztostart)--($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)
        -- ($(\tikztotarget)!($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztostart)$)
        -- (\tikztotarget)},
    ncbar/.default=0.5cm,
}
\begin{tikzpicture}[
bluedot/.style={circle,fill=blue,draw,inner sep=0,minimum size=3pt,anchor=center},
yellowdot/.style={bluedot,fill=yellow},
]
\def\numofsides{10} %Change these
\def\numofiters{10} %Change these
\foreach \x in {1,...,\numofiters}{
\begin{scope}[on background layer]\node[rotate={floor(\numofsides/2)*360/\numofsides}, 
      inner sep=0,anchor=corner 1,
      draw,minimum size=\x*8mm,
      blue,regular polygon,regular polygon sides=\numofsides] (n\x) {};\end{scope}
    \foreach \y[count=\yi,count=\yj from 0,remember=\y as \lasty] in {corner 2,corner 3,corner ...,corner \numofsides} {
    \node[bluedot,rotate={((1-(1-(2/\numofsides)))*90)*\yj-90}] (n\x c\yi) at (n\x.\y) {};
    \ifnum\x>1\ifnum\yi>1\foreach\z in{1,...,\numexpr\x-1\relax}{
        \node[yellowdot] at ($(n\x.\lasty)!\z/\x!(n\x.\y)$){};}\fi\fi
    }
}
\foreach \x[count=\xi from 0] in {1,...,\numexpr\numofsides-1}{
\pgfmathsetmacro\myangle{((1-(1-(2/\numofsides)))*90)*\xi+180}
\draw[rounded corners=1pt,blue] ([shift={(\myangle:1pt)}]n1c\x.south) to[ncbar=2pt] 
                           ([shift={(\myangle:-1pt)}]n\numofiters c\x.north) to[ncbar=2pt]
                           ([shift={(\myangle:1pt)}]n1c\x.south);
}

\end{tikzpicture}

\end{document}

A truly ridiculous image

enter image description here

With \def\numofsides{24} and \def\numofiters{24}:

output

Awesome! :)