Anyone who knows about railroads and fixed block signaling would have an easier time understanding my situation.

I am working on a plugin that is supposed to mimic a railroad interlocking control panel. You can fill the upper and lower levels of a tile (frame on screenGUI) with lines representing tracks, and based on the direction of the line you pick, new tiles open up adjacently to be filled with more tracks. There is an option to select a specific side/point of a tile, either Top, Bottom, Left, or Right. You can decide if this point, along with its adjacent point of the adjacent tile (example - if the bottom point of a tile (lower or upper level) is a block boundary, then the top of the tile below it (lower or upper level) will automatically be a block boundary as well. In order to define blocks of track by finding the connections between tiles, I wrote this recursive function. It is so long because it accounts for times that a point has more than one connection within a tile (example - the left side of a tile is connected to both the top and the right), and also when a block of track takes up more than one tile.

local function adjustBlocks(ti, lvl, pt, num)
	local mainPos = getTilePos(ti)
	local mainX = mainPos[1]
	local mainY = mainPos[2]
	local tilePt = tilePointData[ti][lvl][pt]
	local inbound = {}
	if tilePt.blockBoundary == true then inbound[#inbound + 1] = {Tile = ti, Level = lvl, Point = pt} end
	local outbound = {}
	local function blockRec()
		local A
		local B
		if num % 2 == 0 then
			A = inbound
			B = outbound
		else
			A = outbound
			B = inbound
		end
		local connection = tilePt.connectedPoint
		if typeof(connection) == "string" then
			local otherPoint = tilePointData[ti][lvl][connection] 
			local otherConnection = otherPoint.connectedPoint
			if otherPoint.blockBoundary == true then
				A[#A + 1] = {Tile = ti, Level = lvl, Point = connection}
			else
				if typeof(otherConnection) == "table" then
					for i = 1, #otherConnection do
						if otherConnection[i] ~= pt then
							local secondaryPoint = tilePointData[ti][lvl][otherConnection[i]] 
							print(secondaryPoint)
							if secondaryPoint.blockBoundary == true then
								B[#B + 1] = {Tile = ti, Level = lvl, Point = otherConnection[i]}
							else
								if secondaryPoint.adjLevel then
									if otherConnection[i] == "Top" then
										blockRec(tiles[mainX][mainY - 1], secondaryPoint.adjLevel, "Bottom", num + 1)
									elseif otherConnection[i] == "Bottom" then
										blockRec(tiles[mainX][mainY + 1], secondaryPoint.adjLevel, "Top", num + 1)
									elseif otherConnection[i] == "Right" then
										blockRec(tiles[mainX + 1][mainY], secondaryPoint.adjLevel, "Left", num + 1)
									elseif otherConnection[i] == "Left" then
										blockRec(tiles[mainX - 1][mainY], secondaryPoint.adjLevel, "Right", num + 1)
									end
								end
							end
						end
					end
				else
					if otherPoint.adjLevel then
						if connection == "Top" then
							blockRec(tiles[mainX][mainY - 1], otherPoint.adjLevel, "Bottom", num)
						elseif connection == "Bottom" then
							blockRec(tiles[mainX][mainY + 1], otherPoint.adjLevel, "Top", num)
						elseif connection == "Right" then
							blockRec(tiles[mainX + 1][mainY], otherPoint.adjLevel, "Left", num)
						elseif connection == "Left" then
							blockRec(tiles[mainX - 1][mainY], otherPoint.adjLevel, "Right", num)
						end
					end
				end
			end
		elseif typeof(connection) == "table" then
			for i = 1, #connection do
				local otherPoint = tilePointData[ti][lvl][connection[i]]
				local otherConnection = otherPoint.connectedPoint
				if otherPoint.blockBoundary == true then
					B[#B + 1] = {Tile = ti, Level = lvl, Point = connection[i]}
				else
					if typeof(otherConnection) == "table" then
						for i = 1, #otherConnection do
							if otherConnection[i] ~= pt then
								local secondaryPoint = tilePointData[ti][lvl][otherConnection[i]] 
								if secondaryPoint.blockBoundary == true then
									A[#A + 1] = {Tile = ti, Level = lvl, Point = otherConnection[i]}
								else
									if secondaryPoint.adjLevel then
										if otherConnection[i] == "Top" then
											blockRec(tiles[mainX][mainY - 1], secondaryPoint.adjLevel, "Bottom", num)
										elseif otherConnection[i] == "Bottom" then
											blockRec(tiles[mainX][mainY + 1], secondaryPoint.adjLevel, "Top", num)
										elseif otherConnection[i] == "Right" then
											blockRec(tiles[mainX + 1][mainY], secondaryPoint.adjLevel, "Left", num)
										elseif otherConnection[i] == "Left" then
											blockRec(tiles[mainX - 1][mainY], secondaryPoint.adjLevel, "Right", num)
										end
									end
								end
							end
						end
					else
						if otherPoint.adjLevel then
							if connection == "Top" then
								blockRec(tiles[mainX][mainY - 1], otherPoint.adjLevel, "Bottom", num)
							elseif connection == "Bottom" then
								blockRec(tiles[mainX][mainY + 1], otherPoint.adjLevel, "Top", num)
							elseif connection == "Right" then
								blockRec(tiles[mainX + 1][mainY], otherPoint.adjLevel, "Left", num)
							elseif connection == "Left" then
								blockRec(tiles[mainX - 1][mainY], otherPoint.adjLevel, "Right", num)
							end
						end
					end
				end
			end
		end	
	end
	blockRec()
	return {inbound, outbound}
end

The function only works properly when it only has to traverse one tile to define each block (if two tiles are connected by straight tracks, then I can make the point where they meet a block boundary without a problem). However, if the function actually needs to be recursive (the function needs to search the next tile), there is a stack overflow, and the output says that there were over 16000 instances of recursion, even though there were only 3 tiles I was trying to connect. How can I prevent this problem?