Finding a circle from two points and a tangent in 3D

I believe this is the direct method you were looking for, but it’s a lot harder than just projecting vectors. Here it is anyway. Consider this:

Center of circle given two pointsa and a tangent

  • Point A is the first point.
  • Point B is the second point
  • Vector ℓ is the tangent vector of the sphere at point A.
  • The red vector is the normal vector of the sphere at the tangent point.
  • The green point is the midpoint between A and B.
  • The green vector is the vector pointing from the tangent line to the midpoint of A and B and extending to the center of the circle.
  • The black point at the center is the center of the sphere.

In order to get the center of the sphere, you need to find the intersection between the red vector (normal vector) and the green vector (midpoint vector). In code, this looks like this:

local function GetCenter(p1, p2, t)
	local m = (p2 + p1) * 0.5; -- midpoint between p1 and p2
	local d = (p2 - p1).Magnitude -- distance between p1 and p2
	local s = (p2 - p1).Unit; -- the vector going from p1 to p2
	local v = t:Cross(s).Unit; -- a vector used to get the normal vector at the tangent point

	local n = t:Cross(v).Unit; -- normal vector at the tangent point

	local a = s:Cross(n).Unit; -- a vector orthogonal to the normal vector and midvector used to get the vector perpendicular to the mid vector
	local b = s:Cross(a).Unit; -- the vector perpendicular to the midvector

	local center = GetIntersection(p1, -n, m, b); -- the center of the sphere is at the intersection of these two vectors
	local radius = (center - p1).Magnitude; -- the radius of the sphere

	return center, radius;
end

If you’re interested in the intersection function I used, I’ll add it here as well.

local Huge = 10000000000000000000;

--[[ an intersection function i found here in the dev forum that i editted to give
a y coordinate, props to @PlaasBoer]]
local function GetIntersection(startPoint1, dir1, startPoint2, dir2)
	local endPoint1 = startPoint1 + dir1 * Huge;
	local endPoint2 = startPoint2 + dir2 * Huge;

	local point_1_x1 = startPoint1.X;
	local point_1_y1 = startPoint1.Z;
	local point_1_x2 = endPoint1.X;
	local point_1_y2 = endPoint1.Z;
	local point_2_x1 = startPoint2.X;
	local point_2_y1 = startPoint2.Z;
	local point_2_x2 = endPoint2.X;
	local point_2_y2 = endPoint2.Z;
	-- m = (y1 - y2) / (x1 - x2)
	local line_1_m = 0;
	local line_2_m = 0;
	-- b = -(mx1) + y1
	local line_1_b = 0;
	local line_2_b = 0;
	local intersect_x = 0;
	local intersect_z = 0;
	local isLineOneVertical = ((point_1_x1 / point_1_x2) % 2) == 1;
	local isLineTwoVertical = ((point_2_x1 / point_2_x2) % 2) == 1;
	if isLineOneVertical and isLineTwoVertical then
		error("There is no cross point, both vertical");
	end
	-- Line 1
	if isLineOneVertical then
		line_2_m = (point_2_y1 - point_2_y2) / (point_2_x1 - point_2_x2);
		line_2_b = -(line_2_m * point_2_x1) + point_2_y1;
		intersect_x = point_1_x1;
		intersect_z = (line_2_m * intersect_x) + line_2_b;
		-- Line 2
	elseif isLineTwoVertical then
		line_1_m = (point_1_y1 - point_1_y2) / (point_1_x1 - point_1_x2);
		line_1_b = -(line_1_m * point_1_x1) + point_1_y1;
		intersect_x = point_2_x1;
		intersect_z = (line_1_m * intersect_x) + line_1_b;
	else
		line_1_m = (point_1_y1 - point_1_y2) / (point_1_x1 - point_1_x2);
		line_2_m = (point_2_y1 - point_2_y2) / (point_2_x1 - point_2_x2);

		if line_1_m == line_2_m then
			error("There is no cross point, both same slope")
		end
		line_1_b = -(line_1_m * point_1_x1) + point_1_y1;
		line_2_b = -(line_2_m * point_2_x1) + point_2_y1;
		intersect_x = (line_2_b - line_1_b) / (line_1_m - line_2_m);
		intersect_z = (line_1_m * intersect_x) + line_1_b;

	end

	local u = (intersect_x - point_1_x1) / dir1.X
	return Vector3.new(intersect_x, startPoint1.Y + dir1.Y * u, intersect_z);
end

Testing this in game results in a very nice looking sphere manipulation show. I added the multiple vectors that are created by the code.

Watch the sphere manipulation show here

I know this thread is old, but I couldn’t help but offer this solution!

2 Likes