/
karik
/
fourth
Обзор
Документация
Войти
/
karik
/
fourth
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
4.py
264 строки
10 KB
karik
upload files
02 ноя 2025, 13:42
02 ноя 2025, 13:42
c3c47b6
Код
Авторство
О чём код?
from tkinter import filedialog, messagebox import tkinter as tk import math CANVAS_W = 800 CANVAS_H = 500 def to_int(p): return int(round(p)) def draw_semicircle_equation(canvas, cx, cy, r, orientation, color): """ Рисует полуокружность по уравнению (только верхнюю или нижнюю часть). orientation: 'up' (arc над осью центра) или 'down' (arc под осью центра) """ # Проходим по x в диапазоне [-r, r] и вычисляем соответствующее y for dx in range(-r, r + 1): val = r * r - dx * dx if val < 0: continue dy = math.sqrt(val) if orientation == 'up': x = cx + dx y = cy - dy # вверх в координатах canvas -> уменьшаем y else: x = cx + dx y = cy + dy # ставим точку (рисуем очень короткую линию как пиксель) canvas.create_line(to_int(x), to_int(y), to_int(x)+1, to_int(y), fill=color) def draw_semicircle_parametric(canvas, cx, cy, r, orientation, color): """ Рисует полуокружность параметрически orientation: 'up' or 'down' """ # шаг угла подобран так, чтобы линия была гладкой steps = max(12 * r, 200) if orientation == 'up': t0, t1 = 0, math.pi sign = -1 # sin positive -> go up by subtracting else: t0, t1 = 0, math.pi sign = 1 dt = (t1 - t0) / steps t = t0 x_prev = cx + r * math.cos(t) y_prev = cy + sign * r * math.sin(t) t += dt while t <= t1 + 1e-9: x = cx + r * math.cos(t) y = cy + sign * r * math.sin(t) canvas.create_line(to_int(x_prev), to_int(y_prev), to_int(x), to_int(y), fill=color) x_prev, y_prev = x, y t += dt def draw_semicircle_bresenham(canvas, cx, cy, r, orientation, color): """ Рисуем полуокружность используя алгоритм Брезенхэма для окружности, но выводим только точки нужной половины ('up' или 'down'). """ x = 0 y = r d = 3 - 2 * r points = [] while x <= y: # восьмёрка симметричных точек pts = [ (cx + x, cy + y), (cx - x, cy + y), (cx + x, cy - y), (cx - x, cy - y), (cx + y, cy + x), (cx - y, cy + x), (cx + y, cy - x), (cx - y, cy - x), ] points.extend(pts) if d < 0: d = d + 4 * x + 6 else: d = d + 4 * (x - y) + 10 y -= 1 x += 1 # Отфильтруем и нарисуем только точки, попадающие в нужную половину # (для 'up' — точки с y <= cy, для 'down' — с y >= cy) if orientation == 'up': cond = lambda py: py <= cy + 0.5 else: cond = lambda py: py >= cy - 0.5 # Нужно нарисовать отрезками, чтобы точки выглядели непрерывными # соберём точки по x и усредним для плавности plotted = set() for (px, py) in points: if cond(py): key = (to_int(px), to_int(py)) if key in plotted: continue plotted.add(key) canvas.create_line(to_int(px), to_int(py), to_int(px)+1, to_int(py), fill=color) # ---------- Рисование салинона ---------- def draw_salinon_all_methods(canvas, x0, y0, r): """ Рисует один салинон тремя наложенными методами. x0,y0 - координаты центра большой полуокружности r1, r2 - радиусы двух малых полуокружностей, расположенных вдоль диаметра (в таком порядке: слева r1, справа r2) """ canvas.delete("all") # Приведём радиусы и координаты к целым пикселям x0 = to_int(x0) y0 = to_int(y0) r1 = to_int(r) r2 = to_int(r) if r1 <= 0 or r2 <= 0: canvas.create_text(CANVAS_W//2, CANVAS_H//2, text="Радиусы должны быть положительными", fill="red") return r_top = to_int((r1 * r2) / (r1 + r2)) R = r1 + r2 + r_top cx_left = x0 - r2 - r_top cx_right = x0 + r1 + r_top cx_top = x0 cy_top = y0 - (r1 - 2*r_top) # Цвета для трёх методов: colors = { 'equation': 'blue', 'parametric': 'green', 'bresenham': 'red' } # --- По уравнению (синий) draw_semicircle_equation(canvas, x0, y0, R, 'up', colors['equation']) draw_semicircle_equation(canvas, cx_left, y0, r1, 'up', colors['equation']) draw_semicircle_equation(canvas, cx_right, y0, r2, 'up', colors['equation']) draw_semicircle_equation(canvas, cx_top, cy_top, int(round(r_top)), 'down', colors['equation']) # --- Параметрически (зелёный) draw_semicircle_parametric(canvas, x0, y0, R, 'up', colors['parametric']) draw_semicircle_parametric(canvas, cx_left, y0, r1, 'up', colors['parametric']) draw_semicircle_parametric(canvas, cx_right, y0, r2, 'up', colors['parametric']) draw_semicircle_parametric(canvas, cx_top, cy_top, r_top, 'down', colors['parametric']) # --- Брезенхем (красный) draw_semicircle_bresenham(canvas, x0, y0, R, 'up', colors['bresenham']) draw_semicircle_bresenham(canvas, cx_left, y0, r1, 'up', colors['bresenham']) draw_semicircle_bresenham(canvas, cx_right, y0, r2, 'up', colors['bresenham']) draw_semicircle_bresenham(canvas, cx_top, cy_top, int(round(r_top)), 'down', colors['bresenham']) def on_build(): try: x0 = float(entry_x.get() or (CANVAS_W//2)) y0 = float(entry_y.get() or (CANVAS_H//2 + 50)) r = float(entry_r.get() or 60.0) except ValueError: lbl_status.config(text="Ошибка: введите числа.", fg="red") return if r <= 0: lbl_status.config(text="Радиус должны быть > 0", fg="red") return lbl_status.config(text="Построено", fg="green") draw_salinon_all_methods(canvas, x0, y0, r) # === Функция для получения всех пикселей с холста === def get_canvas_pixels(canvas, width, height): # Создаем матрицу для хранения пикселей (0 - белый, 1 - черный) pixels = [[0 for _ in range(width)] for _ in range(height)] # Получаем все элементы на холсте items = canvas.find_all() for item in items: # Получаем координаты элемента coords = canvas.coords(item) if len(coords) >= 4: # У прямоугольника должно быть 4 координаты x1, y1, x2, y2 = int(coords[0]), int(coords[1]), int(coords[2]), int(coords[3]) # Помечаем пиксель как закрашенный (1) if 0 <= x1 < width and 0 <= y1 < height: pixels[y1][x1] = 1 return pixels # === Функция для сохранения в PBM формат === def save_as_pbm(canvas, width, height): try: # Получаем пиксели с холста pixels = get_canvas_pixels(canvas, width, height) # Открываем диалог для выбора файла file_path = filedialog.asksaveasfilename( defaultextension=".pbm", filetypes=[("PBM files", "*.pbm"), ("All files", "*.*")] ) if file_path: with open(file_path, 'w') as f: # Записываем заголовок PBM f.write("P1\n") f.write(f"{width} {height}\n") # Записываем данные пикселей for row in pixels: f.write(" ".join(map(str, row)) + "\n") messagebox.showinfo("Успех", f"Изображение сохранено в {file_path}") except Exception as e: messagebox.showerror("Ошибка", f"Не удалось сохранить файл: {str(e)}") def on_clear(): canvas.delete("all") lbl_status.config(text="Очищено", fg="black") root = tk.Tk() root.title("Салинон — сравнение трех методов") top = tk.Frame(root) top.pack(padx=6, pady=6, anchor='nw') tk.Label(top, text="x0:").grid(row=0, column=0, sticky='e') entry_x = tk.Entry(top, width=7) entry_x.grid(row=0, column=1, padx=3) tk.Label(top, text="y0:").grid(row=0, column=2, sticky='e') entry_y = tk.Entry(top, width=7) entry_y.grid(row=0, column=3, padx=3) tk.Label(top, text="r").grid(row=0, column=4, sticky='e') entry_r = tk.Entry(top, width=7) entry_r.grid(row=0, column=5, padx=3) btn_build = tk.Button(top, text="Построить", command=on_build) btn_build.grid(row=0, column=8, padx=6) btn_clear = tk.Button(top, text="Очистить", command=on_clear) btn_clear.grid(row=0, column=9, padx=6) lbl_status = tk.Label(root, text="", anchor='w') lbl_status.pack(fill='x', padx=6) canvas = tk.Canvas(root, width=CANVAS_W, height=CANVAS_H, bg='white') canvas.pack(padx=6, pady=6) entry_x.insert(0, str(CANVAS_W//2)) entry_y.insert(0, str(CANVAS_H//2 + 50)) entry_r.insert(0, "60") # Фрейм для кнопоки button_frame = tk.Frame(root) button_frame.pack(pady=10) tk.Button(button_frame, text="Сохранить как PBM", command=lambda: save_as_pbm(canvas, CANVAS_W, CANVAS_H)).pack(side=tk.LEFT, padx=5) root.mainloop()