Getting a CFrame aligned with a triangle of CFrames

So lets say we have 3 CFrames, lets call them… A, B, and C. These represent a triangle in 3D space, B is the tip, where the triangle is actually pointing towards. Now, I’m not too “math savvy”, but I need a CFrame which is placed in the middle of these 3 points, and faces along the direction it’s pointing, in all directions (Yaw, Pitch, and Roll). Of course the CFrame.new(origin, point towards) implementation won’t work, due to multiple issues, and it not getting the result I’m looking for. Here’s an example:

The red line represents the CFrame, as you can see, it’s perfectly in-line with the triangle. This is the preferred result.

Multiple times, I’ve attempted to do implementations of this, none succeeding.

Can I get help with this? As stated previously, I’m not too “math savvy”.

2 Likes

A CFrame doesn’t really represent a triangle, so you have to decide what to do when the triangle isn’t perfect.

disambiguation

Which of these solutions seems more correct?

1 Like

Is this close to what you’re describing (the “x” indicates the position of the CFrame)

I’m having trouble understanding which middle you’re talking about. There are several possible ways of defining which is the center of the triangle. I’m just going off your illustration.

like between the back edge and point b.

actually, the median of all 3 is a more technically accurate way to put it

The mean, rather. The line segment from the midpoint of AC to B will pass through this point, it’s the Centroid on that page of triangle centers, so you could use that point. But why average 3 points when you only need to average 2. The vector you want for your CFrame LookVector is simply: B-(A+C)/2. The surface normal of this triangle can be gotten from the cross product of any of the sides with each other, or with this new look vector, since they’re all in a plane. This normal could be used for the UpVector of your CFrame, or the -UpVector, or +/-RightVector, or none of these depending on how you want that CFrame aligned with the triangle.

how would I apply the surface stuff to my cframe though

a, b, and c are all points in this code. You can get the position of a cframe value using .p property incase you aren’t familiar.

local averagePoint = (a+b+c)*(1/3)
local newCFrame = CFrame.new(averagePoint, b)

There is a CFrame constructor that takes all 12 entries of the CFrame. You could use this to construct the CFrame from the LookVector, the normal, and the cross product of these two. Check out the robloxdev CFrame page, it shows you which arguments to the constructor are which. EgoMoose’s CFrame tutorial page has a lot more info. Which vector’s values you pass as the look, right, and upvector values depend on how you need that CFrame oriented relative to the triangle to solve your problem.

that way of getting cframe is as I said, undesirable. Also I know .p exists.

Can I have links?

Example, assumes a, b, c are all Vector3 that could have come from CFrame.p values. Example not tested, I’m just going to type this from memory:

local midpoint = 0.5*(a+c)
local look = (b - midpoint).Unit
local up = (c-a):Cross(b-a).Unit
local right = look:Cross(up)
local cf = CFrame.new(midpoint.X, midpoint.Y, midpoint.Z, right.X, up.X, -look.X, right.Y, up.Y, -look.Y, right.Z, up.Z, -look.Z)

In this particular case, the CFrame will be positioned halfway between A and C, pointing with the look vector at B, and the up vector will be normal to the triangle in the way that is naturally “up” in your first illustration. Roblox look vector is in the -Z direction, that’s why all the look entries are negated. I could just have easily computed -look directly as (mid-b).Unit. If you want the CFrame positioned at the centroid, then just use midpoint = (a+b+c)/3.

Article that is relevant: http://robloxdev.com/articles/CFrame-Math-Operations

Lastly, might be worth double-checking that the centroid is the triangle center definition that is correct for your use case. The incenter is also common, and what a lot of people will solve for if you just ask them to find the center of a triangle. The difference is that the look vector of your CFrame would bisect the angle at point B rather than the side AC.

2 Likes

What are the undesirable effects?

Give this a try. Again, using a, b, and c and positions.

local rollCFrame = CFrame.new((a+c)*.5, c)
local localCFrame = CFrame.new(Vector3.new(0,0,0), rollCFrame:pointToObjectSpace(b))
local finalCFrame = rollCFrame * localCFrame

If you do something like CFrame.new((a+c)/2, b), you get a valid CFrame that points in the direction you’ve specified, but it will be one with a RightVector in the world XZ plane most of the time. This is often what you want if you have either a camera or character you want to point a specific way while staying “upright” where up is the +Y world direction. Where things get a bit dodgy is if you have a triangle that is very skinny, and the direction vector ends up pointing almost but not quite in the +Y or -Y world direction. Then you’ll get something very difficult to predict. For example, if the points A, B, C are (0,0,0), (0,8,0), and (-1,16,-1), what would you get? Two solutions exist of course with a right vector in the XZ plane, and one of these has an up vector with a positive Y coordinate. You should get that solution, but alas… that constructor gives you something a lot less consistent and a lot less useful (try it). This numerical instability is the source of a lot of pain and bugs.

This just doubles the problem by using that constructor twice. For certain special cases you’ll get a “world upright” CFrame, and for others a CFrame that’s oriented to the triangle, but these are just coincidences, or more accurately points where the results of this code intersect with one of the useful solutions. For just any arbitrary triangle in 3D space the result of this will not be either of these useful orientations.

3 Likes