# Finding the angle of a wedgepart

Hi,

Does anyone have any idea on how to find the angle in a wedge part? More details below:

So what I am trying to do is find the angle of this green section here on a wedge:

Eventually the end goal is to be able to angle a part via script on a wedge just like this:

My first thought, was to try to use Pythagorean theorem:

``````function pythThem(part)
local a = part.Size.Y
local b = part.Size.Z

return math.sqrt((a^2)+(b^2))
end

local c = pythThem(game.Workspace.Wedge)
``````

This got me the size of the green part, but did not get me the angle like I wanted.

Does anyone have any idea how to do this?

3 Likes

For this you would have to use SOHCATOA

http://mathworld.wolfram.com/SOHCAHTOA.html

1 Like

``````local opposite = part.Size.y;
``````

But how are you determining where to put the part? You may have an easier time using linear algebra.

4 Likes

How would I use linear algebra here?

My original idea was to use raycasting to get the point, then implement the rotation.

I recommend raycasting at the spot on the wedge that youâ€™d like to attach the part to
This will give you the surface normal, which is important for cframing the part correctly

Example:

``````local Wedge = workspace.Wedge
local p2 = Wedge.Position + Wedge.CFrame.LookVector * Wedge.Size.Z/2
local ray = Ray.new(p2, (Wedge.Position - p2).unit * Wedge.Size.Z)

local part, hit, normal = workspace:FindPartOnRay(ray)

if part == Wedge then
local brick = Instance.new("Part")
brick.Anchored = true
brick.CFrame = CFrame.new(hit + normal * brick.Size.Z/2, hit + normal * 10000)
brick.Parent = workspace
end
``````

Just tried this in a baseplate and it worked

Can you get the angle with FindPartOnRay?

Like hereâ€™s another example:

Can I make the part match the rotation of the wedgepart when I move the part?

Code I wrote to demo

``````local part2 = Instance.new('Part', workspace)
part2.Size = Vector3.new(1,1,1)
part2.BrickColor = BrickColor.Red()
part2.Transparency = 0.5
part2.Anchored = true
part2.CanCollide = false

local ignoreList = {}

table.insert(ignoreList, part2)

while wait() do

local ray = Ray.new(workspace.Part.Position, Vector3.new(0, -100, 0))

local part, hit, normal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

part2.Position = hit + Vector3.new(0, 0, 0)
end``````
1 Like

The code I sent earlier matches the part to the wedge so that it fits (rotates)- you wonâ€™t need the angle

but
if you want the angle:

``````function angle(v1, v2)
return math.acos(v1.unit:Dot(v2.unit))
end

print(math.deg(angle(normal, Wedge.CFrame.LookVector)))
``````

You can achieve this by acquiring the surface normal of the green face which youâ€™ve highlighted.
You can acquire this by firing a ray in the direction testDistance*Vector3.new(0, -1, 0).

Alternatively, if you know what wedge is being dealt withâ€¦ You can acquire the surface normal by doing, (linear algebra soln)

``````local Zlen, Ylen = wedge.Size.Z, wedge.Size.Y
local rv, bv = wedge.CFrame.RightVector, -(Zlen*wedge.CFrame.LookVector - Ylen*wedge.CFrame.UpVector).Unit
local surfaceNormal = bv:Cross(rv)
``````

From there, it looks like you want to position the part on the green face with the parts upvector / topFace in the same direction as the surface normal of the green face.

Get the x, z position of where you want the part to go and use them to find the y position.
And from there, all should be calculable.

``````local ySize = 4 --Size.y of part being placed on wedge
local x, z = 1, 2 --Position.x & Position.z of part being placed on wedge
local posn = wedge.CFrame:PointToWorldSpace(Vector3.new(0, 1, 1)*wedge.Size/2) + surfaceNormal*ySize/2
local x0, y0, z0 = posn.X, posn.Y, posn.Z
local y = -(surfaceNormal.x*(x - x0) + surfaceNormal.z*(z - z0))/surfaceNormal.y + y0
local newPosition = Vector3.new(x, y, z)

function PointLocalVectorAt(objectSpaceVector, originPosition, lookAtPosition)
return CFrame.new(originPosition, lookAtPosition)*CFrame.new(Vector3.new(), objectSpaceVector):inverse()
end

part.CFrame = PointLocalVectorAt(Vector3.new(0, 1, 0), newPosition, newPosition + surfaceNormal)
``````
1 Like

Only thing is that the code you sent earlier is using the Wedge to make the ray, for what I am actually going to be using this for I need to use the part for the ray though.

Eventually, the game will have over 50 wedges and I want all of the orientations to be able to line up.

The final piece of the puzzle is assembling the CFrame using look vectors. The `normal` is one of the lookVector. A sensible second lookVector is the x axis of the part youâ€™re hitting.

I modified your code to get this result:

Code
``````local part2 = Instance.new('Part', workspace)
part2.Name = "Indicator";
part2.Size = Vector3.new(1,1,1)
part2.BrickColor = BrickColor.Red()
part2.Transparency = 0.5
part2.Anchored = true
part2.CanCollide = false

local ignoreList = {}

table.insert(ignoreList, part2)

while wait() do
local ray = Ray.new(workspace.Part.Position, Vector3.new(0, -100, 0))
local part, hit, normal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

--Here, we use the `normal` variable to indicate the y look vector for our indicator part.
--The second lookVector we can assign to the x axis of the part we are scanning
--(note: a part where the x axis points straight up will break this).
local yLook = normal;
local xLook = part.CFrame.rightVector;
--Derive the z lookVector from the first two lookVectors.
local zLook = xLook:Cross(yLook);
part2.CFrame = CFrame.fromMatrix(hit, xLook, yLook, zLook);
end
``````
2 Likes

Careful, part.CFrame.RightVector isnâ€™t always guaranteed to be perpendicular to the normal vector. (edit: Was thinking of a corner wedge as one if itâ€™s faces isnâ€™t perpendicular to itâ€™s rightface, sorry). (Though, I think Roblox auto-fixes the CFrame if the local axes donâ€™t have orthogonality anyway.)

Also, it might be better if the part lies flat on the surface with the same x, z position. That code puts half of the part ontop, and half below the surface. If you wish to have the part lie â€śflatâ€ť on any surface at any exact x, z position, I would use a slightly modified version of my code above along with your code.

``````local part2 = Instance.new('Part', workspace)
part2.Name = "Indicator";
part2.Size = Vector3.new(1,1,1)
part2.BrickColor = BrickColor.Red()
part2.Transparency = 0.5
part2.Anchored = true
part2.CanCollide = false

local ignoreList = {}

table.insert(ignoreList, part2)

function PointLocalVectorAt(objectSpaceVector, originPosition, lookAtPosition)
return CFrame.new(originPosition, lookAtPosition)*CFrame.new(Vector3.new(), objectSpaceVector):inverse()
end

local testDistance = 100

while wait() do
local ray = Ray.new(workspace.Part.Position, Vector3.new(0, -1, 0)*testDistance)
local part, hit, surfaceNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

local ySize = part2.Size.y
local x, z = workspace.Part.Position.x, workspace.Part.Position.z --Position.x & Position.z of part being placed on wedge
local posn = hit + surfaceNormal*ySize/2
local x0, y0, z0 = posn.X, posn.Y, posn.Z
local y = -(surfaceNormal.x*(x - x0) + surfaceNormal.z*(z - z0))/surfaceNormal.y + y0
local newPosition = Vector3.new(x, y, z)

part2.CFrame = PointLocalVectorAt(Vector3.new(0, 1, 0), newPosition, newPosition + surfaceNormal)
end

``````

This should work with terrain too I believe, as well as any BasePart.

6 Likes

Yeah, I opted to remove all the corner cases in favor of having a solution that could be easily understood by OP. Plus, in case heâ€™s only interested in solving this for the slanted portion of a wedge part, the solution is sufficient.

I only left a very subtle warning that my solution doesnâ€™t capture every edge case.

`--(note: a part where the x axis points straight up will break this).`

1 Like

Thank you so much everyone!

1 Like