Plotting a pivoted cosine line with parts and CFrames

I’m drawing a 3D cosine line given two points in space using parts. I created a transformation of the cosine function to travel between the two extrema on the interval [x_0, x_1], along with its derivative. It returns the worldspace height over an interval

Functions (On Desmos):

Currently the script will plot the cosine function without regards to the Z axis except for the XZ distance intended for pivoting, so no rotation or pivot from the starting point. The script creates a worldspace CFrame fom the function output and then composes that with an angle CFrame with the Z axis rotated over the atan of the derivative output.

2D Result:
image

I haven’t been able to figure out how to rotate or pivot these points with only CFrames and the worldspace positions. I tried welding all of the parts together but that was leading me to half-circles and other strange phenomena

Here’s my script:

function script.DrawCosineLine.OnInvoke(p0: Vector3, p1: Vector3, step: number)
	local xzDist = math.sqrt((p1.X - p0.X)^2 + (p1.Z - p0.Z)^2)
	
	-- (y_1 - y_0) * (1 - cos(pi * (x - x_0) / d_xz)) / 2 + y_0
	local pathHeightFn = function(x) return (p1.Y - p0.Y) * (1 - math.cos(math.pi * (x - p0.X) / xzDist)) / 2 + p0.Y end
	-- y' of above
	-- pi * (y_1 - y_0) * sin(pi * (x - x_0) / d_xz) / (2 * d_xz)
	local pathSlopeFn = function(x) return math.pi * (p1.Y - p0.Y) * math.sin(math.pi * (x - p0.X) / xzDist) / (2 * xzDist) end
	
	for i = 0, xzDist, step do
		local part = Instance.new("Part")
		part.Name = "Cosine Step " .. i
		part.Anchored = true
		part.CanCollide = false
		-- The darker a point is, the further it is from p_0
		part.Color = Color3.fromRGB(171, 203, 255):Lerp(Color3.fromRGB(0, 98, 255), i / xzDist)
		part.Size = Vector3.new(step, 0.5, 0.5)
		part.Parent = workspace

		local fnInfo2D = {}
		fnInfo2D.X = p0.X + i -- p_0 with step applied to get height at step. Z axis is accounted for with xzDist as step stopping point
		fnInfo2D.Y = pathHeightFn(fnInfo2D.X)
		fnInfo2D.M = pathSlopeFn(fnInfo2D.X)
		print("X: " .. fnInfo2D.X .. ", Y: " .. fnInfo2D.Y .. ", M: " .. fnInfo2D.M)
		
		-- Set the CFrame of the part to be placed along the X and Y of pathFn with Y angle of pathFnDdx
		part.CFrame = CFrame.new(fnInfo2D.X, fnInfo2D.Y, 0) * CFrame.Angles(0, 0, math.atan(fnInfo2D.M))
	end
end



script.DrawCosineLine:Invoke(Vector3.new(-30, 5, 0), Vector3.new(30, 25, 0), 1.5)

Thanks

1 Like

I’m not sure what you’re trying to ask so I’m assuming you want to create a cosine wave in 3D space given two points.

If that’s the case, the solution is fairly simple. You get the CFrame of the part at x in world space, then transform that position such that as the direction of p0p1 is the origin:

local p0 = --> vector3
local p1 = --> vector3
local origin = CFrame.lookAt(p0, p1)

local partCFrame = --> the cframe of the part at a specific point in the cosine wave

Part.CFrame = origin * partCFrame

Example implementation:

local p0 = --> vector3
local p1 = --> vector3
local origin = CFrame.lookAt(p0, p1)
local magnitude = (p1 - p0).Magnitude
local points = 10
local amplitude = 10

for i = 0, points do
    local percent = i / points
    local Part = Instance.new("Part")
    Part.CFrame = origin * CFrame.new(0, math.cos(percent * 2 * math.pi) * amplitude, -percent * magnitude)
    Part.Parent  = workspace
end

Not exactly. I already have the cosine line being plotted as I want it to on the X and Y axes. My issue is with rotating these parts around the origin such that they keep the line’s shape and their positions be relatively unchanged with the other parts of the cosine. I can’t use CFrame’s lookAt because my cosine transformations connect two points with no regards for orientation. See the Desmos link to see what I mean

Sorry, I’m still a little confused on the problem :sweat_smile:.

If you mean you want the cosine line to only be affected by the x and y axis, you can always ignore the Z-axis by making a new cframe or vector from scratch:

local p0 = --> some vector
local p1 = --> some vector

local origin = CFrame.lookAt(
    p0,
    Vector3.new(p1.X, p1.Y, p0.Z) --> setting the Z value of the v3 to be equivalent to p0
)

I hate this

I hate trigonmentry especailly integration.I hate that.your one looks like cosine graph and also i barely understand anything because a lot of math is involved.Only Professional programmers can help u.

In short I just want to orbit the parts on a single plane around the origin point where they will face the target point. In the video I attached, the cosine line was just straight. I haven’t been able to get my CFrames to do this

Is it possible to present a visual representation of your goal? For example, showing images or a video of the desired behavior for different part locations. I probably wasn’t able to extract the correct goal from the video provided. What I’m currently understanding from what was provided is that a cosine curve should be drawn between two parts, ignoring the z-axis.

if I understand correctly, you want the plot to work in 3-D, here’s the script:

local function Invoke(p0: Vector3, p1: Vector3, step: number)
	
	--local aa = Instance.new("Part", workspace)
	--aa.Position = p0
	--aa.Anchored = true
	--local bb = Instance.new("Part", workspace)
	--bb.Position = p1
	--bb.Anchored = true
	
	
	local xzDist = math.sqrt((p1.X - p0.X)^2 + (p1.Z - p0.Z)^2)

	-- (y_1 - y_0) * (1 - cos(pi * (x - x_0) / d_xz)) / 2 + y_0
	local pathHeightFn = function(x) return (p1.Y - p0.Y) * (1 - math.cos(math.pi * (x - p0.X) / xzDist)) / 2 + p0.Y end
	-- y' of above
	-- pi * (y_1 - y_0) * sin(pi * (x - x_0) / d_xz) / (2 * d_xz)
	local pathSlopeFn = function(x) return math.pi * (p1.Y - p0.Y) * math.sin(math.pi * (x - p0.X) / xzDist) / (2 * xzDist) end

	for i = 0, xzDist, step do
		
		local part = Instance.new("Part")
		part.Name = "Cosine Step " .. i
		part.Anchored = true
		part.CanCollide = false
		-- The darker a point is, the further it is from p_0
		part.Color = Color3.fromRGB(171, 203, 255):Lerp(Color3.fromRGB(0, 98, 255), i / xzDist)
		part.Size = Vector3.new(step, 0.5, 0.5)
		part.Parent = workspace

		local fnInfo2D = {}
		fnInfo2D.X = p0.X + i -- p_0 with step applied to get height at step. Z axis is accounted for with xzDist as step stopping point
		fnInfo2D.Y = pathHeightFn(fnInfo2D.X)
		fnInfo2D.M = pathSlopeFn(fnInfo2D.X)
	--	print("X: " .. fnInfo2D.X .. ", Y: " .. fnInfo2D.Y .. ", M: " .. fnInfo2D.M)
		
		
		local AngleMovement = CFrame.lookAt(p0, p1).RightVector:Angle(Vector3.zAxis, Vector3.yAxis)
		
		local x1 = (p0.X * math.cos(AngleMovement)) - (p0.Z * math.sin(AngleMovement))--this fomrula rotate the direction by the turnagle
		local z1 = (p0.X * math.sin(AngleMovement)) + (p0.Z * math.cos(AngleMovement))

		
		-- Set the CFrame of the part to be placed along the X and Y of pathFn with Y angle of pathFnDdx
		--part.CFrame = CFrame.new(fnInfo2D.X, fnInfo2D.Y, 0) * CFrame.Angles(0, 0, math.atan(fnInfo2D.M))
		part.CFrame = CFrame.new(p0.X, fnInfo2D.Y,p0.Z) * CFrame.Angles(0, -AngleMovement,0 ) * CFrame.new(i,0,0) * CFrame.Angles(0,0,math.atan(fnInfo2D.M))
	end
	
end


while wait(1) do
	local Mouse = game:GetService("Players").LocalPlayer:GetMouse().Hit.Position
	Invoke(Vector3.new(30, 25, 0), Mouse, 1.5)
end
--Invoke(Vector3.new(-30, 5, 30), Vector3.new(30, 25, 0), 1.5)

Basically I just rotated each part around p0 using a simple formula and adjusted correctly, hope this helps! :grin:

1 Like

This was it. I started playing around with the concepts of what you added. I still can’t really wrap my head around what you did to get the angle movement, but I understand what it is and how it works on the next lines

Thank you

I want to point out that these lines:

local x1 = (p0.X * math.cos(AngleMovement)) - (p0.Z * math.sin(AngleMovement))--this fomrula rotate the direction by the turnagle
local z1 = (p0.X * math.sin(AngleMovement)) + (p0.Z * math.cos(AngleMovement))

are completly useless, it was just tired and forgot to take them out lol.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.