Rotational CFraming

I made a post about this issue earlier, and everyone seems to have missed the point. I think maybe I just didn’t explain it well. I have a part, I know where I want the part’s corners to be, how do I CFrame the part to get it to match with the desired corners? Like this:

Step 1: Rectangle Part with Corner Points

Step 2: Define Where You Want the Corners

Step 3: ???
???

Step 4: Result

What operation do I perform in step 3, to efficiently cframe the part to the desired corner targets?

will the size of the part always be the same?

You could group all the points and then use :GetBoundingBox(). This will give you a CFrame of the center of the model. This will not allow for any extra parts inside the model you are trying to get the center from.

Example Code

local CenterPart = script.Parent
local CenterCFrame = script.Parent.Points:GetBoundingBox()
CenterPart.CFrame = CenterCFrame

Example Result (In while loop)

Yes the size is always the same

Hmmm well the issue is the corners aren’t actual physical parts. I made them parts to help visualize the problem, but they are in-script variables.

Then my way doesn’t work. You could make a model with transparent parts and use that. But that wouldn’t be that efficient.

Well I know there’s gotta be a pretty standard mathematical operation for this.

I found a way. You can get the center by grabbing average position. I think this should also work for the orientation.

CenterPoint.Position = (script.Parent.Point1.Position + script.Parent.Point2.Position + script.Parent.Point3.Position + script.Parent.Point4.Position) / 4
--Change every position to your vector3 values.

That doesn’t account for rotation, I know where the centerpoint is, I need to know the rotation

  1. Take three corners. Label them A, B, C. Imagine that there are line segments going through them (AB, BC, and CA). You have a triangle!

  1. See the line segment labeled CA in the diagram? The center of that will be the Position of the part.
  2. … AB in the diagram? That’s going to be the RightVector of the CFrame.
  3. … BC in the diagram? That’s going to be the UpVector of the CFrame.

Code:

local a, b, c = --< ... three points (Vector3s) in a clockwise direction, assuming that you're looking directly at the front of the part >
local position = (c + a)/2 -- the midpoint of segment CA, which will be the position of the part.
local right = (b - a).Unit -- ... the RightVector of the CFrame you want.
local up = (c - b).Unit --- ... the UpVector of the CFrame you want.
local cf = CFrame.fromMatrix(position, right, up) -- Tada! This should be the CFrame of the part.

(note: I may have swapped right and up. So if the LookVector of the CFrame isn’t facing in the direction you want, just swap the two. Also play around with different corners if the up and right aren’t facing the correct direction. This should give you a rough idea on how to approach this problem.)

Don’t you mean BC is the lookvector?

No. BC would be the UpVector. UpVector is a thing.

Ah I see .fromMatrix that right there is what I really needed. I was already doing a method similar to that, but it had some inaccuracies, so that operation definitely helps.

I know, but in this example, (b-c).Unit represents the lookvector of the final cframe, since if it were the upvector, the part would result 90 degrees off

Well, it really depends. You didn’t explain which dimension of the part is flat. I was assuming that the Size.Z of the part was 0, in which case I would be right.

If instead it was Y that would be flat (so that BC would be the LookVector instead of the UpVector) then you can use Vector3:Cross to calculate the UpVector that you want (look up what a vector cross product is if you want, it’s generally useful if you want a vector that is perpendicular to two vectors)

local right = (b - a).Unit -- ... the RightVector of the CFrame you want.
local look = (c - b).Unit --- ... the LookVector of the CFrame you want.
local uo = right:Cross(look) -- the UpVector of the CFrame you want. I may have swapped these two, so again if it doesn't face the right direction, just switch the two.
local cf = CFrame.fromMatrix(position, right, up) -- Tada! This should be the CFrame of the part.
1 Like

I would think this should work:

local function toPlane(verts)
	local centre   = Vector3.new() * 0
	for _, vert in ipairs(verts) do
		centre = centre + vert
	end
	centre = centre / #verts

	local function toAngle(p, c)
		return math.atan2(p.z - c.z, p.x - c.x)
	end

	local function byPolarAngle(va, vb, centre)
		return toAngle(va, centre) > toAngle(vb, centre)
	end

	table.sort(verts, function (a, b)
		return byPolarAngle(a, b, centre)
	end)
	
	local front = (verts[1] - verts[4]).unit
	local right = (verts[3] - verts[4]).unit
	local up    = right:Cross(front)
	return {
		cf = CFrame.new(
			centre.x, centre.y, centre.z,
			right.x, up.x, -front.x,
			right.y, up.y, -front.y,
			right.z, up.z, -front.z
		);
		sz = Vector3.new(
			(verts[1] - verts[4]).magnitude,
			0,
			(verts[3] - verts[4]).magnitude
		);
	}
end


-- e.g.
local vertices = { }
for i, v in ipairs(script.Parent:GetChildren()) do
	if v:IsA 'BasePart' then
		vertices[#vertices + 1] = v.Position
	end
end

local info  = toPlane(vertices)
local plane = Instance.new 'Part'
plane.Anchored = true
plane.Transparency = 0.5
plane.Size = info.sz
plane.CFrame = info.cf
plane.Parent = script