import tkinter as tk from tkinter import ttk import math import matplotlib matplotlib.use("TkAgg") import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import numpy as np class TabDrag: def __init__(self, notebook, tab_simulator): """ Pestaña para calcular un coef. 'b' a partir de la geometría. Luego se pasa a tab_simulator.set_b_value(...) para usarlo en la simulación. """ self.notebook = notebook self.tab_simulator = tab_simulator self.frame = tk.Frame(notebook) self.frame.pack(fill="both", expand=True) # Layout principal frame_left = tk.Frame(self.frame) frame_left.pack(side="left", fill="y", padx=5, pady=5) frame_right = tk.Frame(self.frame, bd=2, relief="groove") frame_right.pack(side="right", fill="both", expand=True, padx=5, pady=5) self.frame_3d = tk.Frame(frame_right) self.frame_3d.pack(fill="both", expand=True) # Sección Izquierda: geometría + parámetros tk.Label(frame_left, text="Selecciona geometría:").pack(anchor="w") self.geometria_var = tk.StringVar(value="Prisma cuadrado") self.combo_geometrias = ttk.Combobox( frame_left, textvariable=self.geometria_var, values=["Prisma cuadrado", "Cilindro", "Esfera"], state="readonly" ) self.combo_geometrias.pack(anchor="w", pady=5) self.frame_parametros = tk.Frame(frame_left, bd=1, relief="sunken") self.frame_parametros.pack(fill="x", pady=5) self.label_param1 = tk.Label(self.frame_parametros, text="Parámetro 1:") self.label_param1.grid(row=0, column=0, sticky="w", padx=5, pady=2) self.entry_param1 = tk.Entry(self.frame_parametros, width=8) self.entry_param1.grid(row=0, column=1, padx=5, pady=2) self.entry_param1.insert(0, "1") self.label_param2 = tk.Label(self.frame_parametros, text="Parámetro 2:") self.label_param2.grid(row=1, column=0, sticky="w", padx=5, pady=2) self.entry_param2 = tk.Entry(self.frame_parametros, width=8) self.entry_param2.grid(row=1, column=1, padx=5, pady=2) self.entry_param2.insert(0, "7") self.label_result_b = tk.Label(frame_left, text="Coef (b): N/A", fg="blue") self.label_result_b.pack() btn_calc_b = tk.Button( frame_left, text="Calcular Coef. Rozamiento", command=self.calcular_coef_rozamiento ) btn_calc_b.pack(pady=5) tk.Button( frame_left, text="Refrescar Vista 3D", command=self.refrescar_3d ).pack(pady=10) # Creamos la figura 3D self.fig = plt.Figure(figsize=(4,3), dpi=100) self.ax = self.fig.add_subplot(111, projection='3d') self.canvas_3d = FigureCanvasTkAgg(self.fig, master=self.frame_3d) self.canvas_widget = self.canvas_3d.get_tk_widget() self.canvas_widget.pack(fill="both", expand=True) self.combo_geometrias.bind("<>", self.on_change_geometria) self.update_param_labels() self.refrescar_3d() def on_change_geometria(self, event=None): self.update_param_labels() self.refrescar_3d() def update_param_labels(self): geom = self.geometria_var.get() self.entry_param2.config(state="normal", fg="black") if geom=="Prisma cuadrado": self.label_param1.config(text="Lado base (m):") self.label_param2.config(text="Longitud (m):") elif geom=="Cilindro": self.label_param1.config(text="Radio (m):") self.label_param2.config(text="Altura (m):") elif geom=="Esfera": self.label_param1.config(text="Radio (m):") self.label_param2.config(text="(no aplica):") self.entry_param2.delete(0, tk.END) self.entry_param2.config(state="disabled", fg="gray") else: self.label_param1.config(text="Parámetro 1:") self.label_param2.config(text="Parámetro 2:") def refrescar_3d(self): # Dibuja la geometría en self.ax geom = self.geometria_var.get() try: p1 = float(self.entry_param1.get()) except ValueError: p1=1.0 try: if self.entry_param2.cget("state")!="disabled": p2 = float(self.entry_param2.get()) else: p2=1.0 except ValueError: p2=1.0 self.ax.clear() self.ax.set_axis_off() if geom=="Prisma cuadrado": Xs=[0,p1,p1,0,0,p1,p1,0] Ys=[0,0,p1,p1,0,0,p1,p1] Zs=[0,0,0,0,p2,p2,p2,p2] edges=[(0,1),(1,2),(2,3),(3,0), (4,5),(5,6),(6,7),(7,4), (0,4),(1,5),(2,6),(3,7)] for (i,j) in edges: self.ax.plot([Xs[i],Xs[j]], [Ys[i],Ys[j]], [Zs[i],Zs[j]], color='orange') self.ax.set_box_aspect((p1,p1,p2)) elif geom=="Cilindro": import numpy as np import math r=p1 h=p2 theta=np.linspace(0,2*math.pi,30) z=np.linspace(0,h,30) T,Z=np.meshgrid(theta,z) X=r*np.cos(T) Y=r*np.sin(T) self.ax.plot_surface(X, Y, Z, color='orange', alpha=0.8) self.ax.set_box_aspect((2*r,2*r,h)) elif geom=="Esfera": import numpy as np import math r=p1 phi=np.linspace(0,math.pi,30) t=np.linspace(0,2*math.pi,30) phi_grid,t_grid=np.meshgrid(phi,t) X=r*np.sin(phi_grid)*np.cos(t_grid) Y=r*np.sin(phi_grid)*np.sin(t_grid) Z=r*np.cos(phi_grid) self.ax.plot_surface(X,Y,Z,color='orange',alpha=0.8) self.ax.set_box_aspect((2*r,2*r,2*r)) else: self.ax.text2D(0.3,0.5,"Geom. desconocida", transform=self.ax.transAxes) self.ax.set_title(geom) self.canvas_3d.draw() def calcular_coef_rozamiento(self): geom = self.geometria_var.get() try: p1 = float(self.entry_param1.get()) except: self.label_result_b.config(text="Error param1", fg="red") return Cd=1.0 A=1.0 if geom=="Prisma cuadrado": try: p2=float(self.entry_param2.get()) except: self.label_result_b.config(text="Error param2", fg="red") return Cd=1.15 A=p1*p1 elif geom=="Cilindro": try: p2=float(self.entry_param2.get()) except: self.label_result_b.config(text="Error param2", fg="red") return Cd=0.82 import math A=math.pi*(p1**2) elif geom=="Esfera": Cd=0.47 import math A=math.pi*(p1**2) rho=1.225 b_calc=0.5*rho*Cd*A self.label_result_b.config(text=f"Coef (b) ~ {b_calc:.4f}", fg="blue") self.tab_simulator.set_b_value(b_calc)