Nie opracowałem do tej pory pełnych równań, ale oto kilka elementów wizualnych, które pomogą nam rozwiązać problem. Sprowadza się do pewnej geometrii:
( Ikony samochodów przez Kenney )
Z dowolnego punktu początkowego i orientacji możemy narysować dwa okręgi z naszym minimalnym promieniem skrętu - jeden po lewej, drugi po prawej. Opisują one punkty na jak najściślejszym początku naszej ścieżki.
Możemy zrobić to samo dla dowolnej żądanej pozycji końcowej i orientacji. Kręgi te opisują najściślejszy możliwy koniec naszej ścieżki.
Teraz problem sprowadza się do znalezienia ścieżki, która łączy jedno z początkowych kół z jednym z końcowych kół, całując je wzdłuż stycznej.
(Zakłada się, że nie musimy szukać ścieżki pomiędzy przeszkodami pomiędzy nimi, o czym nie wspomniano w pytaniu. Odpowiedź Stormwind dotyczy sposobu wykorzystania informacji z wykresu nawigacyjnego dla tego rodzaju problemów. Po uzyskaniu sekwencji węzłów aby przejść, możemy zastosować poniższą metodę do każdego segmentu planu).
Jeśli dla uproszczenia używamy linii prostych, otrzymujemy coś takiego:
To nas ogranicza. Po znalezieniu ścieżki tą metodą możesz sztucznie napompować jedno lub oba koła początkowe i końcowe, aby uzyskać mniej bezpośrednią, ale gładszą ścieżkę, aż do momentu, w którym oba kręgi się pocałują.
Obliczanie tych ścieżek
Przeanalizujmy przypadki dla jednego kierunku skrętu - powiedzmy, że zaczynamy naszą drogę od skrętu w prawo.
Środek naszego prawego promienia skrętu to:
startRightCenter = carStart.position + carStart.right * minRadius
Nazwijmy kąt prostego odcinka naszej ścieżki (mierzony od dodatniej osi x) pathAngle
Jeśli narysujemy wektor od rightCenter
punktu, w którym opuszczamy okrąg zawracający (w tym momencie musimy być skierowani w stronę pathAngle), to ten wektor jest ...
startOffset = minRadius * (-cos(pathAngle), sin(pathAngle))
Oznacza to, że punktem wyjścia z kręgu musi być ...
departure = startRightCenter + startOffset
Punkt, w którym ponownie wkraczamy w okrąg skrętu, zależy od tego, czy zamierzamy zakończyć zakręt w lewo, czy w prawo:
// To end with a right turn:
reentry = endRightCenter + startOffset
// To end with a left turn: (crossover)
reentry = endLeftCenter - startOffset
Teraz, jeśli dobrze wykonaliśmy naszą pracę, linia łącząca departure
z reentry
powinna być prostopadła do startOffset
:
dot(reentry - departure, startOffset) = 0
A rozwiązanie tego równania da nam kąt (kąty), pod jakim to jest prawdziwe. (Używam liczby mnogiej, ponieważ technicznie istnieją dwa takie kąty, ale jeden z nich obejmuje jazdę do tyłu, co zwykle nie jest tym, czego chcemy)
Jako przykład zamieńmy skręt w prawo na literę skrętu w prawo:
dot(endRightCenter + startOffset - startRightCenter - startOffset, startOffset) = 0
dot(endRightCenter - startRightCenter, startOffset) = 0
pathAngle = atan2(endRightCenter - startRightCenter)
Przypadek crossovera jest bardziej skomplikowany - to ten, na który jeszcze nie wypracowałem całej matematyki. Na razie opublikuję odpowiedź bez odpowiedzi, na wypadek, gdyby była dla Ciebie przydatna podczas opracowywania pozostałych szczegółów.
Edycja: Miejsce docelowe w minimalnym promieniu skrętu
Okazuje się, że ta metoda często działa natychmiast po wyjęciu z pudełka, nawet gdy miejsce docelowe jest bliżej niż nasza minimalna odległość skrętu. Przynajmniej część jednego z kręgów powrotnych kończy się poza promieniem skrętu, co pozwala nam znaleźć realną ścieżkę, o ile nie przeszkadza nam, że robi się trochę w stylu precla ...
Jeśli nie podoba nam się ścieżka, którą dostajemy w ten sposób (lub jeśli nie jest to wykonalne - nie sprawdziłem dokładnie wszystkich przypadków - być może są niemożliwe), zawsze możemy jechać prosto do przodu lub do tyłu, dopóki nie znajdziemy odpowiedniego całowanie kontaktu między kołem początkowym i końcowym, jak pokazano na schemacie powyżej.