In making strings between balloons in Balloon Platform Defense, I had to figure out how to draw a string hanging between two points, the shape more generally known as a catenary. All the examples I found online either placed constraints on the problem such as both points being horizontally level, or relied on your having certain knowledge available such as the angle the string started off at, which wasn’t available in this case – all that is known is the start and end points of the string, and the length of the string. Wikipedia provides a lot of information about the equations that make up a catenary, and is what I used as a starting point.
The equation for the catenary is given as:
y=a\cosh \left (\frac{x}{a} \right )=\frac{a\left ( e^{\frac{x}{a}}+e^{-\frac{x}{a}} \right )}{2}This assumes that the lowest point of the string is where it crosses the y-axis. In practice, the equation will have constants added to the x and y variables, both of which are still to be determined, since we don’t know where the lowest point will be – one of the factors that makes this more complicated than all the textbook examples. (Have you noticed how real life math problems are always so much more complicated than textbook examples? I’m still waiting for the real life problem involving an integral that can be solved analytically.) But that will have to wait, because first we have to determine the value of a, the constant in the equation which will essentially decide how narrow or wide the curve is. Beware that if the points are in the same point on the x-axis, a will be infinite, so you should have handled that case – or even the case where they are very close and a is incalculably large – before getting this far. (Bear in mind that ‘incalculably large’ in this context is any number that, if used as an argument for the exponential function, would cause it to return NaN or Infinity for whatever floating-point precision you are using.) Also of course, avoid letting your code get this far if the two points are further apart than the length of the string – the end result will be bad.
Computing the Scaling Factor
To compute a, Wikipedia gives the equation (based on a property of the catenary that, whereas cosh gives the position of the curve, sinh gives the length of the curve):
\sqrt{s^{2}-v^{2}}=2a\sinh \left ( \frac{h}{2a} \right )where s is the length of the string, and h and v are the (absolute) horizontal and vertical distances between the starting and ending points. These are all known values, leaving just the one variable to find, but it has to be solved for numerically. I first tried to do this without being too picky about my starting value, and it was hit and miss whether it converged or not. Fortunately it turns out to be easy to find a good starting value. The Taylor series for the hyperbolic sine is:
\sinh x=x+\frac{x^{3}}{3!}+\frac{x^{5}}{5!}+\cdotsIf we make the substitutions u = \frac{1}{4a^{2}} and c = \sqrt{s^{2}-v^{2}}, the formula to be solved becomes:
c=\frac{1}{\sqrt{u}}\sinh \left ( h\sqrt{u} \right )which, taking the first three terms of the Taylor series for sinh and simplifying, gives:
c=\frac{1}{\sqrt{u}}\left (h\sqrt{u}+\frac{\left ( h\sqrt{u} \right )^{3}}{3!} +\frac{\left ( h\sqrt{u} \right )^{5}}{5!} \right )c=\frac{1}{\sqrt{u}}\left (h\sqrt{u}+\frac{h^{3}u\sqrt{u}}{3!} +\frac{h^{5}u^{2}\sqrt{u}}{5!} \right ),
c=h+\frac{h^{3}u}{3!} +\frac{h^{5}u^{2}}{5!}, which rearranges to:
\frac{h^{5}}{120}u^{2}+\frac{h^{3}}{6}u+\left ( h-c \right )=0This is a simple quadratic equation in u and substituting its values into the high school formula x = \frac{-b\pm \sqrt{b^{2}-4ac}}{2a} gives us a good starting point for u, and in turn, a.
u = \frac{-\frac{1}{6}h^{3}+ \sqrt{\frac{1}{36}h^{6}-\frac{1}{30}h^{5}\left ( h-c \right )}}{\frac{1}{60}h^{5}}, where a=\frac{1}{2\sqrt{u}}
This is a close enough starting point to find a solution using Newton’s method. Putting into the form f\left ( a \right )=0 means that f\left ( a \right )=2a \sinh \left ( \frac{h}{2a} \right )-c and {f}'\left ( a \right )=2 \sinh \left ( \frac{h}{2a} \right )-\frac{h}{a}\cosh \left ( \frac{h}{2a} \right )
Since a_{n+1}=a_{n}-\frac{f\left ( a_{n} \right )}{{f}'\left ( a_{n} \right )},
a_{n+1}=a_{n}-\frac{2a \sinh \left ( \frac{h}{2a} \right )-c}{2 \sinh \left ( \frac{h}{2a} \right )-\frac{h}{a}\cosh \left ( \frac{h}{2a} \right )}=a_{n}-\frac{a \sinh \left ( \frac{h}{2a} \right )-0.5c}{ \sinh \left ( \frac{h}{2a} \right )-\frac{h}{2a}\cosh \left ( \frac{h}{2a} \right )}I currently test for the series converging to within 0.001 of it’s previous value, which usually happens in 2 to 4 iterations, though sometimes taking over ten when the x-coordinates are closer together. Higher-order Householder methods would probably be possible without requiring very much more computation, since the bulk of the computation is in working out the hyperbolic functions, and further derivatives of the function should only result in more multiples of these. Now that we know the scaling, we can work out the translation.
Computing the Translation
Since the standard equation y=a\cosh \left (\frac{x}{a} \right ) assumes the catenary is symmetrical about the y axis and has it’s bottom at (0,a) whereas the one we are drawing might be anywhere on the screen, we have to work out a translation (p,q) which will move it to the correct position, making our equation:
y-q=a\cosh \left (\frac{x-p}{a} \right )(As a reminder, a is a known value by this point, x and y are variables, so just p and q are still unknown values.) My first thought was to take the equation and substitute in our known values of x and y – that is, the two endpoints of the string. While this works in theory, the resulting equations, despite my best efforts to simplify them, often resulted in some values during the working out process that double-precision floating-point variables couldn’t cope with. I ended up having to find a different approach, which ended up being more efficient anyway.
Remember that substituting sinh for cosh in the catenary equation gives the length of the curve. Substituting the x-coordinates of our left and right end-points into this form, along with the known desired length of the string to be drawn (s), gives the equation:
a\sinh \left (\frac{x_{right}-p}{a} \right )-a\sinh \left (\frac{x_{left}-p}{a} \right )=sOne of the hyperbolic identities states that:
\sinh x - \sinh y=2\cosh\left ( \frac{x+y}{2} \right ) \sinh \left ( \frac{x-y}{2} \right )Therefore:
\sinh \left (\frac{x_{right}-p}{a} \right )-\sinh \left (\frac{x_{left}-p}{a} \right )=2\cosh\left ( \frac{x_{right}+x_{left}-2p}{2a} \right ) \sinh \left ( \frac{x_{right}-x_{left}}{2a} \right )In the cosh argument, the right and left x coordinates are added together and divided by 2, resulting in the coordinate for the middle, so we may as well replace it with that. The subtraction in the sinh argument gives the same value to which we earlier referred as h, so the sinh can be calculated to give another known amount, and the original equation rearranged to give:
\cosh\left ( \frac{x_{middle}-p}{a} \right ) = \frac{s}{2a\sinh \left ( \frac{h}{2a}\right )}Now we can solve for p by taking the arcosh, but some caution is needed. Since cosh is an even function, we will always get back a positive answer, but there is also a negative answer, giving two possible values for p.
p =x_{middle}\pm a\cosh^{-1}\left ( \frac{s}{2a\sinh \left ( \frac{h}{2a}\right )} \right )To find which value we want, we can check the y-coordinates of the end-points. Experimenting with a piece of string will tell you that if for equivalent string lengths and x-coordinates, the bottom of the curve (if it’s within the bounds of the x-coordinates at all) will be to the right of the middle (and hence the one resulting from the positive arcosh) if the right y-coordinate is lower than the left y-coordinate, and vice versa.
One other problem that can arise is that when the y-coordinates are equal, the bottom of the curve should be exactly in the middle, so the argument of the cosh should be zero, and the amount of which you take the arcosh should accordingly be one. Due to floating-point inaccuracies, it often works out as being slightly less than one, causing an error when you try to take the arcosh. It’s wise to check for the y-coordinates being equal (or almost equal) before starting this computation, to save some computation as well as the possibility of this error, and in that case just assume that p is exactly halfway between the end-points.
Now that a and p are known, q can be calculated just by substituting x and y values for one of the end-points into the equation
y-q=a\cosh \left (\frac{x-p}{a} \right )and rearranging to give:
q=y-a\cosh \left (\frac{x-p}{a} \right )Get your value of q from that, then you have your final equation from which you can draw your curve.
y=a\cosh \left (\frac{x-p}{a} \right )+qTo draw the curve is now simple. I used as accurate as possible computation of the hyperbolic functions during most steps of the calculation of the parameters, since it was hard to tell how any inaccuracy would propagate. But in this final step, which will require the majority of the calculations, you will know how high is the argument you’re using and how much inaccuracy you can tolerate, and will be able to predict whether or not a Taylor series to a certain power will be accurate enough for your needs – and most of the time it will be worth using, in my experience.