Fill the area of intersection of two Circles in Pygame

Viewed 659

I am working on a pygame project and I encountered a problem. I have two circles A and B as shown below. Which are overlapped Now I want to fill only some part of the circle which is common to both circles. Is there any way to achieve that?

I know I can fill circle B with red colour and circle A with white, but there is a certain area and circle A goes outside that area. So I cannot implement that.

The image of circles and filled position is mentioned in this image: enter image description here

2 Answers

Create 2 pygame.Surface objects with per pixel alpha (pygame.SRCALPHA):

surf1 = pygame.Surface((500, 500), pygame.SRCALPHA)
surf2 = pygame.Surface((500, 500), pygame.SRCALPHA)

Define the center point and radius of the 2 circles:

pos1, rad1 = (250, 200), 100
pos2, rad2 = (250, 250), 80

Draw the circles on the 2 surfaces, with the same color:

pygame.draw.circle(surf1, (255, 0, 0, 255), pos1, rad1)
pygame.draw.circle(surf2, (255, 0, 0, 255), pos2, rad2)

Blend one surface on the other, using the blend mode pygame.BLEND_RGBA_MIN:

surf1.blit(surf2, (0, 0), special_flags = pygame.BLEND_RGBA_MIN)

At this point surf1 contains the intersection area of the 2 circles.

To get the outer section, another step is required. Blend surf1 on surf2, using the blend mode pygame.BLEND_RGBA_MIN:

surf2.blit(surf1, (0, 0), special_flags = pygame.BLEND_RGBA_SUB)

Now surf2 contains the the part of circle B which is left if circle A is subtracted:


Minimal example: repl.it/@Rabbid76/PyGame-CircleIntersection

import pygame

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

pos1, rad1 = (250, 200), 100
pos2, rad2 = (250, 250), 80

surf1 = pygame.Surface((500, 500), pygame.SRCALPHA)
surf2 = pygame.Surface((500, 500), pygame.SRCALPHA)

pygame.draw.circle(surf1, (255, 0, 0, 255), pos1, rad1)
pygame.draw.circle(surf2, (255, 0, 0, 255), pos2, rad2)

surf1.blit(surf2, (0, 0), special_flags = pygame.BLEND_RGBA_MIN)
surf2.blit(surf1, (0, 0), special_flags = pygame.BLEND_RGBA_SUB)

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.fill((255, 255, 255))

    window.blit(surf2, (0, 0))     
    pygame.draw.circle(window, (128, 128, 128), pos1, rad1+1, 3)
    pygame.draw.circle(window, (128, 128, 128), pos2, rad2+1, 3)

    pygame.display.flip()

I couldn't find a way to do it with pygame functions, so I made a quick demo on how to do it yourself here:

import pygame
import math
import time

# Screen dimensions
WIDTH = 600
HEIGHT = 400

# Circle and screen colours
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# Circle data
BIGGER_POS = (300, 150)
SMALLER_POS = (300, 220)
BIGGER_RADIUS = 130
SMALLER_RADIUS = 100
BIGGER_COLOUR = GREEN
SMALLER_COLOUR = BLUE
INTERSECTION_COLOUR = RED


def prepare_screen():
    """
    Create the initial screen.
    """
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    screen.fill(WHITE)
    return screen


def draw_circles(screen):
    """
    Draw the circles and wait to visually inspect what they look like.
    """
    bigger = pygame.draw.circle(screen, BIGGER_COLOUR, BIGGER_POS, BIGGER_RADIUS)
    smaller = pygame.draw.circle(screen, SMALLER_COLOUR, SMALLER_POS, SMALLER_RADIUS)
    pygame.display.update()
    time.sleep(3)
    return bigger, smaller


def calculate_distance(p1: tuple, p2: tuple):
    """
    Calculate distance between two points p1 and p2.
    """
    return math.sqrt(((p1[0] - p2[0])**2) + ((p1[1] - p2[1])**2))


def draw_intersection(screen, bigger, smaller):
    """
    Animation to mark intersecting points with red.
    
    Ideally this should only go through colliding points, but I'm lazy and made it iterate through all pixels
    instead.
    """
    for x in range(WIDTH):
        for y in range(HEIGHT):
            print("Checking point", x, y)
            
            # A point is in the intersection if it's within the radius-es of both circles
            if calculate_distance(bigger.center, (x, y)) <= BIGGER_RADIUS and \
                    calculate_distance(smaller.center, (x, y)) <= SMALLER_RADIUS:
                screen.set_at((x, y), INTERSECTION_COLOUR)
                pygame.display.update()


# Get screen to draw on
screen = prepare_screen()

# Draw some initial circles
bigger, smaller = draw_circles(screen)

# Draw the intersection
draw_intersection(screen, bigger, smaller)

# Finally sleep some to allow visual inspection, and quit
time.sleep(3)
pygame.quit()

Feel free to adjust as needed - note that various places can be optimised, made more readable etc.

Result:

Result of the code above

Let me know if anything's unclear.

Related