Catenaria entre dos puntos (cadena colgante)

8

Este es uno de los varios desafíos que Calvin's Hobbies dejó para la comunidad .

La curva que hace una cuerda o cadena colgante idealizada es una catenaria .

Una cadena que forma una catenaria.
Imagen de Bin im Garten, vía Wikimedia Commons. Utilizado bajo la licencia CC-By-SA 3.0 .

Escribir un programa que atraerá a una catenaria, como una imagen, en el cuadrante 1 del plano dado dos puntos (x 1 , y 1 ) , (x 2 , y 2 ) , y la "longitud de cuerda" L . L será mayor que la distancia entre los dos puntos.

También debe dibujar ejes en los lados izquierdo e inferior de la imagen (400x400 px min) para la escala. Solo dibuje el cuadrante de x e y en el rango de 0 a 100. (Puede suponer que los puntos están en el rango).

Deben dibujarse puntos, o algo similar, en los puntos finales (x 1 , y 1 ) , (x 2 , y 2 ) para distinguirlos. La curva solo debe dibujarse en el espacio entre estos puntos.

Ajenjo
fuente
¿Qué tan precisos tenemos que ser? ¿La imagen tiene que estar suavizada? ¿Qué tan ancha debe / puede ser la línea?
Sparr
También suponemos que la curva en sí (no solo los puntos) está dentro del rango, ¿verdad? ¿O dibujaríamos dos arcos cortados por el eje si va por debajo de eso?
Geobits
@Sparr La imagen no tiene que estar suavizada. La línea debe tener al menos 1 px de grosor. La catenaria debe ser tan precisa como la aritmética de coma flotante de su idioma.
absenta
55
Iba a hacer esto hasta que me diera cuenta de que las matemáticas podrían ser un poco más complejas que mi cálculo previo actual. Talves el próximo año.
Stretch Maniac
1
@BetaDecay No sé qué es eso. Digamos que es 0.
absenta

Respuestas:

5

Python + NumPy + Matplotlib, 1131

Solo para comenzar, aquí hay un intento que no utiliza ningún conocimiento de cálculo o física que no sea el hecho de que la catenaria minimiza la energía de una cadena. ¡Oye, mi algoritmo puede no ser eficiente, pero al menos tampoco está implementado eficientemente!

import math
import random
import numpy as np
import matplotlib.pyplot as plt
length, x0, y0, x1, y1 = input(), input(), input(), input(), input()
chain = np.array([[x0] + [length / 1000.]*1000, [y0] + [0.] * 1000])
def rotate(angle, x, y):
 return x * math.cos(angle) + y * math.sin(angle), -x * math.sin(angle) + y  * math.cos(angle)
def eval(chain, x1, y1):
 mysum = chain.cumsum(1)
 springpotential = 1000 * ((mysum[0][-1] - x1) ** 2 + (mysum[1][-1] - y1)  ** 2)
 potential = mysum.cumsum(1)[1][-1]
 return springpotential + potential
def jiggle(chain, x1, y1):
 for _ in xrange(100000):
  pre = eval(chain, x1, y1)
  angle = random.random() * 2 * math.pi
  index = random.randint(1,1000)
  chain[0][index], chain[1][index] = rotate(angle, chain[0][index], chain[1][index])
  if( pre < eval(chain, x1, y1)):
   chain[0][index], chain[1][index] = rotate(-angle, chain[0][index], chain[1][index])
jiggle(chain, x1, y1)
sum = chain.cumsum(1)
x1 = 2 * x1 - sum[0][-1]
y1 = 2 * y1 - sum[1][-1]
jiggle(chain, x1, y1)
sum = chain.cumsum(1)
plt.plot(sum[0][1:], sum[1][1:])
plt.show()
QuadmasterXLII
fuente
3

BBC Basic, 300 caracteres ASCII, tamaño de archivo tokenizado 260

  INPUTr,s,u,v,l:r*=8s*=8u*=8v*=8l*=8z=0REPEATz+=1E-3UNTILFNs(z)/z>=SQR(l^2-(v-s)^2)/(u-r)a=(u-r)/2/z
  p=(r+u-a*LN((l+v-s)/(l-v+s)))/2q=(v+s-l*FNc(z)/FNs(z))/2MOVE800,0DRAW0,0DRAW0,800CIRCLEu,v,8CIRCLEr,s,8FORx=r TOu
    DRAW x,a*FNc((x-p)/a)+q
  NEXT
  DEFFNs(t)=(EXP(t)-EXP(-t))/2
  DEFFNc(t)=(EXP(t)+EXP(-t))/2

Emulador en http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Obviamente, esto ya se ha resuelto antes, así que lo primero que hice fue mirar lo que otros han hecho.

La ecuación de una catenaria centrada en el origen es sencilla y=a*cosh(x/a). Se vuelve un poco más complicado si no está centrado en el origen.

Varias fuentes dicen que si se conocen la longitud y los puntos finales, el valor adebe determinarse numéricamente. Hay un parámetro no especificado hen el artículo de wikipedia. Así que encontré otro sitio y básicamente seguí el método aquí: http://www.math.niu.edu/~rusin/known-math/99_incoming/catenary

BBC Basic no tiene sinhe coshincorporado, así que definí dos funciones al final del programa para calcularlas usandoEXP

las coordenadas para el punto izquierdo se deben proporcionar antes del punto derecho, OP confirmó que esto está bien. La longitud se da al final. Los valores pueden estar separados por comas o nuevas líneas.

Código sin golf

  INPUT r,s,u,v,l

  REM convert input in range 0-100 to graphic coordinates in range 0-800 
  r*=8 s*=8 u*=8 v*=8 l*=8

  REM solve for z numerically
  z=0
  REPEAT
    z+=1E-3
  UNTIL FNs(z)/z>=SQR(l^2-(v-s)^2)/(u-r)

  REM calculate the curve parameters
  a=(u-r)/2/z
  p=(r+u-a*LN((l+v-s)/(l-v+s)))/2
  q=(v+s-l*FNc(z)/FNs(z))/2

  REM draw axes, 800 graphics units long = 400 pixels long (2 graphics units per pixel)
  MOVE 800,0
  DRAW 0,0
  DRAW 0,800

  REM draw markers at end and beginning of curve (beginning last, so that cursor is in right place for next step)
  CIRCLE u,v,8
  CIRCLE r,s,8

  REM draw curve from beginning to end
  FORx=r TOu
    DRAW x,a*FNc((x-p)/a)+q
  NEXT

  REM definitions of sinh and cosh
  DEF FNs(t)=(EXP(t)-EXP(-t))/2
  DEF FNc(t)=(EXP(t)+EXP(-t))/2

ingrese la descripción de la imagen aquí

Level River St
fuente
1

Python 2.7 + matplotlib, 424

Correr como

python thisscript.py [length] [x0] [y0] [x1] [y1]

Si puedo suponer que x0 siempre es menor que x1, el recuento de caracteres se reduce a 398

from numpy import *
from pylab import *
from scipy.optimize import *
import sys
c=cosh
l,p,q,u,w=map(float,sys.argv[1:])
if p>u:
 p,q,u,w=u,w,p,q
h=u-p
v=w-q
a=brentq(lambda a:(2.*h/a*sinh(0.5*a))**2-l**2-v**2,1e-20,600)
b=brentq(lambda b:c(a*(1.-b))-c(a*b)-a*v/h,-600/a,600/a)
r=linspace(p,u,100)
plot([p,u],[q,w],'ro')
plot(r,h/a*c(((r-p)/h-b)*a)-h/a*c(a*b)+q,'k-')
gca().set_xlim((0,100))
gca().set_ylim((0,100))
show()

El número mágico 600 que ves aparecer en algunos lugares se debe al hecho de que cosh (x) y sinh (x) comienzan a desbordarse alrededor de x = 710 (600 para mantener un margen)

Básicamente, resuelvo el problema en el marco donde la catenaria atraviesa (0,0) y (x1-x0, (y1-y0) / (x1-x0)) y luego vuelvo a asignar al marco original. Esto mejora la estabilidad numérica mucho .

camelthemammel
fuente