I am trying to make a brush function that draws onto a canvas with a calligraphy style of brush, thin one way, thick the other. Right now, the actual drawing of the brush footprint works, but the code does not run fast enough, and the actual line keeps cutting (as shown in the gif).
This is my code right now:
import pygame
import os
import random
from pygame.locals import *
flags = DOUBLEBUF
pygame.init()
pygame.event.set_allowed([QUIT])
current_path = os.path.dirname(__file__) #The directory the main file is in
iconPath = os.path.join(current_path, 'images') #The icon folder path
displayWidth = 1280
displayHeight = 720
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight), flags)
gameDisplay.set_alpha(None)
pygame.display.set_caption('PyPaint')
black = (0, 0, 0)
white = (255, 255, 255)
grey = (200, 200, 200)
cyan = (0, 200, 255)
green = (0, 150, 0)
lightGreen = (0, 255, 0)
red = (150, 0, 0)
lightRed = (255, 0, 0)
smallfont = pygame.font.SysFont("arial", 40)
medfont = pygame.font.SysFont("arial", 60)
largefont = pygame.font.SysFont("arial", 80)
airbrushIcon = pygame.image.load(os.path.join(iconPath, "airbrush.png"))
pencilIcon = pygame.image.load(os.path.join(iconPath, "pencil.png"))
calligraphyIcon = pygame.image.load(os.path.join(iconPath, "calligraphy.png"))
eraserIcon = pygame.image.load(os.path.join(iconPath, "eraser.png"))
clock = pygame.time.Clock()
FPS = 60
airbrushMode = False
calligraphyMode = False
eraserMode = False
def paintScreen():
global airbrushMode
global calligraphyMode
global eraserMode
airbrushMode = False
paint = True
gameDisplay.fill(cyan)
message_to_screen('Welcome to PyPaint', black, -300, 'large')
click = pygame.mouse.get_pressed()
pygame.draw.rect(gameDisplay, white, (50, 120, displayWidth - 100, displayHeight - 240))
while paint:
cur = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
button('X', 20, 20, 50, 50, red, lightRed, action = 'quit')
icon(airbrushIcon, white, 50, displayHeight - 101, 51, 51, white, grey, 'airbrush')
icon(pencilIcon, white, 140, displayHeight - 101, 51, 51, white, grey, 'pencil')
icon(calligraphyIcon, white, 230, displayHeight - 101, 51, 51, white, grey, 'calligraphy')
icon(eraserIcon, white, 320, displayHeight - 101, 51, 51, white, grey, 'eraser')
pygame.draw.rect(gameDisplay, cyan, (0, 120, 50, displayHeight - 100))#to clean up the left border of the canvas
pygame.draw.rect(gameDisplay, cyan, (displayWidth - 50, 120, 50, displayHeight - 100))#to clean up the right border of the canvas
pygame.draw.rect(gameDisplay, cyan, (0, displayHeight - 120, displayWidth, 20))#to clean up the bottom of the canvas
pygame.draw.rect(gameDisplay, cyan, (0, 100, displayWidth, 20))#to clean up the top of the canvas
if airbrushMode == True:
airbrush()
elif calligraphyMode == True:
calligraphy()
elif eraserMode == True:
eraser()
pygame.display.update()
def icon(icon, colour, x, y, width, height, inactiveColour, activeColour, action = None):
global airbrushMode
global calligraphyMode
global eraserMode
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + width > cur[0] > x and y + height > cur[1] > y:#if the cursor is over the button
pygame.draw.rect(gameDisplay, activeColour, (x, y, width, height))
gameDisplay.blit(icon, (x, y))
if click[0] == 1 and action != None: #if clicked
if action == 'quit':
pygame.quit()
quit()
elif action == 'pencil':
pencilMode = True
airbrushMode = False
calligraphyMode = False
eraserMode = False
elif action == 'airbrush':
airbrushMode = True
calligraphyMode = False
pencilMode = False
eraserMode = False
elif action == 'calligraphy':
calligraphyMode = True
airbrushMode = False
pencilMode = False
eraserMode = False
elif action == 'eraser':
eraserMode = True
airbrushMode = False
pencilMode = False
calligraphyMode = False
else:
pygame.draw.rect(gameDisplay, inactiveColour, (x, y, width, height))
gameDisplay.blit(icon, (x, y))
def button(text, x, y, width, height, inactiveColour, activeColour, action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + width > cur[0] > x and y + height > cur[1] > y:
pygame.draw.rect(gameDisplay, activeColour, (x, y, width, height))
if click[0] == 1 and action != None:
if action == 'quit':
pygame.quit()
quit()
else:
pygame.draw.rect(gameDisplay, inactiveColour, (x, y, width, height))
text_to_button(text, black, x, y, width, height)
def text_to_button(msg, colour, buttonx, buttony, buttonwidth, buttonheight, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = ((buttonx + (buttonwidth/2)), buttony + (buttonheight/2))
gameDisplay.blit(textSurf, textRect)
def message_to_screen(msg, colour, y_displace = 0, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = (displayWidth / 2), (displayHeight / 2) + y_displace
gameDisplay.blit(textSurf, textRect)
def airbrush(brushSize = 3):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if cur[0] >= 50 and cur[0] <= displayWidth - 50 and cur[1] >= 120 and cur[1] <= displayHeight - 120:
if click[0] == 1:
pygame.draw.circle(gameDisplay, black, (cur[0] + random.randrange(-brushSize * 2, brushSize * 2), cur[1] + random.randrange(-brushSize * 2, brushSize * 2)), random.randrange(1, brushSize * 2))
def calligraphy(brushSize = 3):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if cur[0] >= 50 and cur[0] <= displayWidth - 50 and cur[1] >= 120 and cur[1] <= displayHeight - 120:#if cursor is on the canvas
if click[0] == 1:
pygame.draw.rect(gameDisplay, black, (cur[0] - brushSize / 2, cur[1] - brushSize / 4, brushSize, brushSize * 3))
def eraser(brushSize = 3):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if cur[0] >= 50 and cur[0] <= displayWidth - 50 and cur[1] >= 120 and cur[1] <= displayHeight - 120:#if cursor is on the canvas
if click[0] == 1:
pygame.draw.rect(gameDisplay, white, (cur[0] - brushSize / 2, cur[1] - brushSize / 2, brushSize * 6, brushSize * 6))
def text_objects(text, colour, size):
if size == 'small':
textSurface = smallfont.render (text, True, colour)
elif size == 'medium':
textSurface = medfont.render (text, True, colour)
elif size == 'large':
textSurface = largefont.render (text, True, colour)
return textSurface, textSurface.get_rect()
paintScreen()
I tried adding clock.tick()
to several different functions to try to run it as fast as possible, but it still cuts the same way. I even tested it on a newer, faster computer, and there was no difference, which means that the issue lies within python and not the computer. How can I allow pygame to draw on all points on a line between 2 points?