Help to check if a part is full inside another part

Hii
I am trying to check if a part its INSIDE a part, full inside, not just a bit

Code I got atm:

local c = workspace.c
local v = workspace.x
print(c.Position.x - v.Position.x <= v.Size.X/2 and c.Position.x - v.Position.x >= v.Size.X/-2 and c.Position.z - v.Position.z <= v.Size.z/2 and c.Position.z - v.Position.z >= v.Size.z/-2)

https://gyazo.com/904005e3d6bafe64169c0091891a30e1
It should print true when its like this:
https://gyazo.com/aa9f17299ef3747debf27f2e273dd7c6

1 Like

You’ve to calculate doing this;
First you check if the part is inside and then you’ll have to calculate if any of the positions when you use CFrame are inside or outside the part.

1 Like

So what you’d do is something like this but 8 times for ever corner
image
You’d have to do:
the CFrame + Vector(Part.Size.X/2,Part.Size.Y/2,Part.Size.Z/2)
and then do then make each of them negative so you’ve 8 CFrames, which you check the position of and calculate if they’re inside or not.
If you’re making a building system I suggest using other tricks instead of this.

1 Like

EDIT: Or just do what Kry_s suggested xD

Can you tell us about your use case? If we can make some assumptions that might make it a lot easier.

E.g. are the Parts always axis-aligned boxes (AABs)? Or can they rotated, or be spheres/cylinders too?

To check if a AAB is fully within another AAB, you can do like this:

function isAABInsideAAB(position1, size1, position2, size2)
	--Checks if an AABB defined by position1 and size1 is fully within the AABB defined by position2 and size2
	--	if they have the same size and position, returns true.

	local p1, p2 = position1, position2
	local s1, s2 = size1/2, size2/2

	local minX1, minX2 = p1.X - s1.X, p2.X - s2.X
	local maxX1, maxX2 = p1.X + s1.X, p2.X + s2.X
	local minY1, minY2 = p1.Y - s1.Y, p2.Y - s2.Y
	local maxY1, maxY2 = p1.Y + s1.Y, p2.Y + s2.Y
	local minZ1, minZ2 = p1.Z - s1.Z, p2.Z - s2.Z
	local maxZ1, maxZ2 = p1.Z + s1.Z, p2.Z + s2.Z

	return 
		(minX1 >= minX2 and maxX1 <= maxX2) and
		(minY1 >= minY2 and maxY1 <= maxY2) and
		(minZ1 >= minZ2 and maxZ1 <= maxZ2)
end

You can’t just use any part’s position and size for this. It’s important that the part is not e.g. rotated 90 degrees on an axis, so the rotation has to be (0, 0, 0). We can get around this with math, to convert any Part that’s only rotated in increments of 90 degrees to an AAB:

function partToAAB(part)
	--Given an axis-aligned part, returns the position and size of an AAB that takes up the same space as the part,
	--	but whose X, Y and Z axes are the same as the world's axes.
	assert(isAxisAligned(part.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")

	local AABPosition = part.Position
	local AABSize = Vector3.new()

	local worldCFrame = CFrame.new()

	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(part.Size.X, 0, 0) )
	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(0, part.Size.Y, 0) )
	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(0, 0, part.Size.Z) )

	return partPosition, AABSize
end

It errors if the part isn’t axis aligned, i.e. if it’s at an angle other than 90 degrees. That check looks like so:

function isAxisAligned(cFrame)
	--Checks if a CFrame is rotated *only* at right angles.
	local worldCFrame = CFrame.new()

	--How much is the CFrame's sides facing towards the world's sides?
	local dotFront = cFrame.XVector:Dot( worldCFrame.XVector )
	local dotRight = cFrame.YVector:Dot( worldCFrame.YVector )
	
	--Given any combination 90-degree rotations, the angles between the CFrame's sides and the world's sides can only
	--	0, 90 or 180 degrees. That corresponds to dot products of 1, 0, or -1. Any non-whole-number dot products % 1 will
	--	equal somthing other than 0, while 1%1 = 0, 0%1 = 0 and -1%0 = 0.
	--	so checking if these two dot products % 1 == 0 is sufficient to checking of the CFrame's sides are rotated only
	--	through 90-degree increments, i.e. if the CFrame is axis-aligned
	return dotFront % 1 == 0 and dotRight % 1 == 0
end

We can combine it all into a function that checks if one part is fully inside another:

function isPartInsidePart(part1, part2)
	--Returns true if part1 is fully inside part2, assuming both are axis-aligned.
	--Throws an error if part1 or part2 is not axis-aligned.
	assert(isAxisAligned(part1.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")
	assert(isAxisAligned(part2.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")

	local AAB1Pos, AAB1Size = partToAAB(part1)
	local AAB2Pos, AAB2Size = partToAAB(part1)

	return isAABInsideAAB(aab1Pos, aab1Size, aab2Pos, aab2Size)
end

Of course, all this doesn’t work if you also need to check for parts that are rotated at arbitrary angles.

6 Likes

Thanks!
That’s way easier, I tried to also make my own way to LIMIT the moving for my placement system;

function module:Update(HitP,Limits)
	if (self.Placing and self.GrabbingObject) then
		local X,Y,Z = HitP.X,HitP.Y,HitP.Z
		X =math.clamp(X,self.Grid.Position.X - (self.Grid.Size.X/2 - self.GridPart.Size.X/2),self.Grid.Position.X + (self.Grid.Size.X/2) - self.GridPart.Size.X/2)
		Z = math.clamp(Z,self.Grid.Position.Z - (self.Grid.Size.Z/2 - self.GridPart.Size.Z/2),self.Grid.Position.Z + (self.Grid.Size.Z/2) - self.GridPart.Size.Z/2)
		Y = (self.Grid.Position.Y + self.Grid.Size.Y/2 + self.GridPart.Size.Y/2)
		local cf = (CFrame.new(Vector3.new(X,Y,Z)) * CFrame.Angles(0,math.rad(self.Rotation),0))
		self.GrabbingObject:SetPrimaryPartCFrame(cf)
	end
end

Kinda messy
It seems it worked

https://gyazo.com/aae8e4e7b430d75e16084a79488dbbb7

There were a few issues with the script such as;

  1. inside partToAAB() you return partPosition, which isn’t a variable, but AABPosition is;

  2. inside isPartInsidePart()
    2.1 AAB1Pos == AAB2Pos and AAB1Size == AAB2Size as you used partToAAB(part1) twice
    2.2 at the return line you write aab1Pos, aab1Size, aab2Pos and aab2Size all without capital letters

So here’s the fixed script:

function isAABInsideAAB(position1, size1, position2, size2)
	--Checks if an AABB defined by position1 and size1 is fully within the AABB defined by position2 and size2
	--	if they have the same size and position, returns true.
	
	local p1, p2 = position1, position2
	local s1, s2 = size1/2, size2/2
	
	local minX1, minX2 = p1.X - s1.X, p2.X - s2.X
	local maxX1, maxX2 = p1.X + s1.X, p2.X + s2.X
	local minY1, minY2 = p1.Y - s1.Y, p2.Y - s2.Y
	local maxY1, maxY2 = p1.Y + s1.Y, p2.Y + s2.Y
	local minZ1, minZ2 = p1.Z - s1.Z, p2.Z - s2.Z
	local maxZ1, maxZ2 = p1.Z + s1.Z, p2.Z + s2.Z
	
	return 
		(minX1 >= minX2 and maxX1 <= maxX2) and
		(minY1 >= minY2 and maxY1 <= maxY2) and
		(minZ1 >= minZ2 and maxZ1 <= maxZ2)
end

function partToAAB(part)
	--Given an axis-aligned part, returns the position and size of an AAB that takes up the same space as the part,
	--	but whose X, Y and Z axes are the same as the world's axes.
	assert(isAxisAligned(part.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")
	
	local AABPosition = part.Position
	local AABSize = Vector3.new()
	
	local worldCFrame = CFrame.new()
	
	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(part.Size.X, 0, 0) )
	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(0, part.Size.Y, 0) )
	AABSize += part.CFrame:VectorToWorldSpace( Vector3.new(0, 0, part.Size.Z) )
	
	return AABPosition, AABSize
end

function isAxisAligned(cFrame)
	--Checks if a CFrame is rotated *only* at right angles.
	local worldCFrame = CFrame.new()
	
	--How much is the CFrame's sides facing towards the world's sides?
	local dotFront = cFrame.XVector:Dot( worldCFrame.XVector )
	local dotRight = cFrame.YVector:Dot( worldCFrame.YVector )
	
	--Given any combination 90-degree rotations, the angles between the CFrame's sides and the world's sides can only
	--	0, 90 or 180 degrees. That corresponds to dot products of 1, 0, or -1. Any non-whole-number dot products % 1 will
	--	equal somthing other than 0, while 1%1 = 0, 0%1 = 0 and -1%0 = 0.
	--	so checking if these two dot products % 1 == 0 is sufficient to checking of the CFrame's sides are rotated only
	--	through 90-degree increments, i.e. if the CFrame is axis-aligned
	return dotFront % 1 == 0 and dotRight % 1 == 0
end

function isPartInsidePart(part1, part2)
	--Returns true if part1 is fully inside part2, assuming both are axis-aligned.
	--Throws an error if part1 or part2 is not axis-aligned.
	assert(isAxisAligned(part1.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")
	assert(isAxisAligned(part2.CFrame), "Tried to convert a non-axis-aligned Part to an axis-alinged box.")
	
	local AAB1Pos, AAB1Size = partToAAB(part1)
	local AAB2Pos, AAB2Size = partToAAB(part2)
	
	return isAABInsideAAB(AAB1Pos, AAB1Size, AAB2Pos, AAB2Size)
end

Now it’s supposed to run fine, Thanks(RoBama) for helping him as well! :smiley:

2 Likes

Hey, what If I need the part to not be axis aligned?

3 Likes