I would need to draw catenary between dots A and B, A(x1, y1, z1), B(x2, y2, z2) using N straight lines.
I tried with formula for catenary
z = a * Math.cosh(x / a)
where
a = H / (H = horizontal tension, = weight per unit)
and my code is
xCurrent = x1;
yCurrent = y1;
zCurrent = z1;
for (N number of lines) {
x1 = xCurrent;
x2 = xCurrent + x1x2TotalDistance/N;
y1 = yCurrent;
y2 = yCurrent + y1y2TotalDistance/N;
H = 25000;
omega = 1.4 * length;
a = H / omega;
z1 = a * Math.cosh(x1 / a);
z2 = a * Math.cosh(x2 / a);
zDifference = z1 - z2;
zNew = zCurrent - zDifference;
DrawLine(T1(x1, y1, zCurrent), T2(x2, y2, zNew))
zCurrent = zNew;
xCurrent += x1x2TotalDistance/N;
yCurrent += y1y2TotalDistance/N;
}
But the problem is that last zNew is not equal to z2 from B dot. Difference is not that big but it is too big to be noticable.
And if I increase H for example 100 times, then last zNew is equal to z2 in acceptable way, but I don't have catenary shape anymore, it draws straight line.
Is there a way to have last zNew equal to z2 from B dot to at least 2 decimal points, and to keep shape of catenary?
Let's assume the two points are $\vec{p}_1 = (x_1 , y_1 , z_1)$ and $\vec{p}_2 = ( x_2 , y_2 , z_2 )$, with acceleration due to gravity towards negative $z$ axis. Then, we have $$\left\lbrace ~ \begin{aligned} x(t) &= (1-t)x_1 + t x_2 = x_1 + t (x_2 - x_1) \\ y(t) &= (1-t)y_1 + t y_2 = y_1 + t (y_2 - y_1) \\ z(t) &= z_0 + \displaystyle a \cosh\left( \frac{w (t - t_0)}{a} \right) \\ \end{aligned} \right.$$ where $w$ is the horizontal distance, $w = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)}$, $t_0$ is the relative location of the lowest point of the catenary (to be solved), $z_0$ is the $z$-location of the catenary (also to be solved), and $a$ is the scale factor: $$a = \frac{H}{m}$$ where $H$ is the horizontal tension, and $m$ is the total mass of the catenary.
To determine $z_0$ and $t_0$, we need to apply $$\left\lbrace ~ \begin{aligned} z(0) &= z_1 \\ z(1) &= z_2 \\ \end{aligned} \right.$$ Subtracting the latter from the former gives us $$\cosh\left(\frac{w (1 - t_0)}{a}\right) - \cosh\left(\frac{w t_0}{a}\right) - \frac{z_2 - z_1}{a} = 0$$ which (I believed!) we have to solve numerically for $t_0$.
In practice, we can use $$f(x) = \cosh\bigr(k (1 - x)\bigr) - \cosh\bigr(k x\bigr) - c$$ where $k = w/a \gt 0$ and $c = (z_2 - z_1)/a$. $f(x)$ is monotonically decreasing everywhere. So, we can start at $x = 1/2$, and extend the search right or left depending on the sign of $f(x)$. When the range of $x$ spanning zero is found, you can use the binary search method on that span to find $x$.
Here is a Python 3 example for finding $t_0 = x$ given $k$ and $c$:
Note that
epsilonis the desired relative precision; i.e.0.000001gives you about six digits of precision.Edited: Claude Leibovici showed a proper mathematical solution in their answer here. So, instead of the above, I recommend using
I have verified with a large number of random $k \gt 0$ and $c$ that the two implementations produce the same results (within the specified
epsilonfor the numerical search) – but this latter version is obviously much, much more efficient! (This even better version now without any conditionals, again thanks to Claude Leibovici.)The only trick there is that for $k \ge 39$ or so, $\cosh(k) - 1 = \cosh(k) = \sinh(k)$ using double-precision floating-point numbers. This means that this operation is suspect to domain cancellation error. Fortunately, $d(k) = 1 + \sinh(k) - \cosh(k)$ is a simple function with $d(0) = 0$, $d(k) \lt 1$ for $k \in \mathbb{R}$, and we can avoid the domain cancellation error by subtracting the two hyperbolic functions first, then adding 1 to their difference. (In C, one would use e.g.
d = 1.0 - (double)(a - b);, as the cast forcesa - bto be evaluated first and atdoubleprecision and range.) This will be as accurate as is possible with floating point numbers for the entire range $k \gt 0$, $k \in \mathbb{R}$ we're interested in.Finally, we can solve $z_0$ from $z(0) = z_1$ (or $z(1) = z_2$), i.e. $$z_0 = z_1 - a \cosh\left( \frac{- w t_0}{a} \right)$$
To draw the catenary using $N$ line segments $(x_i, y_i, z_i)$ to $(x_{i+1}, y_{i+1}, z_{i+1})$, for $i = 0 .. N-1$. Each point $(x_i, y_i, z_i)$ for $i = 0 .. N$ is $$\begin{aligned} t &= \frac{i}{N} \\ x_i &= (1-t)x_1 + t x_2 \\ y_i &= (1-t)y_1 + t y_2 \\ z_i &= z_0 + \displaystyle a \cosh\left( \frac{w (t - t_0)}{a} \right) \\ \end{aligned}$$