Мне кажется, что эта бумага, на которую вы ссылались, немного напоминает эту операцию.
Ваша проблема в том, что projectVectorOntoPlane
на самом деле не проецирует вектор на нужную вам плоскость.Он проецирует вектор на другую плоскость, параллельную нужной плоскости, но проходящую через начало координат.(Затем вы пытаетесь решить эту проблему с помощью Q[2] = C[2]
, но это только усугубляет ситуацию.)
Плоскость можно определить с помощью нормального вектора вместе с в некоторой точке наплоскость, так что вы могли бы написать функцию projectVectorOntoPlane
следующим образом:
-- Project P onto the plane with normal n containing the point O.
function projectVectorOntoPlane(P, n, O)
return vec3.sub(P, vec3.scale(n, vec3.dot(vec3.sub(P, O), n)))
end
Однако для этой задачи проще всего пройти весь путь в системе координат, основанной на центре круга, поэтомуЯ предлагаю что-то вроде этого:
-- Return a point on the circle with center C, unit normal n and radius r
-- that's closest to the point P. (If all points are closest, return any.)
function pointCircleClosest(P, C, n, r)
-- Translate problem to C-centred coordinates.
local P = vec3.sub(P, C)
-- Project P onto the plane containing the circle.
local Q = vec3.sub(P, vec3.scale(n, vec3.dot(n, P)))
-- If Q is at the centre, all points on the circle are equally close.
if vec3.equal(Q, {0,0,0}) then
Q = perpendicular(n)
end
-- Now the nearest point lies on the line through the origin and Q.
local R = vec3.sub(P, vec3.scale(Q, r / vec3.mag(Q)))
-- Return to original coordinate system.
return vec3.add(R, C)
end
-- Return an arbitrary vector that's perpendicular to n.
function perpendicular(n)
if math.abs(n[1]) < math.abs(n[2]) then
return vec3.cross(n, {1,0,0})
else
return vec3.cross(n, {0,1,0})
end
end
О, и вам может быть удобнее использовать класс vec3
, , возможно, этот , чтобы вы могли написать P - C
вместо суеты vec3.sub(P, C)
и т. д.