Handling rectangle with negative width and height in pygame

Viewed 515

I was trying to make an area select feature where you hold left mouse button down and move the mouse and it makes a rectangle and everything inside that rectangle is selected.

import pygame
import time

window = pygame.display.set_mode((1200, 600))
thickest = 10
begin = False

while True:
    pygame.event.get()
    window.fill((255, 255, 255))

    if any(pygame.mouse.get_pressed()) and not begin:
        rpos = pygame.mouse.get_pos()
        begin = True

    if begin:
        pos2 = pygame.mouse.get_pos()
        pygame.draw.rect(window, (100, 200, 100), (rpos[0], rpos[1], pos2[0] - rpos[0], pos2[1] - rpos[1]), 1)

    if not any(pygame.mouse.get_pressed()) and begin:
        begin = False
    
    pygame.display.flip()

However, I noticed that a rectangle with negative height isn't drawn at all and one with negative width isn't even a rectangle.

One thing I thought of doing was to offset the position by size and have the size as an absolute value of size. Example in x-axis:

if pos2[0] - rpos[0] < 0:
    pygame.draw.rect(window, (100, 200, 100), (rpos[0] - (pos2[0] - rpos[0]), 
                                               rpos[1], 
                                               abs(pos2[0] - rpos[0]), 
                                               pos2[1] - rpos[1]))

But this approach doesn't seem elegant and I was wondering if there is a more pygame-ey way to handle this.

1 Answers

Use min to calculate the top left corner of the rectangle and abs to calculate the width and height of the rectangle:

rect = (
    min(pos2[0], rpos[0]), min(pos2[1], rpos[1]),
    abs(pos2[0]-rpos[0]), abs(pos2[1]-rpos[1])
)
pygame.draw.rect(window, (100, 200, 100), rect)

To draw an un-filled rectangle

rect = (
    min(pos2[0], rpos[0]),
    min(pos2[1], rpos[1]),
    max(2, abs(pos2[0]-rpos[0])),
    max(2, abs(pos2[1]-rpos[1])),
)        
pygame.draw.rect(window, (100, 200, 100), rect, 1)

pygame.Rect.normalize() offers an even more elegant solution to this problem:

normalize() -> None
This will flip the width or height of a rectangle if it has a negative size. The rectangle will remain in the same place, with only the sides swapped.

Create a pygame.Rect object and normalize it:

rect = pygame.Rect(rpos[0], rpos[1], pos2[0]-rpos[0], pos2[1]-rpos[1])
rect.normalize()
pygame.draw.rect(window, (100, 200, 100), rect)
Related