2

I am using the automata library in TikZ, and I'd like to obtain an automaton with its nodes describing an equilateral $n$-polygon inscribed within an ellipse.

I was trying to adapt the answer in a previous question, namely using the code

\begin{tikzpicture}
    \draw (0,0) ellipse (3 and 1);
    \node[state, accepting] at ($(0,0)+(0:3 and 1)$) {};
    \node[state] at ($(0,0)+(54:3 and 1)$) {};
    \node[state] at ($(0,0)+(75:3 and 1)$) {};
\end{tikzpicture}

But i'd like to avoid the explicit computation of the node positions, and I wondered whether there is some automatic way of obtaining them.

What is the easiest way of doing so?

suitangi
  • 799
  • Please add a MWE and best what you've got so far. – TeXnician Feb 08 '17 at 11:59
  • Have you already tried an automated approach using a for loop? – TeXnician Feb 08 '17 at 12:09
  • Is that possible without actually computing the position of every node? – suitangi Feb 08 '17 at 15:16
  • Probably it would calculate most positions for you, but you would have to do some maths. It was more a suggestion than a specific answer. – TeXnician Feb 08 '17 at 15:18
  • I don't understand how the polygon can be equilateral, but inscribed in an ellipse instead of a circle. Moreover, the code you provided don't have the nodes inscribed in a ellipse, but instead with their centers along an elliptical path. – JLDiaz Feb 08 '17 at 17:53
  • @JLDiaz By equilateral I mean "with all sides having the same length", and inscribed implies "with its vertices along the eliptical path", right? – suitangi Feb 08 '17 at 18:31
  • @suitangi Yes, you are right, excuse me. At first I was unable to imagine an equilateral shape inscribed in a ellipse, because I was thinking on regular shapes. – JLDiaz Feb 08 '17 at 18:38
  • 1
    @suitangi I guess it would not be easy... see this answer for example. – JLDiaz Feb 08 '17 at 18:52

1 Answers1

6

The problem is more difficult than it seems. One easy (but wrong) approach could be to divide 360 degrees in equal-sized sectors, and place the nodes at the intersections of those sectors with the ellipse. This works only if the ellipse is a circle. The result worsens as the eccentricity of the ellipse increases, as the following animation shows (for seven nodes):

Bad result

The next approach (still wrong) is to compute the perimeter of the ellipse, divide it in equal-length elliptic arcs and place the nodes at the computed points. This is difficult to do because the lenght of an elliptic arc has no closed formula. It has to be computed by numerical approximations.

The following python code computes the coordinates of seven points evenly spaced around the perimeter of an ellipse of radii 3 and 1. The code is a direct translation to python3 of the pseudocode given in this answer. It prints the result in a format suitable to be copy/pasted into a tikz loop.

from math import sin,cos,pi,sqrt
def dp(t, r1, r2):
    return sqrt((r1*sin(t))**2 + (r2*cos(t)**2))

def main(r1, r2, n):
    circ = sum([ dp(t/10000*2*pi, r1, r2) for t in range(10000) ])
    nextPoint = 0
    points = []
    run = 0.0
    for i in range(10000):
        t = i*2*pi/10000
        if n*run/circ >= nextPoint:
            points.append((r1*cos(t), r2*sin(t)))
            nextPoint += 1
        run += dp(t, r1, r2)
    return points


if __name__ == "__main__":
    pts = main(3,1,7)
    print(",".join("%f/%f"%(x,y) for x,y in pts))

Running this code, the following is output:

3.000000/0.000000,1.423740/0.880212,-0.478610/0.987192,-2.343646/0.624261,-2.342469/-0.624752,-0.474888/-0.987392,1.427057/-0.879615

I copy&pasted that sequence into the following tikz code:

\begin{tikzpicture}    
    \draw[name path=ellipse, blue] (0,0) ellipse(3 and 1);

    \foreach \x/\y  [count=\n from 0] in {3.000000/0.000000,1.423740/0.880212,-0.478610/0.987192,-2.343646/0.624261,-2.342469/-0.624752,-0.474888/-0.987392,1.427057/-0.879615} {  
       \coordinate[state] (node-\n) at (\x,\y) {}; 
    }

    % Draw nodes and edges 
    \node[state, accepting, fill=white] at (node-0) {0};
    \foreach \n [remember=\n as \previous (initially 0)] in {1,...,6} {
       \node[state, fill=white] at (node-\n) {\n}; 
       \draw[red,thick] (node-\previous) -- (node-\n);
    }
    \draw[thick,red] (node-6) -- (node-0);
\end{tikzpicture}

The result is the following:

Result

It can be seen that the nodes are equally spaced in the elliptic path (blue), but the distances between nodes are not equal (red).

Update: The final solution

In my original answer, after the constatation that equal-length elliptic arcs do not imply equidistant nodes, I gave up. But then I was obsessed with this problem, and finally was able to solve it. Be warned, there is no easy solution.

Lets define some notation Every point $(x,y)$ in a ellipse can be represented by a real $t$, using the function $(x,y) = f(t)$, being $f(t)=(a\cos(t), b\sin(t))$. Lets represent the distance between two points $p_i=(x_i,y_i)$ and $p_j=(x_j,y_j)$ by $|p_j-p_i|$, which can be computed as $\sqrt{(x_j-x_i)^2+(y_j-y_i)^2}$.

Now, our problem is to find $N$ points on the ellipse which are equidistant. This problem can be formulated as findng $N$ real values $t_0,\dots,t_{N-1}$ such that $|f(t_1)-f(t_0)| = |f(t_2) - f(t_1)| = \dots = |f(t_0)-f(t_{N-1}|$.

So we have $N$ unknowns, but only $N-1$ equations (the distance equalities). This means that there is infinite solutions, which correspond to different "rotations" on the $N$ points, depending on where we put the first one. We can fix the value of $t_0$, and then have $N-1$ unknowns which should be solvable.

The problem is that the $N-1$ equations are non-linear, so the solution is not easy. Some numerical optimization algorithm is required. I used scipy.optimize.fsolve for this.

I programmed a meta-function which receives as arguments the lengths of the radii of the ellipse ($a$ and $b$), the number of desired nodes (eg. 7) and the position of the first node (as an integer $t_0$ which can take any value from $0$ to $2\pi$). With these parameters, the meta-function creates a function which implements the required equations to solve. Then scipy.optimize.fsolve is used to find the solutions.

I find inappropiate to paste the code of this python script here, so I pasted it as a gist instead. This script solves the system for different values of $t_0$, in order to create the following animation (in which the yellow node represents the "fixed" one by the value of $t_0$, and the blue nodes are the ones found by the script).

Animation

This is an excerpt of the output of the script, showing the tikz figure produced for the case $t_0=0$:

\begin{tikzpicture}
\fill[white] (-3.5, -1.5) rectangle (3.5, 1.5);
\draw[blue] (0,0) ellipse(3 and 1);
\foreach \x/\y [count=\n from 0] in {3.000000/0.000000,1.585814/0.848868,-0.056656/0.999822,-1.696726/0.824697,-1.696726/-0.824697,-0.056656/-0.999822,1.585814/-0.8488
68} {
     \node[fill=blue,circle] (node-\n) at (\x,\y) {};
}
\foreach \n [remember=\n as \previous (initially 0)] in {1,...,6} {
   \draw[thick,red] (node-\previous.center) -- (node-\n.center);
}
\node[fill=yellow, circle] at (node-0) {};
\draw[thick,red] (node-6.center) -- (node-0.center);
\end{tikzpicture}

Unfortunately none of this can be implemented in pure Tex, not even in LuaTeX, due of the use of scipy.

JLDiaz
  • 55,732
  • Probably you mean $f(t)=(acos(t), bsin(t))$ in the second paragraph. Nice explanation and cool animations. Thank you! – suitangi Feb 11 '17 at 20:48