How to create MS Paint clone with Python and pygame

Johnston picture Johnston · Feb 28, 2009 · Viewed 20.7k times · Source

As I see it, there are two ways to handle mouse events to draw a picture.

The first is to detect when the mouse moves and draw a line to where the mouse is, shown here. However, the problem with this is that with a large brush size, many gaps appear between each "line" that is not straight since it is using the line's stroke size to create thick lines.

The other way is to draw circles when the mouse moves as is shown here. The problem with this is that gaps appear between each circle if the mouse moves faster than the computer detects mouse input.

Here's a screenshot with my issues with both:

http://imgur.com/32DXN.jpg

What is the best way to implement a brush like MS Paint's, with a decently-big brush size with no gaps in the stroke of the line or no gaps between each circle?

Answer

SingleNegationElimination picture SingleNegationElimination · Feb 28, 2009

Why not do both?

Draw a circle at each endpoint and a line between the two.

EDIT rofl, just couldn't stop myself.

Actually, you don't want to use pygame.draw.line because it cheats. It fills a 1 pixel wide row or column (depending on angle of attack) of pixels. If you do go at a roughly perpendicular angle, 0 deg or 90 deg, this isn't an issue, but at 45's, you'll notice a sort of string bean effect.

The only solution is to draw a circle at every pixel's distance. Here...

import pygame, random

screen = pygame.display.set_mode((800,600))

draw_on = False
last_pos = (0, 0)
color = (255, 128, 0)
radius = 10

def roundline(srf, color, start, end, radius=1):
    dx = end[0]-start[0]
    dy = end[1]-start[1]
    distance = max(abs(dx), abs(dy))
    for i in range(distance):
        x = int( start[0]+float(i)/distance*dx)
        y = int( start[1]+float(i)/distance*dy)
        pygame.draw.circle(srf, color, (x, y), radius)

try:
    while True:
        e = pygame.event.wait()
        if e.type == pygame.QUIT:
            raise StopIteration
        if e.type == pygame.MOUSEBUTTONDOWN:
            color = (random.randrange(256), random.randrange(256), random.randrange(256))
            pygame.draw.circle(screen, color, e.pos, radius)
            draw_on = True
        if e.type == pygame.MOUSEBUTTONUP:
            draw_on = False
        if e.type == pygame.MOUSEMOTION:
            if draw_on:
                pygame.draw.circle(screen, color, e.pos, radius)
                roundline(screen, color, e.pos, last_pos,  radius)
            last_pos = e.pos
        pygame.display.flip()

except StopIteration:
    pass

pygame.quit()