Voy a empezar de 0 porque con las ediciones que hice al final es cierto que metí la pata.
La ecuación de la velocidad suponiendo una aceleración constante es v(t)=a0t+v0. En consecuencia, la posición dada por dicha velocidad se calcula integrando la anterior ecuación, lo que resulta en p(t)=(a0/2)*t^2+v0t+p0.
Con esta ecuación ya calculada podemos empezar a trabajar. Lo primero es calcular el momento en el que se cruzan. Como los dos objetos van a tener aceleración constante únicamente pueden cruzarse, a lo sumo, una vez y esto es así porque la posición únicamente depende del tiempo, luego existe una relación de proporcionalidad entre la posición de los dos objetos.
El caso es que ambos objetos se encontrarían cuando p1(t)=p2(t), es decir, cuando (a1/2)*t^2+v1t+p1=(a2/2)*t^2+v2t+p2. Si esta última ecuación la transformamos para trabajar con ella obtenemos dos posibles soluciones que básicamente nos van a dar el mismo resultado:
- ((a1-a2)/2)*t^2+(v1-v2)*t-(p1-p2)=0
- ((a2-a1)/2)*t^2+(v2-v1)*t-(p2-p1)=0
Estas ecuaciones no son más que un polinomio de grado dos (at^2+bt+c=0) y sus raices se pueden calcular mediante las ecuaciones:
- r1 = (-b - sqrt(b^2-4ac))/2a
- r2 = (-b + sqrt(b^2-4ac))/2a
Como hemos comentado como mucho una de estas dos raices indicará un punto de cruce, la otra o tendrá parte imaginaria o dará un valor negativo... estamos hablando de tiempo y se asume que el tiempo ha de avanzar, no retroceder.
Vale, supongamos que hemos encontrado un punto de cruce en t=r. Si no hubiese restricciones en cuanto al movimiento de los objetos esa sería nuestra solución final... sin embargo una restricción es que si un objeto alcanza velocidad 0 se detiene y ya no se mueve. Esta restricción podemos tratarla de la siguiente forma:
- Calculamos el tiempo necesario para parar cada objeto. Al igual que sucedía con los puntos de cruce este cálculo puede arrojar 0 o 1 soluciones posibles.
- Si el tiempo anterior es mayor que el tiempo que tardan en cruzarse descartamos el tiempo de la parada... nos interesa saber dónde se cruzan, lo que hagan despues...
- Si resulta que ambos objetos se para antes del tiempo de cruce entonces no se van a cruzar nunca... obvio, no?
- Si solo un objeto se para antes del cruce podemos hacer lo siguiente:
- Calculamos el punto en el que se para el objeto
- Calculamos el tiempo que tardará el segundo objeto en alcanzar dicha posición
- Comprobamos si el segundo objeto se para o no antes de llegar a esta nueva posición
Y ya está... después de todo este proceso pueden pasar dos cosas:
- Que tengamos un tiempo válido. Esto implica que ambos objetos se cruzan después de todo
- Que tengamos un tiempo no válido. Es decir, no se cruzan.
En el caso de que se crucen basta con verificar la posición de cualquiera de los objetos en el tiempo hallado y listo.
Una forma de hacerlo con C++:
Código C++:
Ver original#include <array>
#include <iostream>
#include <cmath>
#include <memory>
enum ParamIndex
{
Aceleracion,
Velocidad,
Posicion
};
using ParamsObj = std::array<double,3>;
using NullDouble = std::unique_ptr<double>;
constexpr bool SonEquivalentes(double a, double b)
{
}
NullDouble CalcularTiempoParada(ParamsObj const& obj)
{
NullDouble toReturn;
auto a = obj[Aceleracion];
auto v = obj[Velocidad];
if( !SonEquivalentes(a,0) && !SonEquivalentes(v,0) )
{
auto tiempo = -v/a;
if( tiempo > 0. )
toReturn = std::make_unique<double>(tiempo);
}
return toReturn;
}
NullDouble CalcularTiempoCruce(ParamsObj const& obj1, ParamsObj const& obj2)
{
NullDouble toReturn;
auto a = (obj1[Aceleracion]-obj2[Aceleracion])/2;
auto b = obj1[Velocidad] -obj2[Velocidad];
auto c = obj1[Posicion] -obj2[Posicion];
if( SonEquivalentes(a,0) ) // Si no hay aceleración
{
auto tiempo = -c/b;
if( tiempo > 0 )
toReturn = std::make_unique<double>(tiempo);
}
else
{
int raiz = b*b-4*a*c;
if( raiz > 0 )
{
auto tiempo
= (-b
+ sqrt(raiz
))/(2*a
); if( (tiempo < 0) || !std::isfinite(tiempo) )
tiempo
= (-b
- sqrt(raiz
))/(2*a
); if( (tiempo > 0) && std::isfinite(tiempo) )
toReturn = std::make_unique<double>(tiempo);
}
}
return toReturn;
}
double CalcularPosicion(ParamsObj const& obj, double tiempo)
{
auto a = obj[Aceleracion];
auto v = obj[Velocidad];
auto p = obj[Posicion];
return ((a
*pow(tiempo
,2))/2)+(v
*tiempo
)+p
; }
void Comprobar(ParamsObj const& obj1, ParamsObj const& obj2)
{
std::cout << "Condiciones iniciales:\n"
<< "\tObjeto 1: a=" << obj1[Aceleracion] << ", v=" << obj1[Velocidad] << ", x=" << obj1[Posicion] << "\n"
<< "\tObjeto 2: a=" << obj2[Aceleracion] << ", v=" << obj2[Velocidad] << ", x=" << obj2[Posicion] << "\n"
<< "\n\nResultado:\n";
auto tiempo = CalcularTiempoCruce(obj1, obj2);
NullDouble posicion;
if( tiempo )
{
auto t1 = CalcularTiempoParada(obj1);
auto t2 = CalcularTiempoParada(obj2);
bool usarT1 = ( t1 && *t1 < *tiempo );
bool usarT2 = ( t2 && *t2 < *tiempo );
// Si ambos se paran antes del cruce entonces no se cruzan
if( usarT1 && usarT2 )
{
usarT1 = usarT2 = false;
t1.reset();
t2.reset();
tiempo.reset();
}
// Si alguno se para antes del cruce hay que reajustar las fórmulas
if( usarT1 )
{
auto pos = CalcularPosicion(obj1,*t1);
ParamsObj newObj1 = { 0, 0, pos };
tiempo = CalcularTiempoCruce(newObj1,obj2);
if( tiempo )
{
if( !t2 || *tiempo < *t2 )
posicion = std::make_unique<double>(pos);
else
tiempo.reset();
}
}
else if( usarT2 )
{
auto pos = CalcularPosicion(obj2,*t1);
ParamsObj newObj2 = { 0, 0, pos };
tiempo = CalcularTiempoCruce(obj1,newObj2);
if( tiempo )
{
if( !t1 || *tiempo < *t1 )
posicion = std::make_unique<double>(pos);
else
tiempo.reset();
}
}
}
if( tiempo )
{
auto pos = (posicion)? *posicion : CalcularPosicion(obj1,*tiempo);
std::cout << "\tLos objetos se cruzan en p=" << pos
<< ", en t=" << *tiempo << "\n";
}
else
{
std::cout << "\tNo se cruzan\n";
}
}
int main()
{
std::cout << "EJEMPLO1: El objeto de atrás va mas rápido\n";
ParamsObj paramsObj1 = { 2 /*aceleracion*/, 2 /*velocidad*/, 5 /*posicion*/};
ParamsObj paramsObj2 = { 1 /*aceleracion*/, 3 /*velocidad*/, 10 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO2: El objeto de delante va mas rápido\n";
paramsObj1 = { 1 /*aceleracion*/, 2 /*velocidad*/, 5 /*posicion*/};
paramsObj2 = { 1 /*aceleracion*/, 3 /*velocidad*/, 10 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO3: El objeto mas adelantado frena mas rapido\n";
paramsObj1 = { -1 /*aceleracion*/, 10 /*velocidad*/, 5 /*posicion*/};
paramsObj2 = { -3 /*aceleracion*/, 10 /*velocidad*/, 7 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO4: Ambos objetos se paran antes de encontrarse\n";
paramsObj1 = { -1 /*aceleracion*/, 5 /*velocidad*/, 5 /*posicion*/};
paramsObj2 = { -2 /*aceleracion*/, 30 /*velocidad*/, 10 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO5: Un objeto se para y el otro le da caza\n";
paramsObj1 = { -1 /*aceleracion*/, 10 /*velocidad*/, 1000 /*posicion*/};
paramsObj2 = { 0 /*aceleracion*/, 2 /*velocidad*/, 0 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO6: Cada objeto va en un sentido y no se cruzan\n";
paramsObj1 = { -1 /*aceleracion*/, -1 /*velocidad*/, -1 /*posicion*/};
paramsObj2 = { 1 /*aceleracion*/, 1 /*velocidad*/, 1 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO7: Objetos enfrentados se encuentran a mitad de camino\n";
paramsObj1 = { -1 /*aceleracion*/, -1 /*velocidad*/, 10 /*posicion*/};
paramsObj2 = { 1 /*aceleracion*/, 1 /*velocidad*/, -5 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
std::cout << "\nEJEMPLO8: Objetos enfrentados se paran cerca pero antes de encontrarse\n";
paramsObj1 = { 1 /*aceleracion*/, -1 /*velocidad*/, 10 /*posicion*/};
paramsObj2 = { -1 /*aceleracion*/, 1 /*velocidad*/, -5 /*posicion*/};
Comprobar(paramsObj1,paramsObj2);
return EXIT_SUCCESS;
}