Некорректное построение обдуваемого ветром дерева Пифагора

Kлассическое, обнажённое и обнажённое обдуваемое ветром строятся как надо, а классическое обдуваемое ветром выходит криво. Прошу помочь с этой проблемой.

import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
 
 
class PythagorasTreeApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Pythagoras Tree")
        self.geometry("800x600")
        self.tree_type_var = tk.StringVar(value="classic")
        self.wind_angle_var = tk.DoubleVar(value=10)  # Угол в градусах
        self.create_widgets()
        self.plot_tree()
 
    def create_widgets(self):
        control_frame = ttk.Frame(self)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
 
        ttk.Label(control_frame, text="Iterations:").pack()
        self.iterations_var = tk.IntVar(value=10)
        self.iterations_entry = ttk.Entry(control_frame, textvariable=self.iterations_var)
        self.iterations_entry.pack()
 
        ttk.Label(control_frame, text="Tree Type:").pack()
        ttk.Radiobutton(control_frame, text="Classic", variable=self.tree_type_var, value="classic",
                        command=self.plot_tree).pack(anchor=tk.W)
        ttk.Radiobutton(control_frame, text="Naked", variable=self.tree_type_var, value="naked",
                        command=self.plot_tree).pack(anchor=tk.W)
 
        ttk.Label(control_frame, text="Wind Angle (degrees):").pack()
        self.wind_angle_entry = ttk.Entry(control_frame, textvariable=self.wind_angle_var)
        self.wind_angle_entry.pack()
 
        fig = Figure()
        self.ax = fig.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(fig, master=self)
        self.canvas.get_tk_widget().pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
 
    def plot_tree(self):
        self.ax.clear()
        iterations = self.iterations_var.get()
        tree_type = self.tree_type_var.get()
        wind_angle_deg = self.wind_angle_var.get()
        wind_angle_rad = np.deg2rad(wind_angle_deg)  # Преобразование угла в радианы
 
        if tree_type == "classic":
            self.draw_classic_tree(self.ax, 0.5, 0, np.pi / 2, 0.1, wind_angle_rad, iterations)
        elif tree_type == "naked":
            self.draw_naked_tree(self.ax, 0.5, 0, np.pi / 2, 0.1, wind_angle_rad, iterations)
 
        self.ax.set_aspect('equal')
        self.canvas.draw()
 
    def draw_classic_tree(self, ax, x, y, angle, length, wind_angle, depth):
        if depth == 0:
            return
 
        # Координаты вершины квадрата
        x2 = x + length * np.cos(angle)
        y2 = y + length * np.sin(angle)
        x3 = x2 + length * np.cos(angle - np.pi / 2)
        y3 = y2 + length * np.sin(angle - np.pi / 2)
        x4 = x + length * np.cos(angle - np.pi / 2)
        y4 = y + length * np.sin(angle - np.pi / 2)
 
        # Рисуем квадрат
        square_x = [x, x2, x3, x4, x]
        square_y = [y, y2, y3, y4, y]
        ax.plot(square_x, square_y, color='green')
 
        # Новая длина для следующего уровня
        new_length = length * np.sqrt(2) / 2
 
        # Корректируем координаты для новых ветвей
        right_x = x + length * np.cos(angle + wind_angle)
        right_y = y + length * np.sin(angle + wind_angle)
        left_x = x3 + new_length * np.cos(angle + np.pi / 4 + wind_angle)
        left_y = y3 + new_length * np.sin(angle + np.pi / 4 + wind_angle)
 
        # Рекурсивные вызовы для левого и правого поддеревьев с учетом ветра
        self.draw_classic_tree(ax, right_x, right_y, angle + np.pi / 4 + wind_angle, new_length, wind_angle, depth - 1)
        self.draw_classic_tree(ax, left_x, left_y, angle - np.pi / 4 + wind_angle, new_length, wind_angle, depth - 1)
 
    def draw_naked_tree(self, ax, x, y, angle, length, wind_angle, depth):
        if depth == 0:
            return
 
        # Рисуем линию
        x2 = x + length * np.cos(angle)
        y2 = y + length * np.sin(angle)
        ax.plot([x, x2], [y, y2], color='green')
 
        # Новая длина для следующего уровня
        new_length = length * np.sqrt(2) / 2
 
        # Рекурсивные вызовы для левого и правого поддеревьев
        self.draw_naked_tree(ax, x2, y2, angle + np.pi / 4 + wind_angle, new_length, wind_angle, depth - 1)
        self.draw_naked_tree(ax, x2, y2, angle - np.pi / 4 + wind_angle, new_length, wind_angle, depth - 1)
 
 
if __name__ == "__main__":
    app = PythagorasTreeApp()
    app.mainloop()

Ответы (1 шт):

Автор решения: MBo

Обратите внимание, что размер квадратов для левого и правого обдуваемого дерева должен быть разным (это два катета прямоугольного треугольника).

(Может быть, в обнажённом тоже не совсем верно размеры считаются, не проверял)

# Новая длина для следующего уровня
new_length_l = length * np.sin(np.pi / 4 + wind_angle)
new_length_r = length * np.cos(np.pi / 4 + wind_angle)

# Корректируем координаты для новых ветвей
right_x = x3 + new_length_r * np.cos(angle + np.pi / 4 - wind_angle)
right_y = y3 + new_length_r * np.sin(angle + np.pi / 4 - wind_angle)

# Рекурсивные вызовы для левого и правого поддеревьев с учетом ветра
self.draw_classic_tree(ax, x2, y2, angle + np.pi / 4 - wind_angle, new_length_l, wind_angle, depth - 1)
self.draw_classic_tree(ax, right_x, right_y, angle - np.pi / 4 - wind_angle, new_length_r, wind_angle, depth - 1)

введите сюда описание изображения

→ Ссылка