Is there a way to see if two "virtual" parts are touching using only CFrame and Size pair data alone?

I am developing a game, but, for performance reasons, need to dynamically generate a 2D table that acts like a virtual 3D container filled with virtual nodes, with each node (virtual basepart) being the table within that 2D table. Each subtable contains the virtual part’s CFrame and Vector3 size information respectively. I want to develop a way to check if a pair of given virtual parts are intersecting in that virtual space using each of their corresponding CFrame and size information alone and no basepart instances whatsoever. I tried using the function below, courtesy of ChatGPT (Exhibit A), but, for some reason, it will sometimes misidentify some virtual parts as touching/intersecting the other part that happen do be in the vicinity of the other part but are not necessarily touching spacially. I know this because I developed a 2nd function (Exhibit B) that renders basepart representations of each virtual part based on its CFrame and size information, to verify what should be going on spacially. Can anyone give me any pointers as to what is going on?

Exhibit A:

local function areVirtualPartsTouching(cframe1, size1, cframe2, size2) --checks if two virtual parts, based each of their CFrame and size values, are touching. Returns true or false accordingly.
	-- Calculate half sizes (extents) for each part
	local halfSize1 = size1 / 2
	local halfSize2 = size2 / 2

	-- Get the corners of the bounding boxes for both parts
	local min1 = cframe1.Position - halfSize1
	local max1 = cframe1.Position + halfSize1

	local min2 = cframe2.Position - halfSize2
	local max2 = cframe2.Position + halfSize2

	-- Check if the bounding boxes intersect in all three axes
	local xOverlap = max1.X >= min2.X and min1.X <= max2.X
	local yOverlap = max1.Y >= min2.Y and min1.Y <= max2.Y
	local zOverlap = max1.Z >= min2.Z and min1.Z <= max2.Z

	-- If all axes overlap, the parts are touching or intersecting
	return xOverlap and yOverlap and zOverlap
end

Exhibit B:

function visualizeNodes(nodesTable) --this function is designed for diagnostic purposes, and just draws node parts on-screen in the corresponding CFrames, names, and sizes of the nodes from a given nodes table.
	print("Starting node visualizer. Warning: This is for diagnostic purposes only, and should not run during production due to performance reasons.")
	local nodePartConstructor = Instance.new("Part")
	nodePartConstructor.Anchored = true
	nodePartConstructor.CanQuery = false
	nodePartConstructor.CanCollide = false
	nodePartConstructor.CanTouch = false
	nodePartConstructor.Transparency = 1
	nodePartConstructor.Locked = false

	local processThrottleCount = 0 --designed to force the process to wait after a certain amount of processes have occurred in the loop for efficiency purposes.
	local nodeCount = 0
	local disconnectedNodesCount = 0
	for _, node in nodesTable do
		nodeCount = nodeCount + 1
		local newNode = nodePartConstructor:Clone()
		newNode.Name = node[1]
		newNode.CFrame = node[2]
		newNode.Size = Vector3.new(node[3], node[3], node[3])
		newNode.Parent = visualizedNodesFolder
		newNode:SetAttribute("PointsTo", node[4])
		if node[4] == -1 then
			newNode.Color = Color3.fromRGB(255, 0, 0)
			newNode.Material = Enum.Material.Neon
			newNode.Transparency = 0
			disconnectedNodesCount = disconnectedNodesCount + 1
			local surroundingNodes, detectionRegions = getNeighborNodePresences(node, grid, cellSize, true)
			if #surroundingNodes <= 0 then
				print("Warning! " .. tostring(node[1]) .. " doesn't have any surrounding nodes!")
			else
				local surroundingNodePartColor = Color3.fromRGB(math.random(0, 255), math.random(0, 255), math.random(0, 255))
				for _, surroundingNode in surroundingNodes do
					local surroundingNodePart = newNode:Clone()
					surroundingNodePart.Name = tostring(node[1] .. "_Surrounding_Node")
					surroundingNodePart.Color = surroundingNodePartColor
					surroundingNodePart.Transparency = 0.75
					surroundingNodePart.CFrame = surroundingNode[2]
					surroundingNode.Size = Vector3.new(surroundingNode[3], surroundingNode[3], surroundingNode[3])
					surroundingNodePart.Parent = visualizedNodesFolder
				end
			end
			
			local detectionRegionColor = Color3.fromRGB(0, 0, 0)
			for _, detectionRegion in detectionRegions do
				local detectionRegionPart = newNode:Clone()
				detectionRegionPart.Name = tostring(node[1] .. "_Detection_Region")
				detectionRegionPart.Color = detectionRegionColor
				detectionRegionPart.Transparency = 0.7
				detectionRegionPart.CFrame = detectionRegion[1]
				detectionRegionPart.Size = Vector3.new(detectionRegion[2].X, detectionRegion[2].Y, detectionRegion[2].Z)
				detectionRegionPart.Parent = visualizedNodesFolder
			end
			
		end
		processThrottleCount = processThrottleCount + 1

		if processThrottleCount > 200 then
			task.wait()
			processThrottleCount = 0
		end

	end

	print("Visualizer completed.")
	print("Total Waypoints/Nodes after Map Generation: " .. tostring(nodeCount))
	print("Total number of nodes that do not point to anything: " .. tostring(disconnectedNodesCount))
end
1 Like

Okay, as it turns out. The algorithm was fine. The issue was with the visualizer system. It wasn’t updating the size of the surrounding nodes accurately - only the CFrame, which is what would cause some parts to appear further away when they’re not.

Are any of the parts rotated? The code in areVirtualPartsTouching doesn’t account for rotation so if the parts are rotated then it won’t necessarily work.

For my usage, I just use the “vanilla” default orientations for my CFrame storage, as the parts in my 3D grid, luckily, are just cubes stacked against each other, but thank you for the insight. As it turns out, the issue was with the visualizer and not the actual algorithm detecting the touch.

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