Get any point Y value between 4 Points

There is now a reward for this on Roblox’s Talent Hub. If you solve, please respond there if you are looking for payment.

There are 4 points in a square plane, but the y values are different. So basically we have a deformed square. Given the following code, we can get any n number of midpoints inside the surface of the plane, except that it seems one corner seems to be off. It doesn’t seem to have the same curve of a deformed mesh.

function interpolate(a, b, alpha)
	return a + (b - a) * alpha
end

function bilinearInterpolate(v1, v2, v3, v4, a1, a2)
	local top = interpolate(v1, v2, a1)
	local bottom = interpolate(v3, v4, a2)
	local middle = interpolate(top, bottom, a2)

	return middle
end

local function getPointOnPlane(corner1, corner2, corner3, corner4, alphaX, alphaZ)
	local x = bilinearInterpolate(corner1.X, corner2.X, corner3.X, corner4.X, alphaX, 0)
	local y = bilinearInterpolate(corner1.Y, corner2.Y, corner3.Y, corner4.Y, alphaX, alphaZ)
	local z = bilinearInterpolate(corner1.Z, corner2.Z, corner3.Z, corner4.Z, 0, alphaZ)

	return Vector3.new(x, y, z)
end

How could we fix it so the points work correctly. It seems p3 doesn’t get included, but it does for y spacing a little, as seen below.

Show

Heres another gif if it helps https://gyazo.com/49b7012f3314b66be862c060387a3e01

1 Like

you are doing the whole thing wrong. you are passing in the value you want to interpolate by as if it were the value to interpolate by, the y value.

you are doing the math as if you were trying to convert it back to cartesian coordinates, but that +a operation isnt suppose to be in your code as you are still in displacement vector space :roll_eyes:

2 Likes

Using linear interpolation to try and build a surface in R3 is alot more work than you need to do. Instead you can implement a bezier surface equation as follows:

First you need to have a function to calculate the bernstein polynomial, which can be done as follows:

--[[
	Gets the factorial of n
	
	@param {integer} n: domain[0, 170], nth factorial to get
]]
local factorial_lookup = {}

local function factorial(n)
	local f = factorial_lookup[n] -- increase speed of factorial method
	if f then return f end
	f = n > 1 and n*factorial(n-1) or 1
	factorial_lookup[n] = f
	return f
end

factorial(170) -- pre-allocate factorials up to 170, 170 is the IEEE-754 limit, anything after that is INF

--[[
	Calculates the bernstein polynomial, only applicable for a 4-sided polygon.
	If you want a larger polynomial, replace 1/... with factorial(m)/...
	
	@see https://en.wikipedia.org/wiki/Bernstein_polynomial
]]
local function bernstein(m, i, u)
	return 1/(factorial(i)*factorial(m-i)) * u^i * (1-u)^(m-i)
end

Next you should create a higher-order function to “give” you the equation you need for any given 4 points:

--[[
	Returns the equation of a bezier surface in terms of u and v, which are degrees similar to an interpolation alpha
	
	@param {Vector3} a: point 1
	@param {Vector3} b: point 2
	@param {Vector3} c: point 3
	@param {Vector3} d: point 4
	
	@returns {function}: bezier surface equation for the surface formed by abcd
]]
local function bezierSurfaceEquation(a, b, c, d)
	--[[
		Calculates the position in R3 of a point on the bezier surface created by abcd
		
		@param {double} u: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
		@param {double} v: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation

        @returns {Vector3}: point on the plane based on supplied degrees of iteration
	]]
	return function(u, v)
		return 	bernstein(1,0,u)*bernstein(1,0,v)*a
				+ bernstein(1,0,u)*bernstein(1,1,v)*b
				+ bernstein(1,1,u)*bernstein(1,0,v)*c
				+ bernstein(1,1,u)*bernstein(1,1,v)*d
	end
end

You can also, optionally, defer the use of a higher-order function and use a similar method to your existing getPointOnPlane() function as follows:

--[[
	Calculates the position in R3 of a point on the bezier surface created by abcd
	
	@param {Vector3} a: point 1
	@param {Vector3} b: point 2
	@param {Vector3} c: point 3
	@param {Vector3} d: point 4
	@param {double} u: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
	@param {double} v: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
	
	@returns {Vector3}: point on the plane based on supplied degrees of iteration
]]
local function getPointOnPlane(a, b, c, d, u, v)
	return 	bernstein(1,0,u)*bernstein(1,0,v)*a
		+ bernstein(1,0,u)*bernstein(1,1,v)*b
		+ bernstein(1,1,u)*bernstein(1,0,v)*c
		+ bernstein(1,1,u)*bernstein(1,1,v)*d
end

This will end up looking alot better than any linear interpolation you might do, and at the same time will let you build neat surfaces like:

I should note that I use some atypical language here, since I opted to use words you might be familiar with, as opposed to typically understood mathematical dialect, to explain some of the concepts here. Since there is so much to un-pack with this, and there are already great explanations online, I’ll link some below instead of trying to explain everything here myself from memory:

Explanations/Further reading:
Bernstein Polynomial (Wikipedia)
Bezier surface (Wikipedia)
Bezier surface (mtu.edu)
Bezier surface (math encyclopedia)
Bezier spline (math encyclopedia)

6 Likes