The result you want can be archived by partitioning the viewport area. You are going to need more constraints to get what you want (e.g. minimum width, minimum height). Which reminds me, I don't know how you want this to behave when the viewport is resized.
Anyway, the idea is to make a tree structure (and for that you can take advantage of the scene tree), where the root has the area of the viewport. It decides how to split it in two (either vertically or horizontally) using random, and then creates two children for those areas. The children do the same with the area they were given, until you get to a point where splitting would violate the constraints.
To decide where to divide, first figure out the valid range for an horizontal and vertical division. If the range is empty you can't do that division. If you can't do either horizontal or vertical division, then don't divide.
The range for a division goes from the minimum to the total minus the minimum. Something like this:
var range_min_x := minimum_width
var range_max_x := width - minimum_width
var can_divide_x := range_min_x < range_max_x
var range_min_y := minimum_height
var range_max_y := height - minimum_height
var can_divide_y := range_min_y < range_max_y
if can_divide_x and can_divide_y:
if randf() < 0.5:
divide_x(range_min_x, range_max_x)
else:
divide_y(range_min_y, range_max_y)
elif can_divide_x:
divide_x(range_min_x, range_max_x)
elif can_divide_y:
divide_y(range_min_y, range_max_y)
else:
# don't divide
pass
And then those functions look like this:
func divide_x(min:float, max:float):
var division := int(rand_range(min, max))
create_child(Rect2(0.0, 0.0, division, height))
create_child(Rect2(division, 0.0, width - division, height))
func divide_y(min:float, max:float):
var division := int(rand_range(min, max))
create_child(Rect2(0.0, 0.0, width, division))
create_child(Rect2(0.0, division, width, height - division))
Using int
is important to avoid one child rounding down and other rounding up, leaving a one pixel gap.
Then in the create_child
function you would be creating and adding a child with the given dimension, which would then also be divided by the same means.
You are not going to have empty areas because you would always be adding pairs of children that make up the total of the parent area.