How to subtract geometry in pygame?

Question:

I am trying to make a logic in which i can subtract the geometrical shapes. Lets say i have to subtract a circle from a rectangle.

The idea is to draw each point of the rectangle as a single individual point using win.set_at(). And then when we need to subtract the circle from the rectangle we an simply calculate that the point on which we are drawing and completing our square lies or not in the circle. If it lies then don’t set pixels else draw the pixel. This is my code:


import pygame

WIDTH, HEIGHT = 700, 500

win = pygame.display.set_mode((WIDTH, HEIGHT))

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

def drawer(window, x, y, radius, s_l, s_w, s_x, s_y):
    for i in range(0, HEIGHT):
        for j in range(0, WIDTH):
            distance = (x - j)**2 + (y - i)**2
            if not distance <= radius**2 and i >= s_y and j >= s_x and i <= s_w + s_x and j <= s_l + s_y:
                window.set_at((j, i), BLACK)
    
c_x, c_y = 130, 150
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        
    win.fill(WHITE)
    drawer(win, c_x, c_y, 30, 100, 100, 100, 100)
    pygame.display.flip()

pygame.quit()

But is it is quite slow. Can you suggest a better way (more optimized algorithm) of doing it.

Asked By: Umer Asif

||

Answers:

For a good performance I suggest to use pygame.mask. Draw the shapes on pygame.Surface objects and create masks from the surfaces:

mask1 = pygame.mask.from_surface(shape1)
mask2 = pygame.mask.from_surface(shape2)

Invert the mask of the second shape and create the overlapping mask. Finally, create a new _Surface: from the overlapping mask:

mask2.invert()
subtract_maks = mask1.overlap_mask(mask2, (0, 0))
subtract_shape = subtract_maks.to_surface(setcolor = "red", unsetcolor=(0, 0, 0, 0))

Minimal example

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

shape1 = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.circle(shape1, "white", (100, 100), 100)
shape2 = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.rect(shape2, "white", (25, 50, 150, 100))

mask1 = pygame.mask.from_surface(shape1)
mask2 = pygame.mask.from_surface(shape2)

mask2.invert()
subtract_maks = mask1.overlap_mask(mask2, (0, 0))
subtract_shape = subtract_maks.to_surface(setcolor = "red", unsetcolor=(0, 0, 0, 0))

background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *background.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
[pygame.draw.rect(background, color, rect) for rect, color in tiles]
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            print(pygame.key.name(event.key))
 
    window.blit(background, (0, 0))
    window.blit(subtract_shape, (50, 50))
    pygame.display.flip()

pygame.quit()
exit()
Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.