Recursive Surfaces in Pygame

92 Views Asked by At

Suppose you have an app with a big gaping hole of negative space, so you want to fill it up with something... so why not fill the void with a copy of the app itself?

I have an app. It will have nested apps. Some apps are normal (i.e., rectangular), but (most) others are elliptical, or even triangular. There are connector layers for nesting the various combinations of app geometries.

While still in progress, there is a notion of somehow managing the ratio of positive space vs. negative space caused by such nesting. One idea I'm working on is subclassing those connectors to create more areas where there will be positive space, and dynamically selecting a subclass.

This will be able to give me a number of extra positive spaces-to-be, which need to be filled with something.

Based on: Pygame. How do I resize a surface and keep all objects within proportionate to the new window size?

1

There are 1 best solutions below

0
On

At a high level, we need to:

  1. take a copy of whatever is going to be fractalized
  2. compute where the fractalized image can be repeated (the bounding rectangles, here called "recursion points")
  3. create a temporary surface for drawing
  4. scale said copy to the size of the temp surface
  5. draw the scaled copy onto the temp surface

In other words:

def draw_background (self, temp):
    App.draw_background (self, temp) # default background for apps is to show a default image
    self.child.draw_scene (temp)                   # draw whatever is going to be fractalized   
def draw_foreground (self, temp):                    # fractalization function
    TR = temp.get_rect ()                            # bounding rect for parent
    X, Y, W, H = TR
    ts = pygame.Surface ((W, H))                     # get a fresh surface for working
        
    pic = temp.copy ()                               # could be anything
        
    for rp in self.recursion_points (temp):
        x, y, w, h = rp
        w, h = tr ((w, h))                           # round coordinates
        trans = pygame.transform.scale (pic, (w, h)) # scale fake screen to bounding rect
        ts.blit (trans, (x, y))                      # blit fake screen onto working surface
                
    temp.blit (ts, (X, Y))                           # blit working-surface onto real surface

Client code:

d = None                # default behavior for no app is to not display a foreground
# these adapters handle differing window geometries
c = CircledSquare (d, rotation=STRAIGHT)               # middle circle, inner square
b = SquaredCircle (c, background=SECONDARY_BACKGROUND) # outer square, middle circle
a = RecursiveComposite (b) # without this layer, the inner square is not visible... boring!

Geometry functions:

def recursive_affine (rect, dx, dy, rw, rh, n):
    x, y, w, h = rect
    for k in range (1, n + 1):
        dx, dy = dx * rw, dy * rh
        x,  y  =  x + dx,  y + dy
        w,  h  =  w * rw,  h * rh
        yield x, y, w, h
def recurse_point (rect, rp, minsz):
    X, Y, W, H = rect
    x, y, w, h = rp
    # get scale and offset for recursion point
    dx, dy = x - X, y - Y
    rw, rh = w / W, h / H
    # get number of recursions until < minsz
    f = lambda a, b, c: (log (a) - log (b)) / log (c)
    xmin, ymin = minsz
    xn, yn = f (xmin, w, rw), f (ymin, h, rh)
    n = min (xn, yn)
    n = ceil (n)
    # recursively apply scale and offset
    tail = recursive_affine (rp, dx, dy, rw, rh, n)
    return rp, *tail