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
andy 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
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 ncbar
from 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
With \def\numofsides{24}
and \def\numofiters{24}
:
Awesome! :)