Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active April 15, 2026 18:13
Show Gist options
  • Select an option

  • Save uwezi/b1a5376accbb21121e0b41e61d96c9e0 to your computer and use it in GitHub Desktop.

Select an option

Save uwezi/b1a5376accbb21121e0b41e61d96c9e0 to your computer and use it in GitHub Desktop.
[optimal tiling?] Is this the optimal tiling of an area? #manim #geometry
class tile(Scene):
def construct(self):
for _ in range(10):
# Size rectangleSize = new Size(); # rectangle size will be used as a default value that is the exact aspect ratio desired.
#
# Aspect Ratio = h / w
# where h is the height of the child and w is the width
#
# the numerator will be the aspect ratio and the denominator will always be one
# if you want it to be square just use an aspect ratio of 1
parent = Rectangle(width=14, height=8)
rectangleSize = Rectangle(width=np.random.uniform(1,3))
desiredAspectRatio = rectangleSize.width/rectangleSize.height
numRectangles = np.random.random_integers(10,30)
# estimate of the number of columns useing the formula:
# n * W * h
# columns = SquareRoot( ------------- )
# H * w
#
# Where n is the number of items, W is the width of the parent, H is the height of the parent,
# h is the height of the child, and w is the width of the child
numColumns = np.sqrt( (numRectangles * rectangleSize.height * parent.width) / (parent.height * rectangleSize.width) )
lowBoundColumns = np.floor(numColumns)
highBoundColumns = np.ceil(numColumns)
# The number of rows is determined by finding the floor of the number of children divided by the columns
lowNumRows = np.ceil(numRectangles / lowBoundColumns)
highNumRows = np.ceil(numRectangles / highBoundColumns)
# Vertical Scale is what you multiply the vertical size of the child to find the expected area if you were to find
# the size of the rectangle by maximizing by rows
#
# H
# Vertical Scale = ----------
# R * h
#
# Where H is the height of the parent, R is the number of rows, and h is the height of the child
#
VerticalScale = parent.height / lowNumRows * rectangleSize.height
#Horizontal Scale is what you multiply the horizintale size of the child to find the expected area if you were to find
# the size of the rectangle by maximizing by columns
#
# W
# Vertical Scale = ----------
# c * w
#
#Where W is the width of the parent, c is the number of columns, and w is the width of the child
HorizontalScale = parent.width / (highBoundColumns * rectangleSize.width)
# The Max areas are what is used to determine if we should maximize over rows or columns
# The areas are found by multiplying the scale by the appropriate height or width and finding the area after the scale
#
# Horizontal Area = Sh * w * ( (Sh * w) / A )
#
# where Sh is the horizontal scale, w is the width of the child, and A is the aspect ratio of the child
#
MaxHorizontalArea = (HorizontalScale * rectangleSize.width) * ((HorizontalScale * rectangleSize.width) / desiredAspectRatio)
#
#
# Vertical Area = Sv * h * (Sv * h) * A
# Where Sv isthe vertical scale, h is the height of the child, and A is the aspect ratio of the child
#
MaxVerticalArea = (VerticalScale * rectangleSize.height) * ((VerticalScale * rectangleSize.height) * desiredAspectRatio)
if (MaxHorizontalArea >= MaxVerticalArea ): # the horizontal are is greater than the max area then we maximize by columns
# the width is determined by dividing the parent's width by the estimated number of columns
# this calculation will work for NEARLY all of the horizontal cases with only a few exceptions
newSize_Width = parent.width / highBoundColumns # we use highBoundColumns because that's what is used for the Horizontal
newSize_Height = newSize_Width / desiredAspectRatio # A = w/h or h= w/A
# In the cases that is doesnt work it is because the height of the new items is greater than the
# height of the parents. this only happens when transitioning to putting all the objects into
# only one row
if (newSize_Height * np.ceil(numRectangles / highBoundColumns) > parent.height):
#in this case the best solution is usually to maximize by rows instead
newHeight = parent.height / highNumRows
newWidth = newHeight * desiredAspectRatio
# However this doesn't always work because in one specific case the number of rows is more than actually needed
# and the width of the objects end up being smaller than the size of the parent because we don't have enough
# columns
if (newWidth * numRectangles < parent.width):
#When this is the case the best idea is to maximize over columns again but increment the columns by one
#This takes care of it for most cases for when this happens.
numColumns += 1
newWidth = parent.width / np.ceil(numColumns)
newHeight = newWidth / desiredAspectRatio
# in order to make sure the rectangles don't go over bounds we
# increment the number of columns until it is under bounds again.
while (newWidth * numRectangles > parent.width):
numColumns += 1
newWidth = parent.width / np.ceil(numColumns)
newHeight = newWidth / desiredAspectRatio;
# however after doing this it is possible to have the height too small.
# this will only happen if there is one row of objects. so the solution is to make the objects'
# height equal to the height of their parent
if (newHeight > parent.height):
newHeight = parent.height
newWidth = newHeight * desiredAspectRatio
# if we have a lot of added items occaisionally the previous checks will come very close to maximizing both columns and rows
# what happens in this case is that neither end up maximized
# because we don't know what set of rows and columns were used to get us to where we are
# we must recalculate them with the current measurements
currentCols = np.floor(parent.width / newWidth)
currentRows = np.ceil(numRectangles/currentCols)
# now we check and see if neither the rows or columns are maximized
if ( (newWidth * currentCols ) < parent.width and ( newHeight * np.ceil(numRectangles/currentCols) ) < parent.height):
# maximize by columns first
newWidth = parent.width / currentCols
newHeight = newSize_Width / desiredAspectRatio
# if the columns are over their bounds, then maximized by the columns instead
if (newHeight * np.ceil(numRectangles / currentCols) > parent.height):
newHeight = parent.height / currentRows
newWidth = newHeight * desiredAspectRatio
# finally we have the height of the objects as maximized using columns
newSize_Height = newHeight
newSize_Width = newWidth
else:
#Here we use the vertical scale. We determine the height of the objects based upong
# the estimated number of rows.
# This work for all known cases
newSize_Height = parent.height / lowNumRows
newSize_Width = newSize_Height * desiredAspectRatio
print(rectangleSize.width,rectangleSize.height)
print(newSize_Width,newSize_Height)
print(numRectangles)
print(lowNumRows,highNumRows)
print(numColumns)
rects = VGroup(
rectangleSize.copy().scale_to_fit_width(newSize_Width) for _ in range(numRectangles)
)
rows = int(lowNumRows)
cols = int(np.ceil(numRectangles/rows))
print(cols,rows)
rects.arrange_in_grid(cols = cols, rows = rows, buff=0)
self.play(Create(rects),run_time=0.5)
self.wait()
self.play(Uncreate(rects),run_time=0.5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment