Обновление: см. Рабочую демонстрацию!
Если я правильно понимаю, ваша ситуация (в плоскости, содержащей A, B и v ) такая, как показано на диаграмме ниже. Заданы точки A и B, а также вектор v и расстояние r . Вы хотите найти точку C.
Хорошо, пусть вектор w = (-v̂ y , v̂ x ) будет единичным вектором, перпендикулярным v . Тогда O = A + r w .
Теперь, | C - O | = r и (C - B) · (C - O) = 0 (где · - скалярное произведение). Объедините их, чтобы получить квадратное уравнение, которое вы можете решить, чтобы найти два возможных положения для C. Затем выберите одно с правильным знаком для (C - B) × (C - O).
(есть второй выбор для центра круга, O = A - r w , представляющий поворот по часовой стрелке вместо против часовой стрелки. Это дает вам еще одну возможность для C. I думаю, вам придется использовать эвристику, чтобы решить, какой из них вы предпочитаете: может, тот, который имеет наименьший OCAOC.)
St0rM просит помощи в выполнении этого в 3D (см. Комментарии). Это легко! Плоскость, содержащая A, B и v , имеет вектор нормали n = (A - B) × v . Пусть u = n × v будет вектором, перпендикулярным к n и v , и пусть w = û (единичный вектор в направлении u ).
Вам также нужно будет принять во внимание, что C лежит в той же плоскости, что и A: C · n = A. n и "правильный знак для (C - B) × (C - O) «становится» правильным знаком для (C - B) × (C - O) · n ».
Не можете решить эту систему уравнений?
Хорошо, если (C - B) · (C - O) = 0, то (C - O + O - B) · (C - O) = 0, поэтому (C - O) · (C - O ) + (O - B) · (C - O) = 0, поэтому C · (O - B) = O · (O - B) - r 2 .
Вы заметите, что это уравнение для плоскости, как и C · n = A. n . Пересечь эти две плоскости ( подробности см. В Википедии - вы можете использовать более простое решение, поскольку плоскости ортогональны и их легко можно сделать ортонормированными), чтобы получить уравнение прямой, на которой лежит C: C = H + λL, скажем, где L = n × (B - O). Затем используйте (C - O) · (C - O) = r 2 , чтобы превратить это в квадратное уравнение в λ. Вы обнаружите, что квадратное уравнение значительно упрощается, если переписать уравнение линии как C = H + λL + O, так что вхождения «- O» исчезают.
Вот реализация на Python, использующая numpy
для создания векторной алгебры. Я уверен, что вы можете выяснить, как перевести это на язык по вашему выбору.
import math
from numpy import cross, dot
from numpy.linalg import norm
def unit(v):
"""Return a unit vector in the same direction as v."""
return v / norm(v)
def turnpoints(A, B, v, r):
"""Generate possible turning instructions for a path from A to B
that starts out in direction v, turns through part of a circle of radius
r until it reaches a point C (to be determined), then heads straight for
B. Return each instruction in the form (sense, C) where sense is -1 for
clockwise and +1 for anticlockwise."""
n = unit(cross(A - B, v)) # Unit normal to plane containing A, B, v.
w = unit(cross(n, v)) # Unit normal to v lying in that plane.
for sense in (-1, +1): # Turn clockwise or anticlockwise?
O = A + sense * r * w # Centre of turning circle.
BB = B - O
m = unit(BB)
# C lies on the line H + l*L + O
H = dot(A, n) * n + (r**2 / norm(BB)) * m
L = cross(n, m)
# |C - O| = r**2 gives quadratic eqn in l with coefficients a=1, b=0, c.
c = dot(H, H) - r**2
disc = - 4 * c # Discriminant of quadratic eqn.
if disc < 0:
continue # No tangents (B inside circle).
elif disc == 0: # One tangent (B on circle).
C = H + O
yield (sense, C)
else: # Two tangents (B outside circle)
for sign in (-1, +1):
l = sign * math.sqrt(disc) / 2
C = H + l * L + O
# Only one choice for C is correct (the other involves
# reversing direction).
if dot(cross(C - B, C - O), n) * sense > 0:
yield (sense, C)