44

If we look down upon the Solar System in a geocentric model, the Sun and all the planets will be orbiting the Earth. In this model, the Earth is at the center of the universe. Let us focus on two celestial bodies other than Earth: Sun and Venus. If we assume the Earth is fixed, as the Sun orbits the Earth 8 times, Venus will do so 13 times. This will create a beautiful diagram known as the Pentagram of Venus (in reality, the ratio is 8:13.004 and the non-integer coefficient will create a wobble). Matthew Henderson posted this simplified image of the cosmic dance of Venus and Earth on his Tumblr webpage:

enter image description here

EDIT. The image above shows the dance of Venus from a heliocentric perspective, not a geocentric one as I explained. I missed it because I had several images of both heliocentric and geocentric Venus Pentagram open in my browser and I apparently copied the wrong one. Thanks to Keks Dose for mentioning that. Here is the link to an awesome video that shows the dance of Venus with Earth fixed at the center.

I was wondering if I can draw this diagram using TikZ/PGF. I have seen a similar topic on Mathematica Stack Exchange but I want to use LaTeX. I recently found the packages pst-solarsystem and tikz-planets and it would be cool to use their features if possible. I do not need the animation, any help to create the final static image would be appreciated.

7
  • Nice. It's such a shame you don't ask for a non-TikZ solution :(
    – user226564
    Dec 22, 2020 at 13:58
  • 5
    Isn't this picuture highly misleading, because the earth in the center is painted yellow? The rose of vensys is a picture from a geocentric model, not a heliocentric.
    – Keks Dose
    Dec 22, 2020 at 15:43
  • @KeksDose If you look at the original Tumblr post, the image indeed depicts the sun as the centre, despite what the OP describes here Dec 22, 2020 at 19:01
  • I put both views in my MP version below...
    – Thruston
    Dec 22, 2020 at 19:35
  • 1
    Keep your eyes on the central yellow circle, and then enjoy the optical illusion when it disappears!
    – Gimelist
    Dec 23, 2020 at 0:36

2 Answers 2

81

Fascinating and lots of fun. This essentially one-liner:

\documentclass{article}

\usepackage[halfletter,margin=0.6in]{geometry}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
    \foreach \x in {0,5,...,2880}{
        \draw[help lines] (\x:5) -- ({(\x/8)*13}:4);%% <<<--- avoids arithmetic overflow
    }
\end{tikzpicture}

\end{document}

yields this beautiful graphic:

enter image description here

Update

Well, it had to happen. Here is a macro that will do the drawing, along with the ability to play with the parameters:

\documentclass[tikz,border=20pt]{standalone}

\usepackage{keyval}

\newlength{\outerlength}
\setlength{\outerlength}{4in}
\newlength{\innerlength}
\setlength{\innerlength}{3in}
\def\outerrate{8}
\def\innerrate{13}
\def\maxtimes{2880}
\def\increment{2}

\makeatletter
\define@key{venus}{outerlength}{\setlength{\outerlength}{#1}}
\define@key{venus}{innerlength}{\setlength{\innerlength}{#1}}
\define@key{venus}{outerrate}{\def\outerrate{#1}}
\define@key{venus}{innerrate}{\def\innerrate{#1}}
\define@key{venus}{maxtimes}{\def\maxtimes{#1}}
\define@key{venus}{increment}{\def\increment{#1}}

\newcommand{\makevenus}[1][]{%
    \setkeys{venus}{#1}
    \begin{tikzpicture}
        \foreach \x in {0,\increment,...,\maxtimes}{%
            \draw[help lines] (\x:\outerlength) --
                ({(\x/\outerrate)*\innerrate}:\innerlength);%% <<<--- avoids arithmetic overflow
        }%
    \end{tikzpicture}%
}

\begin{document}

\makevenus[innerrate=7,innerlength=2.25in,increment=3]

\end{document}

which produces:

enter image description here

while \makevenus[outerrate=5,innerrate=13,innerlength=3.75in,increment=1] produces (I love my new toy!):

enter image description here

Update 2

I was curious about what would happen if the centerpoint of each line drawn was shown. Interestingly, as the macro \newmakevenus shows, it traces the shapes formed by the intersections of the lines.

\RequirePackage[rgb]{xcolor}
\documentclass[tikz,border=20pt]{standalone}

\usepackage{xparse}
\usepackage{keyval}
\usepackage{fp}

\newlength{\outerlength}
\setlength{\outerlength}{4in}
\newlength{\innerlength}
\setlength{\innerlength}{2.8933in}
\def\outerrate{8}
\def\innerrate{13}
\def\maxtimes{2880}
\def\increment{2}
\colorlet{mycolor}{black}

\makeatletter
\define@key{venus}{outerlength}{\setlength{\outerlength}{#1}}
\define@key{venus}{innerlength}{\setlength{\innerlength}{#1}}
\define@key{venus}{outerrate}{\def\outerrate{#1}}
\define@key{venus}{innerrate}{\def\innerrate{#1}}
\define@key{venus}{maxtimes}{\def\maxtimes{#1}}
\define@key{venus}{increment}{\def\increment{#1}}
\define@key{venus}{venuscolor}{\colorlet{mycolor}{#1}}
\makeatother

\NewDocumentCommand{\makevenus}{sO{}}{%
    \setkeys{venus}{#2}
    \begin{tikzpicture}
        \foreach \x in {0,\increment,...,\maxtimes}{%
            \IfBooleanT{#1}{%
                \pgfmathsetmacro{\huenum}{\x/\maxtimes}
                \definecolor{mycolor}{hsb}{\huenum,0.5,1}
            }%
            \draw[help lines,mycolor] (\x:\outerlength) --
                ({(\x/\outerrate)*\innerrate}:\innerlength);%% <<<--- avoids arithmetic overflow
        }%
    \end{tikzpicture}%
}

\NewDocumentCommand{\newmakevenus}{sO{}}{%
    \setkeys{venus}{#2}
    \begin{tikzpicture}
        \foreach \x in {0,\increment,...,\maxtimes}{%
            \IfBooleanT{#1}{%
                \pgfmathsetmacro{\huenum}{\x/\maxtimes}
                \definecolor{mycolor}{hsb}{\huenum,0.5,1}
            }%
            \draw(\x:\outerlength) --node[circle,fill=orange,pos=.5]{}
                ({(\x/\outerrate)*\innerrate}:\innerlength);%% <<<--- avoids arithmetic overflow
        }%
    \end{tikzpicture}%
}

\begin{document}

%%\makevenus*[outerrate=8,innerrate=13,increment=2,maxtimes=2880,innerlength=2.9in]

\newmakevenus[outerrate=8,innerrate=13,increment=3,innerlength=2.9in,maxtimes=2880]

\end{document}

enter image description here

An optional * will colorize the graphic so that \makevenus*[outerrate=8,innerrate=13,increment=2,maxtimes=2880,innerlength=2.9in] will produce:

enter image description here

2880 is simply 8*360.

A festive holiday update

Colorizing the graphic is simple: using the hsb colorspace and changing the hue of each line as it is drawn (requires \RequirePackage[rgb]{xcolor} before \documentclass command). While interesting, I wondered what would happen if the number of color cycles (red-yellow->green->blue->purple->red) were to cycle as many times as the lobes of the pattern generated -- turns out this is simply abs(\innerrate-\outerrate) -- this works best if either/both \innerrate or \outerrate are prime numbers. I was stunned by just how effective this is:

\RequirePackage[rgb]{xcolor}
\documentclass[tikz,border=20pt]{standalone}

\usepackage{xparse}
\usepackage{keyval}

\newlength{\outerlength}
\setlength{\outerlength}{4in}
\newlength{\innerlength}
\setlength{\innerlength}{2.8933in}
\def\outerrate{8}
\def\innerrate{13}
\def\maxtimes{2880}
\def\increment{2}
\def\changephase{0}
\colorlet{mycolor}{black}

\makeatletter
\define@key{venus}{outerlength}{\setlength{\outerlength}{#1}}
\define@key{venus}{innerlength}{\setlength{\innerlength}{#1}}
\define@key{venus}{outerrate}{\def\outerrate{#1}}
\define@key{venus}{innerrate}{\def\innerrate{#1}}
\define@key{venus}{increment}{\def\increment{#1}}
\define@key{venus}{changephase}{\def\changephase{#1}}
\define@key{venus}{venuscolor}{\colorlet{mycolor}{#1}}
\makeatother

\NewDocumentCommand{\makevenus}{sO{}}{%
    \setkeys{venus}{#2}
    \begin{tikzpicture}
        \pgfmathsetmacro{\nmaxtimes}{360*\outerrate}
        \foreach \x in {0,\increment,...,\nmaxtimes}{%
            \IfBooleanT{#1}{%
                \pgfmathsetmacro{\huenum}{%
                    abs(sin((360/\nmaxtimes)*\x*abs(\innerrate-\outerrate)+\changephase))
                }%
                \definecolor{mycolor}{hsb}{\huenum,1,1}
            }%
            \draw[help lines,mycolor] (\x:\outerlength) --
                ({(\x/\outerrate)*\innerrate}:\innerlength);%% <<<--- avoids arithmetic overflow
        }%
    \end{tikzpicture}%
}


\begin{document}

\makevenus*[outerrate=8,innerrate=17,increment=2,innerlength=3.5in,changephase=60]

\end{document}    

enter image description here

It is also possible to change the phase of the color cycling using changephase so that \makevenus*[outerrate=8,innerrate=13,increment=2,innerlength=3.5in,changephase=60] produces:

enter image description here

Hope you have fun with this -- I know I did.

A Festive New Year's Fireworks Update

Most importantly, a terrific vote of thanks to @Thruston on two counts: first for the elegant one-liner that does the drawing and, second, for the wonderful MP code that translates HSL to RGB.

The following code outputs three styles of graphics: 1: a monochrome version; 2: Thruston's original version of coloring; 3: phased coloring that repeats the spectrum (inner rate - outer rate) times. Note that I am not a MP expert by any manner of means, so that, no doubt, all sorts of improvements could be made to this code.

\documentclass[border=10pt]{standalone}
\usepackage{luamplib}
\usepackage{xparse}
\usepackage{graphicx}
\mplibnumbersystem{double}

%% TO DO: use keyval.sty
%% [#1 step delta]; #2 inner r; #3 outer r;
%% #4 [cycle offset]
%% #5 inner rate; #6 outer rate; 
%% #7[pen scale]; #8*=color (& #9* phased color)
%% #3 (outer r) is the radius of the finished graphic, assuming that it is
%% greater than #2 (inner r).
\NewDocumentCommand{\makevenusL}{O{1} m m O{0} m m O{.125} s s}{%
    \begin{mplibcode}
    vardef hsv_color_cyc(expr h,s,v) = % HSB->rgb following wikipedia article on "HSL and HSV"
          save chroma, hh, x, m;
          chroma = v*s;
          %% The following cycles through color spectrum (\innerrate-\outerrate) times
          %% a constant can be added to shift the cycle.
          hh = (((#6-#5)*h + #4) mod 360)/60;
          x  = chroma * (1-abs(hh mod 2 - 1));
          m  = v - chroma;
          if      hh < 1: (chroma,x,0)+(m,m,m)
          elseif hh < 2: (x,chroma,0)+(m,m,m)
          elseif hh < 3: (0,chroma,x)+(m,m,m)
          elseif hh < 4: (0,x,chroma)+(m,m,m)
          elseif hh < 5: (x,0,chroma)+(m,m,m)
          else:          (chroma,0,x)+(m,m,m)
          fi
    enddef;
    vardef hsv_color(expr h,s,v) = % HSB->rgb following wikipedia article on "HSL and HSV"
          save chroma, hh, x, m;
          chroma = v*s;
          %% The following cycles through color range once
          hh = h/60;
          x  = chroma * (1-abs(hh mod 2 - 1));
          m  = v - chroma;
          if      hh < 1: (chroma,x,0)+(m,m,m)
          elseif hh < 2: (x,chroma,0)+(m,m,m)
          elseif hh < 3: (0,chroma,x)+(m,m,m)
          elseif hh < 4: (0,x,chroma)+(m,m,m)
          elseif hh < 5: (x,0,chroma)+(m,m,m)
          else:          (chroma,0,x)+(m,m,m)
          fi
    enddef;
    \IfBooleanTF{#8}
    {%
        \IfBooleanTF{#9}
        {% cycle through spectrum multiple times
            beginfig(1);
                for t=0 step #1 until 360: 
                    draw #2 dir #5t -- #3 dir #6t
                        withpen pencircle scaled #7 withcolor hsv_color_cyc(t, 1, 1); 
                endfor;
                draw (288,0) .. (0,288) .. (-288,0) .. (0,-288) .. cycle;
            endfig;
        }%
        {% cycle through spectrum once
            beginfig(1);
                for t=0 step #1 until 360: 
                    draw #2 dir #5t -- #3 dir #6t
                        withpen pencircle scaled #7 withcolor hsv_color(t, 1, 1); 
                endfor;
                draw (288,0) .. (0,288) .. (-288,0) .. (0,-288) .. cycle;
            endfig;
        }%
    }%
    {% one color
        beginfig(1);
            for t=0 step #1 until 360: 
                 draw #2 dir #5t -- #3 dir #6t
                    %% << change (r,g,b) spec to suit
                    withpen pencircle scaled #7 withcolor (0.2,0.5,0.5); %% (0,0,0) for black
            endfor;
            draw (288,0) .. (0,288) .. (-288,0) .. (0,-288) .. cycle;
        endfig;
    }%
    \end{mplibcode}
}

\begin{document}

\makevenusL[.2]{260}{288}{8}{13}[0.125]
\makevenusL[.2]{260}{288}{8}{13}[0.125]*
\makevenusL[.2]{260}{288}[225]{8}{13}[0.125]**

\end{document}


%%% Local Variables: 
%%% coding: utf-8
%%% mode: latex
%%% TeX-engine: luatex
%%% End: 

enter image description here

14
  • 5
    Spectacular...I have upvoted before.
    – Sebastiano
    Dec 22, 2020 at 12:54
  • 4
    Wow, this is incredible!
    – fractal
    Dec 22, 2020 at 14:35
  • 4
    Woohoo! This is exactly what I was looking for. I'm going to make all kinds of variations for this beautiful \makevenus macro. I was just curious why you chose 2880 for \maxtimes; is there a reason for that? Dec 22, 2020 at 21:40
  • 4
    @AmirHossein Thanks. 2880 was a rough guess for what would display the symmetries.
    – sgmoye
    Dec 22, 2020 at 21:44
  • 2
    @sgmoye Makes sense. I just checked it and 2700 is enough to make the symmetry. Here is the step-by-step construction: docdro.id/syvxJNf Dec 22, 2020 at 22:50
32

Here is a Metapost "one-liner" that draws the geocentric view of the orbit. Compile with lualatex:

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
beginfig(1);
    draw for t=0 upto 359: 108 dir 13t - 147 dir 8t .. endfor cycle;
endfig;
\end{mplibcode}
\end{document}

So what is going on here? First note that the Earth is 147 million km from the sun, and Venus is 108 million km. So I have used these distances as my radius (scaled down so that 1 pt = 1 million km). And then that Venus does 13 orbits to Earth's 8 orbits.

Now note, that in MP you can write polar coordinates as r * dir theta. So for a given angle t, Venus will be at 108 * dir 13t when Earth is at 147 * dir 8t. (And because MP allows implied multiplication between a known constant and a function we can actually write 108 dir 13t and 147 dir 8t directly.)

To get the geocentric view we want to know where Venus is from Earth, so the simplest way to get a point representing that is to subtract the pair representing the position of Earth from the pair representing the position of Venus. So at a given angle t, one way to represent the position of Venus from Earth is

108 dir 13t - 147 dir 8t

The rest of the one liner just wraps this up into an inline for loop.

for t=0 upto 359: 108 dir 13t - 147 dir 8t .. endfor cycle

using .. to connect each point to the previous, and cycle to close the path at the end. This could be saved as a path variable. Or as I have shown, you can pass it straight to draw. The path comes back on itself at t=360,

Heliocentric

And another that does the corresponding heliocentric view (where each line connects Earth to Venus...)

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibnumbersystem{double}
\begin{mplibcode}
beginfig(1);
    for t=0 step 1/4 until 359: draw 108 dir 13t -- 147 dir 8t withpen pencircle scaled 1/8; endfor;
endfig;
\end{mplibcode}
\end{document}

Note the need for the enhanced number system here... again compile with lualatex. This one is similar to the first, except that I have moved the draw statement inside the loop, and instead of subtracting the Earth point from the Venus point, I am drawing a line segment between them, as in the OP animation at the top.

Some HSV colours help a bit here. In particular the five cardoids appear more clearly like this:

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibnumbersystem{double}
\begin{mplibcode}
vardef hsv_color(expr h,s,v) =
    % following wikipedia article on "HSL and HSV"
    save chroma, hh, x, m;
    chroma = v*s;
    hh = h/60;
    x  = chroma * (1-abs(hh mod 2 - 1));
    m  = v - chroma;
    if     hh < 1: (chroma,x,0)+(m,m,m)
    elseif hh < 2: (x,chroma,0)+(m,m,m)
    elseif hh < 3: (0,chroma,x)+(m,m,m)
    elseif hh < 4: (0,x,chroma)+(m,m,m)
    elseif hh < 5: (x,0,chroma)+(m,m,m)
    else:          (chroma,0,x)+(m,m,m)
    fi
enddef;
beginfig(1);
    for t=0 step 1/4 until 360-1/4: 
        draw 108 dir 13t -- 147 dir 8t
        withpen pencircle scaled 1/8 withcolor hsv_color(t, .4, .8); endfor;
endfig;
\end{mplibcode}
\end{document}

Almost good enough for a bank note now....

6
  • Wow, this is very cool and convenient. Thank you! I didn't know about Metapost... I just changed Venus' relative angular position from 13t to 13.004t to make it closer to reality and it works like a charm. I will be having so much fun with MP to generate similar diagrams in the following days. Dec 22, 2020 at 21:31
  • I'm loving the coloured version of your design. Is it possible to make a macro using Metapost (similar to \makevenus command in sgmoye's answer). I tried to make one but failed miserably... Dec 23, 2020 at 20:50
  • Sure, it is, but the documentation is a bit spare. Try texdoc luamplib or look for luamplib on CTAN. When I get a few mins spare I’ll update this with an example.
    – Thruston
    Dec 23, 2020 at 22:40
  • Thank you very much, I'll look it up. It would be great to see an example. Dec 24, 2020 at 0:41
  • The number of petals is the difference between the two numbers defining the harmonic: 5 = 13 - 8.
    – Cicada
    Dec 24, 2020 at 7:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .