Todavía no he trabajado en las ecuaciones completas para esto, pero aquí hay algunas imágenes para ayudar a entender el problema. Se reduce a algo de geometría:
( Iconos de autos a través de Kenney )
Desde cualquier punto de partida y orientación, podemos dibujar dos círculos con nuestro radio de giro mínimo: uno a la izquierda y otro a la derecha. Estos describen los puntos en el comienzo más estrecho posible de nuestro camino.
Podemos hacer lo mismo para cualquier posición final y orientación deseadas. Estos círculos describen el final más estrecho posible de nuestro camino.
Ahora el problema se reduce a encontrar un camino que une uno de los círculos iniciales a uno de los círculos finales, besando a cada uno a lo largo de su tangente.
(Esto supone que no necesitamos encontrar caminos en torno a obstáculos intermedios, lo cual no se mencionó en la pregunta. La respuesta de Stormwind se centra en cómo podemos usar la información del gráfico de navegación para este tipo de problemas. Una vez que tenemos la secuencia de nodos para pasar, podemos aplicar el siguiente método a cada segmento del plan).
Si, por simplicidad, usamos líneas rectas, obtenemos algo como esto:
Esto nos da el caso limitante. Una vez que haya encontrado un camino con este método, puede inflar artificialmente uno o ambos círculos iniciales y finales para obtener un camino menos directo pero más suave, hasta el punto donde los dos círculos se besan.
Computando estos caminos
Analicemos los casos para una dirección de giro: digamos que comenzamos nuestro camino girando a la derecha.
El centro de nuestro círculo de giro a la derecha es:
startRightCenter = carStart.position + carStart.right * minRadius
Llamemos al ángulo de la sección recta de nuestro camino (medido desde el eje x positivo) pathAngle
Si dibujamos un vector desde rightCenter
el punto donde dejamos el círculo de giro (en cuyo punto debemos estar orientados por pathAngle), entonces ese vector es ...
startOffset = minRadius * (-cos(pathAngle), sin(pathAngle))
Eso significa que el punto donde dejamos el círculo debe ser ...
departure = startRightCenter + startOffset
El punto en el que volvemos a entrar en un círculo de giro depende de si pretendemos terminar con un giro a la izquierda o a la derecha:
// To end with a right turn:
reentry = endRightCenter + startOffset
// To end with a left turn: (crossover)
reentry = endLeftCenter - startOffset
Ahora, si hemos hecho bien nuestro trabajo, la línea que se une departure
a reentry
debería ser perpendicular a startOffset
:
dot(reentry - departure, startOffset) = 0
Y resolver esta ecuación nos dará los ángulos en los que esto es cierto. (Uso un plural aquí porque técnicamente hay dos ángulos de este tipo, pero uno de ellos implica conducir en reversa, lo que generalmente no es lo que queremos)
Sustituyamos el caso de giro a la derecha a la derecha como ejemplo:
dot(endRightCenter + startOffset - startRightCenter - startOffset, startOffset) = 0
dot(endRightCenter - startRightCenter, startOffset) = 0
pathAngle = atan2(endRightCenter - startRightCenter)
El caso cruzado es más complicado: es el que aún no he resuelto todas las matemáticas. Publicaré la respuesta sin por ahora, en caso de que sea útil para usted mientras resuelvo los detalles restantes.
Editar: Destino dentro del radio de giro mínimo
Resulta que este método a menudo funciona fuera de la caja incluso cuando el destino está más cerca de nuestra distancia mínima de giro. Al menos una parte de uno de los círculos de reentrada termina fuera del radio de giro, lo que nos permite encontrar un camino viable siempre que no nos importe que se vuelva un poco como un pretzel ...
Si no nos gusta el camino que tomamos de esa manera (o si uno no es factible, no he revisado todos los casos exhaustivamente, tal vez hay uno imposible), siempre podemos conducir hacia adelante o hacia atrás hasta obtener un adecuado contacto de besos entre un círculo inicial y final, como se muestra arriba