How can I limit pygame.draw.circle?

2019-07-21 18:01发布

问题:

I have a draw area that I want to draw to and borders that I would not like to draw over. Currently, if the mouse positions (m_x & m_y) are within the circles radius of the border I have the program draw the circle and then redraw rectangles which cut off the part of the circle which crossed. There must be a smarter and more efficient way to draw only the parts of the circle which are within the border.

if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and mouse_pressed[0] == 1:
        if m_x < draw_areax-brush_size and m_y < draw_areay-brush_size:
            circle = pygame.draw.circle(screen,brush_colour,(m_x,m_y),brush_size)
        else:
            circle = pygame.draw.circle(screen,brush_colour,(m_x,m_y),brush_size)
            reloadareas()

回答1:

The documentation for pygame.draw says:

All the drawing functions respect the clip area for the Surface, and will be constrained to that area.

So if you want to draw only the parts of the circle that are within some rectangular region, then set up a clip area by calling pygame.Surface.set_clip, draw the circle, and then remove the clip area. Assuming that you do not normally have a clip area in effect on the screen, then you would program it like this:

clip_area = pygame.Rect(0, 0, draw_areax, draw_areay)
screen.set_clip(clip_area)
pygame.draw.circle(...)
screen.set_clip(None) # clear the clip area

Here's an example:

from pygame import *
init()
screen = display.set_mode((640, 480))

# Yellow circle drawn without clipping
draw.circle(screen, Color('yellow'), (150, 120), 60)

# Orange circle drawn with clipping
clip = Rect((100, 100, 200, 100))
screen.set_clip(clip)
draw.circle(screen, Color('orange'), (150, 120), 60)
screen.set_clip(None)

# Outline the clip rectangle in black
draw.rect(screen, Color('black'), clip, 1)
display.flip()

If you do a lot of drawing with a clip rectangle, then you might want to encapsulate the setting and unsetting of the clip rectangle in a context manager, perhaps like this, using contextlib.contextmanager:

from contextlib import contextmanager

@contextmanager
def clipped(surface, clip_rect):
    old_clip_rect = surface.get_clip()
    surface.set_clip(clip_rect)
    try:
        yield
    finally:
        surface.set_clip(old_clip_rect)

and then my example could be written like this:

# Orange circle drawn with clipping
clip = Rect((100, 100, 200, 100))
with clipped(screen, clip):
    draw.circle(screen, Color('orange'), (150, 120), 60)


回答2:

You have a bit of repetitive code in here. I would move the circle = statement out of the if/else block, reverse the ifs condition, and put reloadareas() in it:

if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and mouse_pressed[0] == 1:
    circle = pygame.draw.circle(screen,brush_colour,(m_x,m_y),brush_size)

    if m_x > draw_areax-brush_size or m_y > draw_areay-brush_size:
        reloadareas()


标签: python pygame