Untitled

 avatar
unknown
plain_text
a month ago
45 kB
5
Indexable
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw
import numpy as np
import scipy.ndimage

DEFAULT_PIXEL_SIZE = 10
DEFAULT_CANVAS_SIZE = 64
DEFAULT_FILL_COLOR = (30,120,130)

DEFAULT_BG_COLOR_RGB = (255, 255, 255)
DEFAULT_DRAW_COLOR_RGB = (0, 0, 0)
# DEFAULT_DRAW_PREVIEW_COLOR_RGB = (128, 128, 128)
DEFAULT_GRID_COLOR_HEX = "#9dc2c7"

class SimpleDrawingApp:
    def __init__(self, master, canvas_size=DEFAULT_CANVAS_SIZE, pixel_size=DEFAULT_PIXEL_SIZE):
        self.master = master
        self.canvas_size = canvas_size
        self.pixel_size = pixel_size
        self.actual_size = canvas_size * pixel_size
        self.color = DEFAULT_DRAW_COLOR_RGB
        self.show_grid = tk.BooleanVar(value=False)  # Track if grid is shown
        self.kreslenie = tk.IntVar(value = 0)
        self.points = []
        self.temppoints = [0]

        # Initialize the Pillow image and draw object
        self.image = Image.new('RGB', (self.actual_size, self.actual_size), DEFAULT_BG_COLOR_RGB)
        self.draw = ImageDraw.Draw(self.image)

        # Add buttons and checkbox
        self.setup_controls()

        # Create a Tkinter canvas to display the image
        self.canvas = tk.Canvas(master, width=self.actual_size, height=self.actual_size, highlightthickness = 0)
        self.canvas.pack()

        # Bind mouse events
        self.canvas.bind('<B1-Motion>', self.paint)
        self.update_canvas()

        self.menu_bar = tk.Menu(self.master)
        self.filter_menu = tk.Menu(self.menu_bar, tearoff=True)
        self.filter_menu.add_command(label="Averaging Blur", command=self.averaging_blur)
        self.filter_menu.add_command(label="Gaussian Blur", command=self.gaussian_blur)
        self.filter_menu.add_command(label="Sobel Filter", command=self.sobel_filter)
        self.filter_menu.add_command(label="Sharpen Filter", command=self.sharpen_filter)
        self.menu_bar.add_cascade(label="Filters", menu=self.filter_menu)
        self.master.config(menu=self.menu_bar)

    def paint_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.bind('<Button-1>', self.paint)
        self.canvas.bind('<B1-Motion>', self.paint)
    
    def DDA_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.bind('<Button-1>', self.press)
        self.canvas.bind('<B1-Motion>', self.DDA)
        self.canvas.bind('<ButtonRelease-1>', self.DDA_release)
        self.bezierzrus()

    def Bresenhamline_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.bind('<Button-1>', self.press)
        self.canvas.bind('<B1-Motion>', self.Bresenham_line)
        self.canvas.bind('<ButtonRelease-1>', self.Bresenham_line_release)
        self.bezierzrus()

    def Bresenhamcircle_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.bind('<Button-1>', self.press)
        self.canvas.bind('<B1-Motion>', self.Bresenham_circle)
        self.canvas.bind('<ButtonRelease-1>', self.Bresenham_circle_release)
        self.bezierzrus()

    def Beziercurve_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.focus_set()
        self.canvas.bind('<Button - 1>', self.pridajtempbod)
        self.canvas.bind('<B1-Motion>', self.pridajtempbod)
        self.canvas.bind('<ButtonRelease-1>', self.pridajbod)
        self.canvas.bind('<Return>', self.bezierkoniec)
        self.canvas.bind('<Escape>', self.bezierzrus)
    
    def Floodfill_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.bind('<Button - 1>', self.floodfill_begin)
        self.bezierzrus()
    
    def Polygon_bind(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.canvas.focus_set()
        self.canvas.bind('<Button - 1>', self.pridajtempbodpoly)
        self.canvas.bind('<B1-Motion>', self.pridajtempbodpoly)
        self.canvas.bind('<ButtonRelease-1>', self.pridajbodpoly)
        self.canvas.bind('<Escape>', self.polyzrus)
        self.bezierzrus()

    def setup_controls(self):
        # Create a frame to hold the controls
        controls_frame = tk.Frame(self.master)
        controls_frame.pack(side=tk.TOP, fill=tk.X)

        # Clear button
        clear_button = tk.Button(controls_frame, text="Clear", command=self.clear_canvas)
        clear_button.grid(row=0, column=0, padx=5, pady=5)

        grid_checkbox = tk.Checkbutton(controls_frame, text="Grid", variable=self.show_grid, command=self.toggle_grid)
        grid_checkbox.grid(row=0, column=1, padx=5, pady=5)

        brush_checkbox = tk.Radiobutton(controls_frame, text="Brush", variable=self.kreslenie, value=1, command=self.paint_bind)
        brush_checkbox.grid(row=0, column=2, padx=5, pady=5)
            
        DDA_checkbox = tk.Radiobutton(controls_frame, text="DDA line", variable=self.kreslenie, value=2, command=self.DDA_bind)
        DDA_checkbox.grid(row=0, column=3, padx=5, pady=5)
            
        Bresenhamline_checkbox = tk.Radiobutton(controls_frame, text="Bresenham line", variable=self.kreslenie, value=3, command=self.Bresenhamline_bind)
        Bresenhamline_checkbox.grid(row=0, column=4, padx=5, pady=5)
            
        Bresenhamcircle_checkbox = tk.Radiobutton(controls_frame, text='Bresenham circle', variable=self.kreslenie, value = 4, command=self.Bresenhamcircle_bind)
        Bresenhamcircle_checkbox.grid(row=0, column=5, padx=5, pady=5)

        Beziercurve_checkbox = tk.Radiobutton(controls_frame, text='Besier curve', variable=self.kreslenie, value = 5, command=self.Beziercurve_bind)
        Beziercurve_checkbox.grid(row=1, column=2, padx=5, pady=5)

        Floodfill_checkbox = tk.Radiobutton(controls_frame, text='Flood fill', variable=self.kreslenie, value = 6, command=self.Floodfill_bind)
        Floodfill_checkbox.grid(row=1, column=3, padx=5, pady=5)

        Polygon_checkbox = tk.Radiobutton(controls_frame, text='Polygon', variable=self.kreslenie, value = 7, command=self.Polygon_bind)
        Polygon_checkbox.grid(row=1, column=4, padx=5, pady=5)



    def averaging_blur(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.kreslenie.set(0)
        matica = np.array(self.image, dtype=np.float32) 
        pixely = np.zeros((self.canvas_size, self.canvas_size,3), dtype=np.float32)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                pixely[i, j] = matica[i*self.pixel_size+self.pixel_size//2, j*self.pixel_size+self.pixel_size//2]
        kernel = np.array([[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]], dtype=np.float32)
        rozmenit = scipy.ndimage.convolve(pixely, kernel[..., None], mode='mirror')
        rozmenit = np.clip(rozmenit, 0, 255).astype(np.uint8)
        obrazok = np.zeros((self.actual_size,self.actual_size,3),  dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                obrazok[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size] = rozmenit[i, j]
        self.image = Image.fromarray(obrazok)
        self.update_canvas()
 
    def gaussian_blur(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.kreslenie.set(0)
        matica = np.array(self.image, dtype=np.float32) 
        pixely = np.zeros((self.canvas_size, self.canvas_size,3), dtype=np.float32)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                kde = matica[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size]
                pixely[i, j] = np.mean(kde, axis = (0, 1))
        kernel = np.array([[1/16,1/8,1/16],[1/8,1/4,1/8],[1/16,1/8,1/16]], dtype=np.float32)
        rozmenit = np.zeros((self.canvas_size, self.canvas_size,3), dtype=np.float32)
        rozmenit = scipy.ndimage.convolve(pixely, kernel[..., None], mode='mirror')
        rozmenit = np.clip(rozmenit, 0, 255).astype(np.uint8)
        obrazok = np.zeros((self.actual_size,self.actual_size,3),  dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                obrazok[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size] = rozmenit[i, j]
        self.image = Image.fromarray(obrazok)
        self.update_canvas()

    def sobel_filter(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.kreslenie.set(0)
        matica = np.array(self.image, dtype=np.float32)
        pixely = np.zeros((self.canvas_size, self.canvas_size, 3), dtype=np.float32)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                kde = matica[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size]
                pixely[i, j] = np.mean(kde, axis=(0, 1))
        grayscale = np.mean(pixely, axis=2)
        kernelx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float32)
        kernely = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=np.float32)
        gx = scipy.ndimage.convolve(grayscale, kernelx, mode='mirror')
        gy = scipy.ndimage.convolve(grayscale, kernely, mode='mirror')
        gradient_magnitude = np.sqrt(gx**2 + gy**2)
        max_val = gradient_magnitude.max()
        if max_val > 0:
            gradient_magnitude = (gradient_magnitude * (255.0 / max_val)).astype(np.float32)
        else:
            gradient_magnitude = gradient_magnitude.astype(np.uint32)
        gradient_magnitude = np.clip(gradient_magnitude, 0, 255).astype(np.uint8)
        obrazok = np.zeros((self.actual_size, self.actual_size, 3), dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                farba = gradient_magnitude[i, j]
                obrazok[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size] = (farba, farba,farba)
        self.image = Image.fromarray(obrazok)
        self.update_canvas()

    def sharpen_filter(self):
        self.canvas.unbind("<B1-Motion>")
        self.canvas.unbind("<Button-1>")
        self.canvas.unbind("<ButtonRelease-1>")
        self.canvas.unbind('<Return>')
        self.canvas.unbind('<Escape>')
        self.kreslenie.set(0)
        matica = np.array(self.image, dtype=np.float32) 
        pixely = np.zeros((self.canvas_size, self.canvas_size,3), dtype=np.float32)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                kde = matica[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size]
                pixely[i, j] = np.mean(kde, axis = (0, 1))
        kernel = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]], dtype=np.float32)
        rozmenit = np.zeros((self.canvas_size, self.canvas_size,3), dtype=np.float32)
        rozmenit = scipy.ndimage.convolve(pixely, kernel[..., None], mode='mirror')
        rozmenit = np.clip(rozmenit, 0, 255).astype(np.uint8)
        obrazok = np.zeros((self.actual_size,self.actual_size,3),  dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                obrazok[i * self.pixel_size:(i + 1) * self.pixel_size, j * self.pixel_size:(j + 1) * self.pixel_size] = rozmenit[i, j]
        self.image = Image.fromarray(obrazok)
        self.update_canvas()
  
    def draw_pixel(self, x, y, color):
        # Calculate the top-left and bottom-right corners of the enlarged pixel
        top_left = (x * self.pixel_size, y * self.pixel_size)
        bottom_right = ((x + 1) * self.pixel_size - 1, (y + 1) * self.pixel_size - 1)
        self.draw.rectangle([top_left, bottom_right], fill=color)
        self.update_canvas()

    def paint(self, event):
        # Calculate the logical pixel position
        self.kopia = self.image.copy()
        self.paintx = event.x//self.pixel_size
        self.painty = event.y//self.pixel_size
        temp_draw = ImageDraw.Draw(self.kopia)
        temp_draw.rectangle([((self.paintx) * self.pixel_size, (self.painty) * self.pixel_size), (((self.paintx) + 1) * self.pixel_size - 1, ((self.painty) + 1) * self.pixel_size - 1)], fill='black')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia 


    def press(self,event):
        self.kopia = self.image.copy()
        self.x1 = int(event.x // self.pixel_size)
        self.y1 = int(event.y // self.pixel_size)
    
    def DDA(self, event):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        self.DDAxt = int(event.x // self.pixel_size)
        self.DDAyt = int(event.y // self.pixel_size)
        self.dx = self.DDAxt - self.x1
        self.dy = self.DDAyt - self.y1

        self.dlzkapixelov = max(abs(self.dx), abs(self.dy))
        if self.dlzkapixelov != 0:
            self.prirastokx = self.dx / self.dlzkapixelov
            self.prirastoky = self.dy / self.dlzkapixelov
        else:
            self.prirastokx = 0
            self.prirastoky = 0

        self.DDAx = self.x1
        self.DDAy = self.y1

        for i in range(0, self.dlzkapixelov + 1):
            temp_draw.rectangle([(round(self.DDAx) * self.pixel_size, round(self.DDAy) * self.pixel_size), ((round(self.DDAx) + 1) * self.pixel_size, (round(self.DDAy) + 1) * self.pixel_size)], fill='grey')
            self.DDAx = self.DDAx + self.prirastokx
            self.DDAy = self.DDAy + self.prirastoky
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)    

    
    def DDA_release(self,event):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        self.DDAx2 = int(event.x // self.pixel_size)
        self.DDAy2 = int(event.y // self.pixel_size)
        self.dx = self.DDAx2 - self.x1
        self.dy = self.DDAy2 - self.y1

        self.dlzkapixelov = max(abs(self.dx), abs(self.dy))
        if self.dlzkapixelov != 0:
            self.prirastokx = self.dx / self.dlzkapixelov
            self.prirastoky = self.dy / self.dlzkapixelov
        else:
            self.prirastokx = 0
            self.prirastoky = 0

        self.DDAx = self.x1
        self.DDAy = self.y1

        for i in range(0, self.dlzkapixelov + 1):
            temp_draw.rectangle([(round(self.DDAx) * self.pixel_size, round(self.DDAy) * self.pixel_size), ((round(self.DDAx) + 1) * self.pixel_size, (round(self.DDAy) + 1) * self.pixel_size)], fill='black')
            self.DDAx = self.DDAx + self.prirastokx
            self.DDAy = self.DDAy + self.prirastoky
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)

        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia

    def Bresenham_line(self,event):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        self.Bresxt = event.x // self.pixel_size
        self.Bresyt = event.y // self.pixel_size
        self.Bresdx = self.Bresxt - self.x1
        self.Bresdy = self.Bresyt - self.y1
        self.krokx = 1 if self.Bresdx>=0 else -1
        self.kroky = 1 if self.Bresdy>=0 else -1
        self.chyba = abs(self.Bresdx)-abs(self.Bresdy)
        self.Bresx = self.x1
        self.Bresy = self.y1
        while self.Bresx != self.Bresxt or self.Bresy != self.Bresyt:
            temp_draw.rectangle([((self.Bresx) * self.pixel_size, (self.Bresy) * self.pixel_size), 
                         (((self.Bresx) + 1) * self.pixel_size - 1, 
                          ((self.Bresy) + 1) * self.pixel_size - 1)], fill='grey')
            self.chyba2 = 2* self.chyba
            if self.chyba2 > -abs(self.Bresdy):
                self.chyba = self.chyba - abs(self.Bresdy)
                self.Bresx = self.Bresx + self.krokx
            if self.chyba2 < abs(self.Bresdx):
                self.chyba = self.chyba + abs(self.Bresdx)
                self.Bresy = self.Bresy + self.kroky
        temp_draw.rectangle([((self.Bresxt) * self.pixel_size, (self.Bresyt) * self.pixel_size), 
                         (((self.Bresxt) + 1) * self.pixel_size - 1, 
                          ((self.Bresyt) + 1) * self.pixel_size - 1)], fill='grey')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
    
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)


    def Bresenham_line_release(self,event):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        self.Bresx2 = event.x // self.pixel_size
        self.Bresy2 = event.y // self.pixel_size
        self.Bresdx = self.Bresx2 - self.x1
        self.Bresdy = self.Bresy2 - self.y1
        self.krokx = 1 if self.Bresdx>=0 else -1
        self.kroky = 1 if self.Bresdy>=0 else -1
        self.chyba = abs(self.Bresdx)-abs(self.Bresdy)
        self.Bresx = self.x1
        self.Bresy = self.y1
        while self.Bresx != self.Bresx2 or self.Bresy != self.Bresy2:
            temp_draw.rectangle([((self.Bresx) * self.pixel_size, (self.Bresy) * self.pixel_size), 
                         (((self.Bresx) + 1) * self.pixel_size - 1, 
                          ((self.Bresy) + 1) * self.pixel_size - 1)], fill='black')
            self.chyba2 = 2* self.chyba
            if self.chyba2 > -abs(self.Bresdy):
                self.chyba = self.chyba - abs(self.Bresdy)
                self.Bresx = self.Bresx + self.krokx
            if self.chyba2 < abs(self.Bresdx):
                self.chyba = self.chyba + abs(self.Bresdx)
                self.Bresy = self.Bresy + self.kroky
        temp_draw.rectangle([((self.Bresx2) * self.pixel_size, (self.Bresy2) * self.pixel_size), 
                         (((self.Bresx2) + 1) * self.pixel_size - 1, 
                          ((self.Bresy2) + 1) * self.pixel_size - 1)], fill='black')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
    
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia

    def Bresenham_circle(self,event):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        
        self.Brescxt = event.x // self.pixel_size
        self.Brescyt = event.y // self.pixel_size
        self.r = round(((self.Brescxt - self.x1) ** 2 + (self.Brescyt - self.y1) ** 2) ** 0.5)
        self.xc = self.x1
        self.yc = self.y1
        self.x = 0
        self.y = self.r
        self.p = 1 - self.r
        
        while self.x <= self.y:
            temp_draw.rectangle([(int(self.xc + self.x) * self.pixel_size, int(self.yc + self.y) * self.pixel_size), ((int(self.xc + self.x) + 1) * self.pixel_size - 1, (int(self.yc + self.y) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc - self.x) * self.pixel_size, int(self.yc + self.y) * self.pixel_size), ((int(self.xc - self.x) + 1) * self.pixel_size - 1, (int(self.yc + self.y) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc + self.x) * self.pixel_size, int(self.yc - self.y) * self.pixel_size), ((int(self.xc + self.x) + 1) * self.pixel_size - 1, (int(self.yc - self.y) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc - self.x) * self.pixel_size, int(self.yc - self.y) * self.pixel_size), ((int(self.xc - self.x) + 1) * self.pixel_size - 1, (int(self.yc - self.y) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc + self.y) * self.pixel_size, int(self.yc + self.x) * self.pixel_size), ((int(self.xc + self.y) + 1) * self.pixel_size - 1, (int(self.yc + self.x) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc - self.y) * self.pixel_size, int(self.yc + self.x) * self.pixel_size), ((int(self.xc - self.y) + 1) * self.pixel_size - 1, (int(self.yc + self.x) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc + self.y) * self.pixel_size, int(self.yc - self.x) * self.pixel_size), ((int(self.xc + self.y) + 1) * self.pixel_size - 1, (int(self.yc - self.x) + 1) * self.pixel_size - 1)], fill='grey')
            temp_draw.rectangle([(int(self.xc - self.y) * self.pixel_size, int(self.yc - self.x) * self.pixel_size), ((int(self.xc - self.y) + 1) * self.pixel_size - 1, (int(self.yc - self.x) + 1) * self.pixel_size - 1)], fill='grey')
            self.x = self.x + 1
            if self.p < 0:
                self.p = self.p+ 2 * self.x + 1
            else:
                self.y =self.y - 1
                self.p = self.p +  2 * (self.x - self.y) + 1
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)     
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)

    def Bresenham_circle_release(self,event):
        self.kopia = self.image.copy()
        final_draw = ImageDraw.Draw(self.kopia)
        self.Brescx2 = event.x // self.pixel_size
        self.Brescy2 = event.y // self.pixel_size
        self.r = round(((self.Brescx2 - self.x1) ** 2 + (self.Brescy2 - self.y1) ** 2) ** 0.5)
        self.xc = self.x1
        self.yc = self.y1
        
        self.x = 0
        self.y = self.r
        self.p = 1 -  self.r 
        while self.x <= self.y:
            final_draw.rectangle([(int(self.xc + self.x) * self.pixel_size, int(self.yc + self.y) * self.pixel_size), ((int(self.xc + self.x) + 1) * self.pixel_size - 1, (int(self.yc + self.y) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc - self.x) * self.pixel_size, int(self.yc + self.y) * self.pixel_size), ((int(self.xc - self.x) + 1) * self.pixel_size - 1, (int(self.yc + self.y) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc + self.x) * self.pixel_size, int(self.yc - self.y) * self.pixel_size), ((int(self.xc + self.x) + 1) * self.pixel_size - 1, (int(self.yc - self.y) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc - self.x) * self.pixel_size, int(self.yc - self.y) * self.pixel_size), ((int(self.xc - self.x) + 1) * self.pixel_size - 1, (int(self.yc - self.y) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc + self.y) * self.pixel_size, int(self.yc + self.x) * self.pixel_size), ((int(self.xc + self.y) + 1) * self.pixel_size - 1, (int(self.yc + self.x) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc - self.y) * self.pixel_size, int(self.yc + self.x) * self.pixel_size), ((int(self.xc - self.y) + 1) * self.pixel_size - 1, (int(self.yc + self.x) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc + self.y) * self.pixel_size, int(self.yc - self.x) * self.pixel_size), ((int(self.xc + self.y) + 1) * self.pixel_size - 1, (int(self.yc - self.x) + 1) * self.pixel_size - 1)], fill='black')
            final_draw.rectangle([(int(self.xc - self.y) * self.pixel_size, int(self.yc - self.x) * self.pixel_size), ((int(self.xc - self.y) + 1) * self.pixel_size - 1, (int(self.yc - self.x) + 1) * self.pixel_size - 1)], fill='black')
            self.x = self.x + 1
            if self.p < 0:
                self.p = self.p+ 2 * self.x + 1
            else:
                self.y =self.y - 1
                self.p = self.p +  2 * (self.x - self.y) + 1
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia
    
    def pridajbod(self, event):
        x, y = event.x // self.pixel_size, event.y // self.pixel_size
        self.points.append((x, y))
        self.temppoints = self.points + [0]
    
    def pridajtempbod(self, event):
        self.temppoints.pop()
        x, y = event.x // self.pixel_size, event.y // self.pixel_size
        self.temppoints.append((x,y))
        self.bezierkrivkatemp(self.temppoints)

    def bezierkrivka(self, points):
        self.kopia = self.image.copy()
        final_draw = ImageDraw.Draw(self.kopia)
        for t in [i / 600 for i in range(600 + 1)]:
            x, y = self.decasteljeau(points, t)
            final_draw.rectangle([(round(x) * self.pixel_size, round(y) * self.pixel_size),(round(x) * self.pixel_size + self.pixel_size, round(y) * self.pixel_size + self.pixel_size)],fill='black')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color) 
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia

    def bezierkrivkatemp(self, temppoints):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        for t in [i / 600 for i in range(600 + 1)]:
            x, y = self.tempdecasteljeau(temppoints, t)
            temp_draw.rectangle([(round(x) * self.pixel_size, round(y) * self.pixel_size),(round(x) * self.pixel_size + self.pixel_size, round(y) * self.pixel_size + self.pixel_size)],fill='grey')
        for i in range (len(temppoints)-1):
            temp_draw.line([temppoints[i][0]* self.pixel_size,temppoints[i][1]* self.pixel_size,temppoints[i+1][0]* self.pixel_size,temppoints[i+1][1]* self.pixel_size],width=3,fill='black')
            temp_draw.ellipse([temppoints[i][0]* self.pixel_size-8,temppoints[i][1]* self.pixel_size-8,temppoints[i][0]* self.pixel_size+8,temppoints[i][1]* self.pixel_size+8],outline='blue',fill='white')
        temp_draw.ellipse([temppoints[len(temppoints)-1][0]* self.pixel_size-8,temppoints[len(temppoints)-1][1]* self.pixel_size-8,temppoints[len(temppoints)-1][0]* self.pixel_size+8,temppoints[len(temppoints)-1][1]* self.pixel_size+8],outline='red',fill='white')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color) 
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        
    
    def tempdecasteljeau(self, temppoints, t):
        if len(temppoints) == 1:
            return temppoints[0]
        new_temppoints = []
        for i in range(len(temppoints) - 1):  
            x = temppoints[i][0] + t *( temppoints[i + 1][0] - temppoints[i][0])
            y = temppoints[i][1] + t *( temppoints[i + 1][1] - temppoints[i][1])
            new_temppoints.append((x, y))
        return self.tempdecasteljeau(new_temppoints, t)

    def decasteljeau(self, points, t):
        if len(points) == 1:
            return points[0]
        new_points = []
        for i in range(len(points) - 1):  
            x = points[i][0] + t *( points[i + 1][0] - points[i][0])
            y = points[i][1] + t *( points[i + 1][1] - points[i][1])
            new_points.append((x, y))
        return self.decasteljeau(new_points, t)


    def tempbezierkoniec(self):
        self.bezierkrivkatemp(self.temppoints)

    def bezierkoniec(self, event = None):
        if len(self.points) > 0:
            self.bezierkrivka(self.points)
            self.points = []
            self.temppoints = [0]
        else:
            pass

    def bezierzrus(self, event = None):
        self.points = []
        self.temppoints = [0]
        self.kopiac = self.image.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)

    def floodfill_begin(self, event):
        global mamka, x1, x2
        matica = np.array(self.image, dtype=np.uint8)
        pixely = np.zeros((self.canvas_size, self.canvas_size, 3), dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                pixely[i, j] = matica[i * self.pixel_size + self.pixel_size // 2, j * self.pixel_size + self.pixel_size // 2]
        x1 = event.y // self.pixel_size
        x2 = event.x // self.pixel_size
        mamka = pixely[x1, x2]
        self.floodfill()

    def floodfill(self):
        global mamka, x1, x2
        matica = np.array(self.image, dtype=np.uint8)
        pixely = np.zeros((self.canvas_size, self.canvas_size, 3), dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                pixely[i, j] = matica[i * self.pixel_size + self.pixel_size // 2, j * self.pixel_size + self.pixel_size // 2]
        pixely[x1,x2] = DEFAULT_FILL_COLOR
        zasobnik  = [(x1, x2)]
        ssd = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        while zasobnik:
            x, y = zasobnik.pop()
            for dx, dy in ssd:
                novex = x + dx
                novey = y + dy
                if 0 <= novex < self.canvas_size and 0 <= novey < self.canvas_size:
                    if np.all(pixely[novex, novey] == mamka):
                        pixely[novex, novey] = DEFAULT_FILL_COLOR
                        zasobnik.append((novex, novey))

        obrazok = np.zeros((self.actual_size, self.actual_size, 3), dtype=np.uint8)
        for i in range(self.canvas_size):
            for j in range(self.canvas_size):
                obrazok[i * self.pixel_size:(i + 1) * self.pixel_size, 
                        j * self.pixel_size:(j + 1) * self.pixel_size] = pixely[i, j]
        self.image = Image.fromarray(obrazok)
        self.update_canvas()

    def pridajbodpoly(self, event):
        x, y = event.x // self.pixel_size, event.y // self.pixel_size
        self.points.append((x, y))
        self.temppoints = self.points + [0]
        self.zaciatokx = self.points[0][0]
        self.zaciatoky = self.points[0][1]
        self.polykoniec(self.points)
    
    def pridajtempbodpoly(self, event):
        self.temppoints.pop()
        x, y = event.x // self.pixel_size, event.y // self.pixel_size
        self.temppoints.append((x,y))
        self.polytemp(self.temppoints)

    def poly(self, points):
        self.kopia = self.image.copy()
        final_draw = ImageDraw.Draw(self.kopia)
        for i in range (1, len(points)):
            self.Bresxt = points[i][0]
            self.Bresyt = points[i][1]
            self.Bresx1 = points[i-1][0]
            self.Bresy1 = points[i-1][1]
            self.Bresdx = self.Bresxt - self.Bresx1
            self.Bresdy = self.Bresyt - self.Bresy1
            self.krokx = 1 if self.Bresdx>=0 else -1
            self.kroky = 1 if self.Bresdy>=0 else -1
            self.chyba = abs(self.Bresdx)-abs(self.Bresdy)
            self.Bresx = self.Bresx1
            self.Bresy = self.Bresy1
            while self.Bresx != self.Bresxt or self.Bresy != self.Bresyt:
                final_draw.rectangle([((self.Bresx) * self.pixel_size, (self.Bresy) * self.pixel_size), 
                            (((self.Bresx) + 1) * self.pixel_size - 1, 
                            ((self.Bresy) + 1) * self.pixel_size - 1)], fill='black')
                self.chyba2 = 2* self.chyba
                if self.chyba2 > -abs(self.Bresdy):
                    self.chyba = self.chyba - abs(self.Bresdy)
                    self.Bresx = self.Bresx + self.krokx
                if self.chyba2 < abs(self.Bresdx):
                    self.chyba = self.chyba + abs(self.Bresdx)
                    self.Bresy = self.Bresy + self.kroky
            final_draw.rectangle([((self.Bresxt) * self.pixel_size, (self.Bresyt) * self.pixel_size), 
                            (((self.Bresxt) + 1) * self.pixel_size - 1, 
                            ((self.Bresyt) + 1) * self.pixel_size - 1)], fill='black')
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color) 
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image = self.kopia

    def polytemp(self, temppoints):
        self.kopia = self.image.copy()
        temp_draw = ImageDraw.Draw(self.kopia)
        for i in range (1, len(temppoints)):
            self.Bresxt = temppoints[i][0]
            self.Bresyt = temppoints[i][1]
            self.Bresx1 = temppoints[i-1][0]
            self.Bresy1 = temppoints[i-1][1]
            self.Bresdx = self.Bresxt - self.Bresx1
            self.Bresdy = self.Bresyt - self.Bresy1
            self.krokx = 1 if self.Bresdx>=0 else -1
            self.kroky = 1 if self.Bresdy>=0 else -1
            self.chyba = abs(self.Bresdx)-abs(self.Bresdy)
            self.Bresx = self.Bresx1
            self.Bresy = self.Bresy1
            while self.Bresx != self.Bresxt or self.Bresy != self.Bresyt:
                temp_draw.rectangle([((self.Bresx) * self.pixel_size, (self.Bresy) * self.pixel_size), 
                            (((self.Bresx) + 1) * self.pixel_size - 1, 
                            ((self.Bresy) + 1) * self.pixel_size - 1)], fill='grey')
                self.chyba2 = 2* self.chyba
                if self.chyba2 > -abs(self.Bresdy):
                    self.chyba = self.chyba - abs(self.Bresdy)
                    self.Bresx = self.Bresx + self.krokx
                if self.chyba2 < abs(self.Bresdx):
                    self.chyba = self.chyba + abs(self.Bresdx)
                    self.Bresy = self.Bresy + self.kroky
            temp_draw.rectangle([((self.Bresxt) * self.pixel_size, (self.Bresyt) * self.pixel_size), 
                            (((self.Bresxt) + 1) * self.pixel_size - 1, 
                            ((self.Bresyt) + 1) * self.pixel_size - 1)], fill='grey')
        for i in range (len(temppoints)-1):
            temp_draw.ellipse([temppoints[i][0]* self.pixel_size-8,temppoints[i][1]* self.pixel_size-8,temppoints[i][0]* self.pixel_size+8,temppoints[i][1]* self.pixel_size+8],outline='blue',fill='white')
        temp_draw.ellipse([temppoints[len(temppoints)-1][0]* self.pixel_size-8,temppoints[len(temppoints)-1][1]* self.pixel_size-8,temppoints[len(temppoints)-1][0]* self.pixel_size+8,temppoints[len(temppoints)-1][1]* self.pixel_size+8],outline='red',fill='white')
        self.polykoniec(self.points)
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color) 
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)

    def scanline_fill(self, points):
        self.kopia = self.image.copy()
        final_draw = ImageDraw.Draw(self.kopia)
        if len(points) < 3:
            return
        min_y = min(p[1] for p in points)
        max_y = max(p[1] for p in points)
        edge_table = {}
        for i in range(len(points)):
            x1, y1 = points[i]
            x2, y2 = points[(i + 1) % len(points)]
            if y1 == y2:
                continue
            if y1 > y2:
                x1, y1, x2, y2 = x2, y2, x1, y1
            
            slope = (x2 - x1) / (y2 - y1)
            edge_table.setdefault(y1, []).append((x1, y2, slope))
        active_edges = []
        for y in range(min_y, max_y + 1):
            if y in edge_table:
                active_edges.extend(edge_table[y])
            active_edges = [(x, y_max, slope) for (x, y_max, slope) in active_edges if y < y_max]
            active_edges.sort()
            
            for i in range(0, len(active_edges), 2):
                x_start = int(active_edges[i][0])
                x_end = int(active_edges[i + 1][0])
                for x in range(x_start+1, x_end+1):
                    final_draw.rectangle([(x * self.pixel_size, y * self.pixel_size), 
                            ((x + 1) * self.pixel_size)-1, 
                            ((y + 1) * self.pixel_size)-1], fill=DEFAULT_FILL_COLOR)
            active_edges = [(x + slope, y_max, slope) for (x, y_max, slope) in active_edges]
            
        self.kopiac = self.kopia.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color) 
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
        self.image=self.kopia

    def polykoniec(self, points):
        if len(points) > 1 and points[0] == points[-1]:
            self.points.append(points[0])
            self.scanline_fill(self.points)
            self.poly(self.points)
            self.points = []
            self.temppoints = [0]
        else:
            pass

    def polyzrus(self, event = None):
        self.points = []
        self.temppoints = [0]
        self.kopiac = self.image.copy()
        if self.show_grid.get():
            draw = ImageDraw.Draw(self.kopiac)
            for i in range(0, self.actual_size, self.pixel_size):
                line_color = DEFAULT_GRID_COLOR_HEX
                draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
                draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        self.tk_image = ImageTk.PhotoImage(self.kopiac)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)


    def clear_canvas(self, event=None):
        self.image = Image.new('RGB', (self.actual_size, self.actual_size), (255, 255, 255))
        self.draw = ImageDraw.Draw(self.image)
        self.bezierzrus()
        if self.show_grid.get():
            self.draw_grid()
        self.update_canvas()

    def toggle_grid(self):
        self.bezierzrus()
        self.update_canvas()
        

    def update_canvas(self):
        self.kopiac = self.image.copy()
        if self.show_grid.get():
            self.kopiac = self.draw_grid()
            self.tk_image = ImageTk.PhotoImage(self.kopiac)
        else:
            self.tk_image = ImageTk.PhotoImage(self.image)
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
    
    def draw_grid(self):
        # Create a temporary image for grid overlay
        draw = ImageDraw.Draw(self.kopiac)
        for i in range(0, self.actual_size, self.pixel_size):
            line_color = DEFAULT_GRID_COLOR_HEX
            draw.line([(i, 0), (i, self.actual_size)], fill=line_color)
            draw.line([(0, i), (self.actual_size, i)], fill=line_color)
        return self.kopiac

if __name__ == '__main__':
    root = tk.Tk()
    root.title("Simple Drawing App")
    app = SimpleDrawingApp(root)
    root.mainloop()
Editor is loading...
Leave a Comment