diff --git a/README.md b/README.md index 7ed0d0d..c987472 100644 --- a/README.md +++ b/README.md @@ -1 +1,15 @@ -# grad \ No newline at end of file +# Градиент, по параметрической кривой, реализованный путём интерполяции линейного оператора +## algebra.py: +### Vector +Реализация векторов, как элементов векторного пространства +### Poly +Реализация многочленов, как элементов кольца многочленов над полем +### Interpol +Интерполяция по методу Лагранжа +## window.py +### Окно Tk +- По нажатию ЛКМ отмечает на экране точку +- По нажатию space выставляет 5 случайных точек +- По нажатию esc очищает холст +## grad.py +По мажатию Enter выстраивает интерполяционный многочлен по заданным точкам, окрашивая кривую градиентом по случайным цветам в каждой из точек \ No newline at end of file diff --git a/algrebra.py b/algrebra.py new file mode 100644 index 0000000..790cce0 --- /dev/null +++ b/algrebra.py @@ -0,0 +1,166 @@ +from typing import Union, List + +class Vector: + def __init__(self, *comps): + self.len = len(comps) + self.comps = comps + + def __add__(self, other): + if type(self) == type(other): + if self.len == other.len: + return Vector(*(self.comps[i] + other.comps[i] for i in range(self.len))) + else: + return "Длины должны совпадать" + + def __sub__(self, other): + if type(self) == type(other): + if self.len == other.len: + return self + (-other) + else: + return "Длины должны совпадать" + + def __neg__(self): + return Vector(*(-self.comps[i] for i in range(self.len))) + + def __str__(self): + return '(' + ', '.join(map(str, self.comps)) + ')' + + def __mul__(self, other): + if type(other) == Vector: + if self.len == other.len: + return sum((self.comps[i]*other.comps[i] for i in range(self.len))) + else: + return "Длины должны совпадать" + if type(other) == int: + return Vector(*(self.comps[i] * other for i in range(self.len))) + + def __round__(self): + return Vector(*(round(self.comps[i]) for i in range(self.len))) + + def tuple(self): + return self.comps + + + +class Poly: + def __init__(self, coefs: Union[List[int], int]): + + if type(coefs) == int: + self.coefs = [coefs] + self.deg = 0 + + elif type(coefs) == list: + self.coefs = coefs + self.deg = len(coefs) - 1 + + def __neg__(self): + coefs = [-a for a in self.coefs] + + return Poly(coefs) + + def __add__(self, other): + if type(other) == int: + return self + Poly(other) + + if type(other) == Vector: + new_comps = [] + for com in other.comps: + new_comps.append(self + com) + + return Vector(*new_comps) + + if type(other) == Poly: + p = self.coefs + [0]*other.deg + q = other.coefs + [0]*self.deg + + n = max(self.deg, other.deg) + result = [0]*n + + for k in range(n): + result[k] = p[k] + q[k] + + return Poly(result) + + def __sub__(self, other): + return self + (-other) + + def __mul__(self, other): + if type(other) == int: + coefs = [other*a for a in self.coefs] + + return Poly(coefs) + + elif type(other) == Vector: + new_coms = [] + + for com in other.comps: + new_coms.append(self * com) + + return Vector(*new_coms) + + elif type(other) == Poly: + n = self.deg + m = other.deg + p = list(self.coefs) + [0] * other.deg + q = list(other.coefs) + [0] * self.deg + result = [0]*(self.deg + other.deg + 1) + + for k in range(self.deg + other.deg + 1): + for l in range(k + 1): + result[k] += p[l]*q[k-l] + + return Poly(result) + + def __str__(self): + res = [] + for i in range(self.deg+1): + if self.coefs[i] == 0: + continue + + if i == 0: + res.append(f"({self.coefs[i]})") + elif i == 1: + res.append(f"({self.coefs[i]}*x)") + else: + res.append(f"({self.coefs[i]}*x^{i})") + return ' + '.join(res) + + def value(self, t): + res = 0 + for i in range(self.deg+1): + res += self.coefs[i]*(t**i) + return res + + def diff(self): + if self.deg > 0: + coefs = [self.coefs[i] * i for i in range(1, self.deg + 1)] + return Poly(coefs) + else: + return 0 + + pass + + +def interpol(points: List[tuple]) -> Poly: + n = len(points) + + X = tuple(point[0] for point in points) + Y = tuple(point[1] for point in points) + + res = Poly([0]*n) + + for i in range(n): + iter = Poly([0]*n) + iter.coefs[0] = 1 + + for j in range(n): + if i==j: + continue + + dx = 1/(X[i] - X[j]) + iter = iter * Poly([-X[j]*dx, dx]) + + res = res + (iter * Y[i]) + return res + + diff --git a/grad.py b/grad.py index 7dc1dcb..66acc64 100644 --- a/grad.py +++ b/grad.py @@ -1,22 +1,81 @@ -from vector import Vector -from typing import Tuple, List +from algrebra import * +from window import window +from random import randint -def interpol(points: List[Tuple[int]]) -> List[Tuple[int]]: - n = len(points) - X = tuple(point[0] for point in points) - Y = tuple(point[1] for point in points) - graph = [] - for t in range(points[0][0], points[n-1][0]+1): - y = 0 - for i in range(n): - q = Y[i] - for j in range(n): - if j != i: - q = q * (t-X[j]) / (X[i] - X[j]) - y = y + q - graph.append((t, y)) - return graph + + +def color_filter(color): + result = [] + for el in color: + if (el < 0): + result.append(0) + elif (el > 255): + result.append(255) + else: + result.append(round(el)) + return tuple(result) + + +def rgb_to_hex(color): + r, g, b = color + return f'#{r:02x}{g:02x}{b:02x}' + + +def main(wind): + L = interpol(wind.points) + dL = L.diff() + N = len(wind.points) + perp = [] + + for p in wind.points: + k = dL.value(p[0]) + if k==0: + wind.canvas.create_line(p[0], 0, p[0], wind.height, fill=rgb_to_hex((0, 0, 0))) + else: + k = -1/k + b = p[1] - k*p[0] + perp.append(Poly([b, k])) + + colors = [(0, Vector(0, 0, 0))] + for i in range(N): + colors.append((wind.points[i][0], Vector(randint(0, 255), randint(0, 255), randint(0, 255)))) + colors.append((width, Vector(255, 255, 255))) + + color_func = interpol(colors).tuple() + + step = 0.5 + for dx in range(round(width/step)): + x=dx*step + color = color_filter(comp.value(x) for comp in color_func) + print(x, color) + """ + k = dL.value(x) + if k == 0: + wind.canvas.create_line(x, L.value(x), x+1, L.value(x+1), fill=rgb_to_hex(color)) + else: + k = -1/k + b = L.value(x) - k*x + for y in range(width*2): + wind.canvas.create_line(y/2, k*y/2+b, y/2+1, k*(y/2+1)+b, fill=rgb_to_hex(color)) + """ + wind.canvas.create_line(x, L.value(x), x+step, L.value(x+step), fill=rgb_to_hex((color)), width=5) + + + for i in range(N): + wind.canvas.create_oval(colors[i][0] - 3, L.value(colors[i][0]) - 3, colors[i][0] + 3, L.value(colors[i][0]) + 3, fill=rgb_to_hex(colors[i][1].tuple())) + + + + +if __name__ == "__main__": + title = 'Huy' + width = 1000 + height = 500 + wind = window(title, width, height) + + def on_enter_pressed(event): + main(wind) + wind.bind("", on_enter_pressed) -def main(points: List[Tuple[int]], colors: Tuple[Vector]) -> Tuple[int]: - return () \ No newline at end of file + wind.mainloop() \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 41f264e..0000000 --- a/main.py +++ /dev/null @@ -1,43 +0,0 @@ -import tkinter as tk -from grad import interpol - -def rgb_to_hex(color): - r, g, b = color - return f'#{r:02x}{g:02x}{b:02x}' - -# Создаем основное окно -root = tk.Tk() -root.title("Pixel Coloring") - -# Устанавливаем размеры окна -width = 400 -height = 400 - -# Создаем холст (Canvas) для рисования -canvas = tk.Canvas(root, width=width, height=height) -canvas.pack() - -# Массив для хранения координат точек -points = [] - -# Функция, которая будет вызвана при нажатии Enter -def on_enter_pressed(event): - graph = interpol(points) - for x, y in graph: - canvas.create_line(x, y, x+1, y, fill=rgb_to_hex((0, 0, 0))) - -# Функция, которая вызывается при нажатии мыши -def on_mouse_click(event): - x, y = event.x, event.y - points.append((x, y)) # Добавляем координаты в массив - print(f"Point added: ({x}, {y})") - # Рисуем точку на холсте - canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill="red") - -# Привязываем события -canvas.bind("", on_mouse_click) # Левый клик мыши -root.bind("", on_enter_pressed) # Клавиша Enter - - -# Запускаем главный цикл обработки событий -root.mainloop() \ No newline at end of file diff --git a/vector.py b/vector.py deleted file mode 100644 index d63e899..0000000 --- a/vector.py +++ /dev/null @@ -1,39 +0,0 @@ -class Vector(): - def __init__(self, *comps): - self.len = len(comps) - self.comps = comps - - def __add__(self, other): - if type(self) == type(other): - if self.len == other.len: - return Vector(*(self.comps[i] + other.comps[i] for i in range(self.len))) - else: - return "Длины должны совпадать" - - def __sub__(self, other): - if type(self) == type(other): - if self.len == other.len: - return Vector(*(self.comps[i] - other.comps[i] for i in range(self.len))) - else: - return "Длины должны совпадать" - - def __neg__(self): - return Vector(*(-self.comps[i] for i in range(self.len))) - - def __str__(self): - return '(' + ', '.join(map(str, self.comps)) + ')' - - def __mul__(self, other): - if type(other) == Vector: - if self.len == other.len: - return sum((self.comps[i]*other.comps[i] for i in range(self.len))) - else: - return "Длины должны совпадать" - if type(other) == int: - return Vector(*(self.comps[i] * other for i in range(self.len))) - - def __round__(self): - return Vector(*(round(self.comps[i]) for i in range(self.len))) - - def tuple(self): - return self.comps \ No newline at end of file diff --git a/window.py b/window.py new file mode 100644 index 0000000..8d6ea4b --- /dev/null +++ b/window.py @@ -0,0 +1,52 @@ +import tkinter as tk +from random import randint + + +def rgb_to_hex(color): + r, g, b = color + return f'#{r:02x}{g:02x}{b:02x}' + +def window(title, width, height): + # Создаем основное окно + root = tk.Tk() + root.title(title) + + # Устанавливаем размеры окна + root.width = width + root.height = height + + # Создаем холст (Canvas) для рисования + root.canvas = tk.Canvas(root, width=width, height=height) + root.canvas.pack() + + # Массив для хранения координат точек + root.points = [] + + # Функция, которая вызывается при нажатии мыши + def on_mouse_click(event): + x, y = event.x, event.y + root.points.append((x, y)) # Добавляем координаты в массив + print(f"Point added: ({x}, {y})") + # Рисуем точку на холсте + root.canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill="red") + + def on_esc_pressed(event): + root.points = [] + root.canvas.delete("all") + + def on_space_pressed(event): + on_esc_pressed(event) + + for i in range(5): + x = randint(round(root.width * 0.2), round(root.width * 0.8)) + y = randint(round(root.height * 0.2), round(root.height * 0.8)) + root.points.append((x, y)) + root.canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill="red") + + + # Привязываем события + root.canvas.bind("", on_mouse_click) # Левый клик мыши + root.bind("", on_space_pressed) + root.bind("", on_esc_pressed) + + return root \ No newline at end of file