[Closed] [SOLVED] Hanging rope (catenary)
Yes, real effective! Thanks again, hopefully I can play with it today a little
I noticed there was a small issue with the code above. The catenary worked splendid on the x plane, but was looking off in the y plane. (moving one of the helpers to the same x value as the other)
I added a small portion of code to the catenary function to account for this. Basically I’m just looking at the caterary in the x plane and then translate the calculated positions to the plane between the two points.
(see top and bottom of the function)
Thanks to you I now have a perfect and quick catenary:
fn catenary pt0 pt1 extent count &sag:1 max_iterations:100 =
(
--build local coordsys for flat approach
xMax = distance pt0 [pt1.x,pt1.y,pt0.z]
zMax = pt1.z - pt0.z
oldpt0 = pt0
oldpt1 = pt1
pt0 = [0,0,0]
pt1 = [xMax,0,zMax] / extent
len = 1.0
fn interp s len d h =
(
2 * _sinh(s*d/2)/s - sqrt(len*len - h*h)
)
fn deriv s len d h =
(
2 * _cosh(s*d/2)*d/(2*s) - 2 * _sinh(s*d/2)/(s*s)
)
max_iterations -- maximum number of iterations
min_grad = 1e-10 -- minimum norm of gradient
min_val = 1e-8 -- minimum norm of sag function
step_size = 0.5 -- factor for decreasing step_size
min_step = 1e-9 -- minimum step size
if pt0.x > pt1.x do swap pt1 pt0
d = pt1.x - pt0.x
h = pt1.z - pt0.z
pts = makePointTab count pt0 pt1
ptx = #()
pty = #()
ptz = #()
compPoint3Tab pts ptx pty ptz
if len > sqrt(d*d + h*h) do -- rope is not stretched: straight line
(
-- find rope sag
end = off
for i=1 to max_iterations while not end do
(
val = interp sag len d h
grad = deriv sag len d h
end = abs(val) < min_val or abs(grad) < min_grad
if not end do
(
search = -(interp sag len d h)/(deriv sag len d h)
alpha = 1
_sag = sag + alpha*search
out = false
while (_sag < 0 or abs(interp _sag len d h) > abs(val)) and not out do
(
alpha *= step_size
if not (out = alpha < min_step) do
(
_sag = sag + alpha*search
)
)
sag = _sag
)
)
-- get location of rope minimum and vertical bias
x_left = 0.5 * (log((len + h)/(len - h))/sag - d)
x_min = pt0.x - x_left
_bias = pt0.z - _cosh(x_left * sag)/sag
ptz = #()
for x in ptx do append ptz (_cosh((x - x_min)*sag)/sag + _bias)
)
--scale back to original values
pts = combPoint3Tab ptx pty ptz scale:extent
--recalculate in actual 3d plane
for i=1 to pts.count do
(
pt = pts[i]
horFactor = pt.x / xMax
pt.x = oldPt0.x + ((oldPt1.x - oldpt0.x) * horFactor)
pt.y = oldPt0.y + ((oldPt1.y - oldpt0.y) * horFactor)
pt.z = oldPt0.z + pt.z
pts[i] = pt
)
pts
)
I also tried and successfully got a parabola to work but the sharpness at the bottom of the curve was bothering me. Now I have the ‘real’ thing at least. I also figured out what I was doing wrong in my own catenary function. I was superclose but the normalizing and properly setting the bias was where I was off. Thanks again man