Subdivisions of a Quadrant

Subdivisions of a Quadrant

Basically, the problem that I am facing here is that there is something going wrong in my calculation of calculating a quadrant, though the issue only occurs when attempting to section an already sectioned quadrant into 4. This is how the sectioning should ideally look like (Image 1), though when I proceed to attempt to section those quadrants into 4, I get a very weird looking result (Image 2);

This is what I am working from:

Script
local ScreenSize = workspace.CurrentCamera.ViewportSize


function Quadrants.new(Point1: Vector2, Point2: Vector2, Width: number, Height: number, CentreVector: Vector2)
    local Quadrant = setmetatable({}, Quadrants)
    Quadrant.Bounds = {
        Min = Point1,
        Max = Point2,
        Width = Width,
        Height = Height,
        CentreVector = CentreVector
    }
    Quadrant.Divided = false
    return Quadrant
end

function Quadrants:SubDivide()
    if (self.Divided) and true then return end

    local CentreVector = self.Bounds.CentreVector

    local northeastMax = Vector2.new(math.clamp(CentreVector.X + CentreVector.X, 0, ScreenSize.X), math.clamp(CentreVector.Y - CentreVector.Y, 0, ScreenSize.Y))
    local northwestMax = Vector2.new(math.clamp(CentreVector.X - CentreVector.X, 0, ScreenSize.X), math.clamp(CentreVector.Y - CentreVector.Y, 0, ScreenSize.Y))
    local southeastMax = Vector2.new(math.clamp(CentreVector.X + CentreVector.X, 0, ScreenSize.X), math.clamp(CentreVector.Y + CentreVector.Y, 0, ScreenSize.Y))
    local southwestMax = Vector2.new(math.clamp(CentreVector.X - CentreVector.X, 0, ScreenSize.X), math.clamp(CentreVector.Y + CentreVector.Y, 0, ScreenSize.Y))

    local northeastHeight = CentreVector.Y
    local northwestHeight = CentreVector.Y
    local southwestHeight = CentreVector.Y
    local southeastHeight = CentreVector.Y

    local northwestWidth = CentreVector.X
    local southeastWidth = CentreVector.X
    local southwestWidth = CentreVector.X
    local northeastWidth = CentreVector.X

    local CentreNorthWest = Vector2.new(CentreVector.X - (northwestWidth / 2), CentreVector.Y - (northwestHeight / 2))
    local CentreNorthEast = Vector2.new(CentreVector.X + (northeastWidth / 2), CentreVector.Y - (northeastHeight / 2))
    local CentreSouthWest = Vector2.new(CentreVector.X - (southwestWidth / 2), CentreVector.Y + (southwestHeight / 2))
    local CentreSouthEast = Vector2.new(CentreVector.X + (southeastWidth / 2), CentreVector.Y + (southeastHeight / 2))

    self.northeast = Quadrants.new(CentreVector, northeastMax, northeastWidth, northeastHeight, CentreNorthEast)
    self.northwest = Quadrants.new(CentreVector, northwestMax, northwestWidth, northwestHeight, CentreNorthWest)
    self.southeast = Quadrants.new(CentreVector, southeastMax, southeastWidth, southeastHeight, CentreSouthEast)
    self.southwest = Quadrants.new(CentreVector, southwestMax, southwestWidth, southwestHeight, CentreSouthWest)

   
end

I based my calculations on these ones I had made a sketch off, though I am unsure if they are even right!

2 Likes

I did the math and your quadrant max and center points are correct.

You code looks great. The only thing I can think of that could be wrong is if the min and max of the quadrant actually have to the min and max points.

so this:

self.northeast = Quadrants.new(CentreVector, northeastMax, northeastWidth, northeastHeight, CentreNorthEast)
self.northwest = Quadrants.new(CentreVector, northwestMax, northwestWidth, northwestHeight, CentreNorthWest)
self.southeast = Quadrants.new(CentreVector, southeastMax, southeastWidth, southeastHeight, CentreSouthEast)
self.southwest = Quadrants.new(CentreVector, southwestMax, southwestWidth, southwestHeight, CentreSouthWest)

should be this:

self.northeast = Quadrants.new(northeastMax, CentreVector, northeastWidth, northeastHeight, CentreNorthEast)
self.northwest = Quadrants.new(northwestMax, CentreVector, northwestWidth, northwestHeight, CentreNorthWest)
self.southeast = Quadrants.new(CentreVector, southeastMax, southeastWidth, southeastHeight, CentreSouthEast)
self.southwest = Quadrants.new(CentreVector, southwestMax, southwestWidth, southwestHeight, CentreSouthWest)

Otherwise I got nothing for ya as the code looks good. spamming print and looking through the debugger to see what’s going on

1 Like

I’m not sure what is wrong with your code exactly, but I decided to come up with a solution myself using recursion. I have tested mine and it works (supports any number of subdivides and supports both pixel scale and offset for different screen sizes and use cases). Hopefully, my code can help you understand what went wrong!

Visual sketch of my math

Complete code

local Quadrants = {}
Quadrants.__index = Quadrants

--Use UDim2 to support both scale and pixel offsets
function Quadrants.new(Position: UDim2, Size: UDim2)
	local Quadrant = setmetatable({}, Quadrants)
	Quadrant.Position = Position --top left position (x, y)
	Quadrant.Size = Size --size from top left (x, y)
	
	--Array of existing subdivisions for this quadrant. The index is the standard quadrant number
	--(1 = top right, move counter-clockwise, 4 = bottom right)
	Quadrant.Subdivisions = {}
	
	return Quadrant
end

--Subdivides a single given quadrant object (see supplied image for visual of what this does)
local function doSubdivide(Quadrant): ()
	local halfSize = UDim2.new(Quadrant.Size.X.Scale/2, Quadrant.Size.X.Offset/2, Quadrant.Size.Y.Scale/2, Quadrant.Size.Y.Offset/2)
	Quadrant.Subdivisions = {
		Quadrants.new(UDim2.new(Quadrant.Position.X + halfSize.X, 	Quadrant.Position.Y), 				halfSize), --top right
		Quadrants.new(UDim2.new(Quadrant.Position.X, 				Quadrant.Position.Y), 				halfSize), --top left
		Quadrants.new(UDim2.new(Quadrant.Position.X, 				Quadrant.Position.Y + halfSize.Y), 	halfSize), --bottom left
		Quadrants.new(UDim2.new(Quadrant.Position.X + halfSize.X, 	Quadrant.Position.Y + halfSize.Y), 	halfSize), --bottom right
	}
end

--Recursively subdivides all quadrants, or a specific quadrant if specified by index
function Quadrants:Subdivide(QuadrantIndex: number): ()
	if (QuadrantIndex ~= nil and QuadrantIndex > 0) then
		local Quadrant = self.Subdivisions[QuadrantIndex]
		if (Quadrant) then
			return doSubdivide(Quadrant)
		end
	else
		if (self.Subdivisions == nil or table.getn(self.Subdivisions) > 0) then
			for i, Quadrant in ipairs(self.Subdivisions) do
				Quadrant:Subdivide()
			end
		else
			return doSubdivide(self)
		end
	end
end

--Returns an array of all the *deepest* quadrants
function Quadrants:GetQuadrants()
	local quadrants = {}
	for i, InnerQuadrant in ipairs(self.Subdivisions) do
		if (table.getn(InnerQuadrant.Subdivisions) > 0) then
			local moreQuadrants = InnerQuadrant:GetQuadrants()
			local count: number = table.getn(moreQuadrants)
			if (moreQuadrants and count > 0) then
				table.move(moreQuadrants, 1, count, table.getn(quadrants) + 1, quadrants)
			end
		else
			table.insert(quadrants, InnerQuadrant)
		end
	end
	return quadrants
end

--Example code
local GuiFrame: Frame = script.Parent:FindFirstChild("Frame") :: Frame
local FrameQuadrants = Quadrants.new(GuiFrame.Position, GuiFrame.Size)

local SubdivideIterations: number = 1
for i = 1, SubdivideIterations do
	FrameQuadrants:Subdivide()
end

--Visualize
for i, Quadrant in ipairs(FrameQuadrants:GetQuadrants()) do
	local frame: Frame = Instance.new("Frame")
	frame.Name = tostring(i)
	frame.Position = Quadrant.Position
	frame.Size = Quadrant.Size
	frame.BackgroundColor3 = BrickColor.Random().Color
	local textLabel: TextLabel = Instance.new("TextLabel")
	textLabel.BackgroundTransparency = 1
	textLabel.Size = UDim2.fromScale(1, 1)
	textLabel.TextColor3 = Color3.new(1, 1, 1)
	textLabel.TextStrokeTransparency = 0
	textLabel.TextScaled = true
	textLabel.Text = tostring(i)
	textLabel.Parent = frame
	frame.Parent = GuiFrame
end

The results

Screenshots

1 Iteration


2 Iterations

3 Iterations

1 Like

Thank you so much, your calculations work like a charm, appreciate your help :smiley: !

1 Like

This didn’t quite seem to resolve my issue, thank you for your time and help though :slight_smile: