Untitled

mail@pastecode.io avatar
unknown
lua
5 months ago
9.0 kB
4
Indexable
local pointsList = {}

task.wait(0.5)

local padding = 0.01 -- Keep padding small to minimize gaps

function generatePart(position)
	local part = Instance.new("Part")
	part.Size = Vector3.new(.4, .4, .4)
	part.Position = position
	part.Material = Enum.Material.Neon
	part.Color = Color3.fromRGB(255, 0, 0)
	part.Anchored = true
	part.Parent = game.Workspace.VisualizationPoints
	return part
end

table.insert(pointsList, script.Parent.Bounds.Attachment.WorldCFrame.Position)

local function movePartsTopLeftVerticeTo(part, position)
	local size = part.Size
	local normalSize = Vector3.new(size.X + padding, size.Y + padding, size.Z + padding)
	local topLeftOffset = Vector3.new(-normalSize.X / 2, -normalSize.Y / 2, -normalSize.Z / 2)
	local currentTopLeftPosition = part.Position + topLeftOffset

	local offset = position - currentTopLeftPosition
	part.Position = part.Position + offset
end

local function registerVertices(part)
	local temp = {}
	local size = part.Size
	size = Vector3.new(size.X + padding, size.Y + padding, size.Z + padding)

	local topLeftOffset = Vector3.new(-size.X / 2, -size.Y / 2, size.Z / 2)
	local currentTopLeftPosition = part.CFrame.Position + topLeftOffset
	table.insert(temp, currentTopLeftPosition)

	local topRightOffset = Vector3.new(-size.X / 2, -size.Y / 2, -size.Z / 2)
	local currentTopRightPosition = part.CFrame.Position + topRightOffset
	table.insert(temp, currentTopRightPosition)

	local bottomLeftOffset = Vector3.new(size.X / 2, -size.Y / 2, size.Z / 2)
	local currentBottomLeftPosition = part.CFrame.Position + bottomLeftOffset
	table.insert(temp, currentBottomLeftPosition)

	local bottomRightOffset = Vector3.new(size.X / 2, -size.Y / 2, -size.Z / 2)
	local currentBottomRightPosition = part.CFrame.Position + bottomRightOffset
	table.insert(temp, currentBottomRightPosition)

	return temp
end

local function isInContainer(part)
	local box = script.Parent.Bounds
	local size = part.Size
	local partSize = Vector3.new(size.X + padding, size.Y + padding, size.Z + padding)

	local partCFrame = part.CFrame
	local boxCFrame, boxSize = box.CFrame, box.Size

	local partMin = partCFrame.Position - (partSize / 2)
	local partMax = partCFrame.Position + (partSize / 2)

	local boxMin = boxCFrame.Position - (boxSize / 2)
	local boxMax = boxCFrame.Position + (boxSize / 2)

	return partMin.X >= boxMin.X and partMax.X <= boxMax.X and
		partMin.Y >= boxMin.Y and partMax.Y <= boxMax.Y and
		partMin.Z >= boxMin.Z and partMax.Z <= boxMax.Z
end

local function isOverlapping(part)
	local params = OverlapParams.new()
	params.FilterType = Enum.RaycastFilterType.Include
	local children = script.Parent.Pending:GetChildren()
	local removeIndex = table.find(children, part)
	table.remove(children, removeIndex)
	params.FilterDescendantsInstances = children

	local cframe = part.CFrame
	local partSize = part.Size
	partSize = Vector3.new(partSize.X + padding, partSize.Y + padding, partSize.Z + padding)

	local parts = workspace:GetPartBoundsInBox(cframe, partSize, params)

	return #parts > 0
end

local function returnRelativeMagnitude(position)
	local offset = position - script.Parent.Bounds.Attachment.WorldCFrame.Position
	return math.abs(offset.X) + (math.abs(offset.Y)) + (500000000 * math.abs(offset.Z))
end

local function tryAllRotationsAndPlace(box, pointsList)
	local bestPoint = nil
	local bestOrientation = nil
	local minMagnitude = math.huge

	for _, rotation in ipairs({0, 90, 180, 270}) do
		box.Orientation = Vector3.new(0, rotation, 0)

		for _, point in ipairs(pointsList) do
			movePartsTopLeftVerticeTo(box, point)

			if not isOverlapping(box) and isInContainer(box) then
				local magnitude = returnRelativeMagnitude(point)
				if magnitude < minMagnitude then
					minMagnitude = magnitude
					bestPoint = point
					bestOrientation = rotation
				end
			end
		end
	end

	return bestPoint, bestOrientation
end

function putPartAbovePart(partAbove, partBelow)
	local padding2 = 0.01
	local partAboveSize = Vector3.new(partAbove.Size.X + padding2, partAbove.Size.Y + padding2, partAbove.Size.Z + padding2)
	partAbove.Position = Vector3.new(partAbove.Position.X, partBelow.Position.Y + partBelow.Size.Y / 2 + partAboveSize.Y / 2, partAbove.Position.Z)
end

local function getVertices(part)
	local padding2 = -1
	local size = part.Size
	size = Vector3.new(size.X + padding2, size.Y + padding2, size.Z + padding2)

	local topLeft = part.CFrame * CFrame.new(-size.X / 2, size.Y / 2, -size.Z / 2).Position
	local topRight = part.CFrame * CFrame.new(size.X / 2, size.Y / 2, -size.Z / 2).Position
	local bottomLeft = part.CFrame * CFrame.new(-size.X / 2, size.Y / 2, size.Z / 2).Position
	local bottomRight = part.CFrame * CFrame.new(size.X / 2, size.Y / 2, size.Z / 2).Position

	return {topLeft, topRight, bottomLeft, bottomRight}
end

local function compFunc(part, partAbove)
	return (part.Position - partAbove.Position).Magnitude
end

local function getTopLeftAndBottomRightVertices(partAbove)
	local size = partAbove.Size
	size = Vector3.new(size.X + padding, size.Y + padding, size.Z + padding)

	local topLeftOffset = Vector3.new(-size.X / 2, -size.Y / 2, size.Z / 2)
	local currentTopLeftPosition = partAbove.CFrame.Position + topLeftOffset

	local bottomRightOffset = Vector3.new(size.X / 2, -size.Y / 2, -size.Z / 2)
	local currentBottomRightPosition = partAbove.CFrame.Position + bottomRightOffset

	return currentTopLeftPosition, currentBottomRightPosition
end

local function findPartJustBelow(partAbove, partFolder)
	local partsListBelow = {}
	for _, v in pairs(partFolder:GetChildren()) do
		if v:IsA("BasePart") and v.Position.Y < partAbove.Position.Y then
			table.insert(partsListBelow, v)
		end
	end

	local partsBelowInRange = {}

	local topLeft, bottomRight = getTopLeftAndBottomRightVertices(partAbove)

	for _, iterationBox in pairs(partsListBelow) do
		local vertices = getVertices(iterationBox)

		for _, vertex in pairs(vertices) do
			if vertex.X >= topLeft.X and vertex.X <= bottomRight.X and
				vertex.Z >= bottomRight.Z and vertex.Z <= topLeft.Z then
				table.insert(partsBelowInRange, iterationBox)
				break -- No need to check other vertices once one is inside the range
			end
		end
	end

	table.sort(partsBelowInRange, function(a, b)
		return compFunc(a, partAbove) < compFunc(b, partAbove)
	end)

	return partsBelowInRange[1]
end

local function findBestPositionForBox(box, pointsList)
	local bestPoint = nil
	local bestOrientation = nil
	local minMagnitude = math.huge

	-- First try horizontal placement
	for _, rotation in ipairs({0, 90, 180, 270}) do
		box.Orientation = Vector3.new(0, rotation, 0)

		for _, point in ipairs(pointsList) do
			movePartsTopLeftVerticeTo(box, point)

			if not isOverlapping(box) and isInContainer(box) then
				local magnitude = returnRelativeMagnitude(point)
				if magnitude < minMagnitude then
					minMagnitude = magnitude
					bestPoint = point
					bestOrientation = rotation
				end
			end
		end
	end

	-- If no horizontal placement found, try vertical stacking
	if not bestPoint then
		for _, rotation in ipairs({0, 90, 180, 270}) do
			box.Orientation = Vector3.new(0, rotation, 0)

			for _, point in ipairs(pointsList) do
				for y = 0, script.Parent.Bounds.Size.Y - box.Size.Y, box.Size.Y + padding do
					local adjustedPoint = point + Vector3.new(0, y, 0)
					movePartsTopLeftVerticeTo(box, adjustedPoint)

					if not isOverlapping(box) and isInContainer(box) then
						local magnitude = returnRelativeMagnitude(adjustedPoint)
						if magnitude < minMagnitude then
							minMagnitude = magnitude
							bestPoint = adjustedPoint
							bestOrientation = rotation
						end
					end
				end
			end
		end
	end

	return bestPoint, bestOrientation
end

local debounce = false		

local function reCalculate()
	if debounce then return end
	debounce = true

	local boxes = script.Parent.Pending:GetChildren()
	for _, BoxIterate in ipairs(boxes) do
		task.wait()

		local prevpos = BoxIterate.Position
		local bestPoint, bestOrientation = findBestPositionForBox(BoxIterate, pointsList)

		if bestPoint and bestOrientation then
			warn("Placement found")
			BoxIterate.Orientation = Vector3.new(0, bestOrientation, 0)
			movePartsTopLeftVerticeTo(BoxIterate, bestPoint)

			local vertices = registerVertices(BoxIterate)

			for _, v in ipairs(vertices) do
				table.insert(pointsList, v)
			end
		else
			BoxIterate.Position = prevpos
		end
	end
	debounce = false
end

local function cleanUp()
	pointsList = nil
	pointsList = {}
	table.insert(pointsList, script.Parent.Bounds.Attachment.WorldCFrame.Position)
 
	for i, v in ipairs(script.Parent.Pending:GetChildren()) do
		v.Position = Vector3.new(0, 0, 0)
	end
end

reCalculate()

script.Parent.Pending.ChildAdded:Connect(function()
	cleanUp()
	reCalculate()
end)
Leave a Comment