Need help understanding Bezier Curves

All good im also going to bed so ill see your tomorrow, i cant express how thankful i am for your help.

function lerp(p0, p1, t)
    return p0 + t * (p1 - p0)
end

local points = {}
points[1] = {
    ["Position"] = Vector3.new(), -- starting point
    ["Distance"] = 0
}

for t = 0.01, 1, 0.01 do
    local position = bezierPoint(t, ...) -- get unweighted position
    local previous = points[#points] 
    local gap = (position - previous.Position).Magnitude
    local distance = previous.Distance + gap

    -- store the vector3 as position, total distance as distance
    -- and distance from the previous point as gap
    points[#points+1] = {
        ["Position"] = position,
        ["Distance"] = distance,
        ["Gap"] = gap
    }
end

-- total curve length
local length = points[#points].Distance
local bezier = {}

for t = 0.01, 1, 0.01 do
    local target = length * t

    -- find the first point that is >= target and the one before it
    -- that means the target length is in between these two points
    local p0, p1
    for i, point in pairs(points) do
        if point.Distance >= target then
            p0 = points[i-1]
            p1 = point
            break
        end
    end

    bezier[#bezier+1] = lerp(
        p0.Position,
        p1.Position,
        (target - p0.Distance) / p1.Gap
    )
end

return bezier

note, I stored p1.Distance - p0.Distance as "Gap" so it doesn’t have to calculate twice. I also didn’t use binary search to keep it simple. haven’t tested, ask me about any problems.

1 Like

Sorry for the delay, so how do I know when to use each bezier point? it just returns them all in 1 table right?

yeah. if you want, instead of storing them in a table you can put a part there or something.

also FYI, it doesn’t include the first point. did that on purpose so you can chain them, if you want to add the first point then change it to t=0 for the second loop.

i changed the t thing but it does this for some reason it does this: https://i.gyazo.com/aba6882287af761868706e4f84808a31.gif

what’s the issue? is it because it stops early before reaching the top right square?

it looks like the spacing works, at least.

That and it begins at 0,0,0 for some reason

at the first part when you do points[1]=… change Vector3.new() to the starting point.

edit: looks like an error on my part. you need to change points[#points] to points[1]!

I have it to the starting unweighted position already

	local Bezierpoints = {}
	for t = 0, 1, 0.01 do 
		local v3 = cubic(p0.Position, p1.Position, p2.Position, p3.Position, t)
		Bezierpoints[t] = v3
	end

for t = 0, 1, 0.01 do
	local position = Bezierpoints[t] -- get unweighted position
	local previous = points[#points] 
	local gap = (position - previous.Position).Magnitude
	local distance = previous.Distance + gap

	-- store the vector3 as position, total distance as distance
	-- and distance from the previous point as gap
	points[#points+1] = {
		["Position"] = position,
		["Distance"] = distance,
		["Gap"] = gap
	}
end

Not full code btw

alright… what happens when you print out the bezier before adjusting it. does it start at (0,0,0)?

it might be a problem with something else because I can’t think a place the stuff I wrote ever refers to (0,0,0). I can test it tomorrow.

for the reason it ends early, maybe t doesn’t reach 1. I don’t remember if the for loops include the last number in lua, maybe it only gets to 0.99.

I see now, t at the very beginning is 0 and doesn’t reach 1, thats what our problem is I just need to change the for loops.

can’t say I understand but ignorance is bliss! let’s hope it works :slight_smile:

It didnt work, im not sure why. I changed all the for loops from

for t = 0, 1, 0.01 do -- this means 0 will always be lower than or equal to t and t will always be lower than or equal to 1

to

for t = 0.01, 1, 0.01 do -- this means 0.01 will always be lower than or equal to t and t will always be lower than or equal to 1

this still has the same results thought and I have no idea why

no it prints the correct position

I found it, the starting point position was set to Vector.new() which is 0,0,0 i fixed it by switching it to p0.Position

Now I just need to find out why its not finishing

sounds familiar :slight_smile:

tell me, in your unweighted curve, is the final point at the right position? additionally, is there the same number of points in the unweighted curve as there is in the new one?

my bad

No its off by like 1 stud

idk but I dont think so, when i try to print #Bezierpoints (unweighted) it returns 0 and all of the time values in the unweighted points are weird. Example from output when i print unweighted points:
image

Anyways im gonna get off now as its getting late, we can fix this part in the morning

Also here is the script in case you wanna test something while im gone

function lerp(p0, p1, t)
	return (1 - t) * p0 + t * p1
end

function lerp2(p0, p1, t)
	return p0 + t * (p1 - p0)
end

local function quadratic(p0, p1, p2, t)
	local L1 = lerp(p0, p1, t)
	local L2 = lerp(p1, p2, t)
	return lerp(L1, L2, t)
end


local function cubic(p0, p1, p2, p3, t)
	local Q1 = quadratic(p0, p1, p2, t)
	local Q2 = quadratic(p1, p2, p3, t)
	return lerp(Q1, Q2, t)
end

local function GetBezier(p0, p1, p2, p3, cart)
local points = {}
points[#points] = {
	["Position"] = p0.Position, -- starting point
	["Distance"] = 0
}
	
	local Bezierpoints = {}
	for t = 0, 1, 0.01 do 
		local v3 = cubic(p0.Position, p1.Position, p2.Position, p3.Position, t)
		Bezierpoints[t] = v3
	end
	print(Bezierpoints)
	
for t = 0, 1, 0.01 do
	local position = Bezierpoints[t] -- get unweighted position
	local previous = points[#points] 
	local gap = (position - previous.Position).Magnitude
	local distance = previous.Distance + gap

	-- store the vector3 as position, total distance as distance
	-- and distance from the previous point as gap
	points[#points+1] = {
		["Position"] = position,
		["Distance"] = distance,
		["Gap"] = gap
	}
end

-- total curve length
local length = points[#points].Distance
local bezier = {}

for t = 0, 1, 0.01 do
	local target = length * t

	-- find the first point that is >= target and the one before it
	-- that means the target length is in between these two points
	local p0, p1
		for i, point in pairs(points) do
		if point.Distance >= target then
			p0 = points[i-1]
			p1 = point
			break
		end
	end

		bezier[#bezier+1] = lerp(
			p0.Position,
			p1.Position,
			(target - p0.Distance) / p1.Gap
		)
end

	return bezier
end

local bezier = GetBezier(workspace.p0, workspace.p1, workspace.p2, workspace.p3, workspace.cart)

print(bezier)

for i,v in pairs(bezier) do
	workspace.cart.Position = v
	local newpart = Instance.new("Part")
	newpart.Material = "Neon"
	newpart.BrickColor = BrickColor.White()
	newpart.Position = workspace.cart.Position
	newpart.Size = Vector3.new(0.25, 0.25, 0.25)
	newpart.Anchored = true
	newpart.CanCollide = false
	newpart.Parent = workspace
	wait(0.01)
end

your output looks mostly normal.

both of the lerp functions do the same thing… just choose one. I think the first one might be faster.

points[#points] at the beginning should be points[1]. that was a mistake I made.

after you do that, it must be a problem with your cubic function. which is weird because I’m guessing that comes from the wiki itself. make sure that your cubic function ends with the last point when t=1.

When I change it, the script breaks

It doesn’t, it ends with t = 0.9900000000000007