소스 검색

fisrt commit

dacowars 1 개월 전
커밋
9502c950a8
4개의 변경된 파일1767개의 추가작업 그리고 0개의 파일을 삭제
  1. 1 0
      .gitignore
  2. 899 0
      app_designer.py
  3. 423 0
      secciones.py
  4. 444 0
      secciones_config.json

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+*.xlsx

+ 899 - 0
app_designer.py

@@ -0,0 +1,899 @@
+import tkinter as tk
+from tkinter import ttk, messagebox, simpledialog
+import json
+import numpy as np
+import math
+import matplotlib.pyplot as plt
+from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
+import os
+
+# ==================== CONSTANTES DE MATERIAL ====================
+DENS = 7850  # kg/m3
+FY = 355  # MPa
+GAMMAS = 1.05  # Factor seguridad acero
+EYOUNG = 210000  # MPa
+NU = 0.3  # Coef Poisson
+
+FYD = FY * 10**6 / GAMMAS  # Pa
+EYOUNG_PA = EYOUNG * 10**6  # Pa
+G = EYOUNG_PA / (2 * (1 + NU))  # Pa
+
+# ==================== FUNCIONES DE CÁLCULO ====================
+
+def generate_ipe_points(H, b, tf, tw):
+    """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
+    # H: altura total, b: ancho flange, tf: espesor flange, tw: espesor alma
+    h_alma = H - 2*tf
+    x_alma_ini = (b - tw) / 2
+    x_alma_fin = (b + tw) / 2
+
+    # Puntos de la sección (sentido horario)
+    points = np.array([
+        [0, 0],                           # Esquina inferior izquierda flange
+        [b, 0],                           # Esquina inferior derecha flange
+        [b, tf],                          # Unión alma-flange derecha
+        [x_alma_fin, tf],                 # Esquina superior alma derecha
+        [x_alma_fin, H - tf],             # Esquina inferior alma derecha (arriba)
+        [b, H - tf],                      # Unión alma-flange arriba
+        [b, H],                           # Esquina superior derecha flange
+        [0, H],                           # Esquina superior izquierda flange
+        [0, H - tf],                      # Unión alma-flange arriba izquierda
+        [x_alma_ini, H - tf],             # Esquina inferior alma izquierda (arriba)
+        [x_alma_ini, tf],                 # Esquina superior alma izquierda
+        [0, tf],                          # Unión alma-flange izquierda
+        [0, 0],                           # Cerrar la sección
+    ])
+
+    return points
+
+def generate_puente_nuevo_points(h, b, tf, tw, ha, ta, tr, theta):
+    """Genera los puntos de la sección tipo puente nuevo a partir de parámetros normalizados"""
+    # h: altura exterior, b: ancho total, tf: espesor ala, tw: espesor alma,
+
+    hr = (b-tw) / 2 * math.tan(math.radians(theta))
+    hl = tr / math.cos(math.radians(theta))
+    hr_ = (b/2 - ta - tw/2)* math.tan(math.radians(theta))
+
+    # Puntos de la sección (sentido horario)
+    points = np.array([
+        [0, 0],                           
+        [b, 0],                           
+        [b, tf + ha],                          
+        [(b + tw)/2, tf + ha + hr],                
+        [(b + tw)/2, tf + ha + hr - hl],           
+        [b - ta, tf + ha + hr - hl - hr_],         
+        [b - ta, tf],                           
+        [(b + tw)/2, tf],                           
+        [(b + tw)/2, h - tf],                      
+        [b - ta, h - tf],             
+        [b - ta, h - tf - ha - hr + hl + hr_],                 
+        [(b + tw)/2, h - tf - ha - hr + hl],                          
+        [(b + tw)/2, h - tf - ha - hr],
+        [b, h - tf - ha],
+        [b, h],
+        [0, h],                           
+        [0, h - tf - ha],                      
+        [(b - tw)/2, h - tf - ha - hr],                          
+        [(b - tw)/2, h - tf - ha - hr + hl],
+        [ta, h - tf - ha - hr + hl + hr_],
+        [ta, h - tf],
+        [(b - tw)/2, h - tf],
+        [(b - tw)/2, tf],
+        [ta, tf],
+        [ta, tf + ha + hr - hl - hr_],
+        [(b - tw)/2, tf + ha + hr - hl],                
+        [(b - tw)/2, tf + ha + hr],                          
+        [0, tf + ha],                          
+        [0, 0],                           
+    ])
+
+    return points
+
+def calculate_section_properties(points):
+    """Calcula todas las propiedades de la sección - CÓDIGO ORIGINAL SIN MODIFICAR"""
+    npuntos = len(points)
+    puntos = points
+
+    px = puntos[:, 0]
+    py = puntos[:, 1]
+
+    # ANCHO Y CANTO MÁXIMO
+    bmax = np.amax(px) - np.amin(px)
+    hmax = np.amax(py) - np.amin(py)
+
+    # PERÍMETRO
+    long_i = np.zeros(npuntos - 1)
+    for i in range(npuntos - 1):
+        long_i[i] = ((puntos[i+1, 0] - puntos[i, 0])**2 + (puntos[i+1, 1] - puntos[i, 1])**2)**(1/2)
+    perimetro = abs(sum(long_i))
+
+    # ÁREA
+    area_i = np.zeros(npuntos - 1)
+    for i in range(npuntos - 1):
+        area_i[i] = (puntos[i+1, 0] - puntos[i, 0]) * (puntos[i+1, 1] + puntos[i, 1]) / 2
+    area = abs(sum(area_i))
+
+    # PESO POR METRO
+    peso = area * DENS
+
+    # CENTRO DE GRAVEDAD
+    cdg_i = np.zeros([npuntos, 2])
+    for i in range(npuntos - 1):
+        h1 = puntos[i, 1]
+        h2 = puntos[i+1, 1]
+        b = puntos[i+1, 0] - puntos[i, 0]
+        d = puntos[i, 0]
+
+        if h1 + h2 == 0:
+            cdg_i[i, 1] = 0
+        else:
+            cdg_i[i, 1] = 1/3 * (h1*h1 + h1*h2 + h2*h2) / (h1 + h2)
+
+        if h1 + h2 == 0:
+            cdg_i[i, 0] = d + b/2
+        else:
+            cdg_i[i, 0] = d + b/3 * (h1 + 2*h2) / (h1 + h2)
+
+    # MOMENTO ESTÁTICO
+    statico_i = np.zeros([npuntos, 2])
+    for i in range(npuntos - 1):
+        statico_i[i, 1] = area_i[i] * cdg_i[i, 1]
+        statico_i[i, 0] = area_i[i] * cdg_i[i, 0]
+
+    cdg = sum(statico_i) / sum(area_i)
+    xg = cdg[0]
+    yg = cdg[1]
+
+    # FIBRAS MÁS ALEJADAS
+    v1y = np.amax(py) - yg
+    v2y = np.amin(py) - yg
+    v1x = np.amax(px) - xg
+    v2x = np.amin(px) - xg
+
+    # MOMENTOS DE INERCIA
+    inercia_i = np.zeros([npuntos, 3])
+    for i in range(npuntos - 1):
+        h1 = puntos[i, 1]
+        h2 = puntos[i+1, 1]
+        b = puntos[i+1, 0] - puntos[i, 0]
+        d = puntos[i, 0]
+        xgi = cdg_i[i, 0]
+        ygi = cdg_i[i, 1]
+        ai = area_i[i]
+
+        # Ixg
+        if h2 >= h1:
+            ixcuad_G_local = 1/12 * b * (h1**3) + b * h1 * (h1/2 - ygi)**2
+            ixtriang_G_loc = 1/36 * b * (h2-h1)**3 + 1/2 * b * (h2-h1) * ((2*h1+h2)/3 - ygi)**2
+        else:
+            ixcuad_G_local = 1/12 * b * (h2**3) + b * h2 * (h2/2 - ygi)**2
+            ixtriang_G_loc = 1/36 * b * (h1-h2)**3 + 1/2 * b * (h1-h2) * ((2*h2+h1)/3 - ygi)**2
+        inercia_i[i, 0] = ixcuad_G_local + ixtriang_G_loc + ai * (yg - ygi)**2
+
+        # Iyg
+        if h2 >= h1:
+            iycuad = 1/12 * h1 * b**3 + h1 * b * (b/2 + d - xgi)**2
+            iytrian = 1/36 * (h2-h1) * b**3 + 1/2 * b * (h2-h1) * (2/3*b + d - xgi)**2
+        else:
+            iycuad = 1/12 * h2 * b**3 + h2 * b * (b/2 + d - xgi)**2
+            iytrian = 1/36 * (h1-h2) * b**3 + 1/2 * b * (h1-h2) * (1/3*b + d - xgi)**2
+        inercia_i[i, 1] = iycuad + iytrian + ai * (xg - xgi)**2
+
+        # Pxyg
+        if h2 >= h1:
+            pxygcuadrado = b * h1 * (-h1/2 + ygi) * (-d - b/2 + xgi)
+            pxytriangulo = b*b * (h2-h1)**2 / 72 + b * (h2-h1) / 2 * (-(h2-h1)/3 - h1 + ygi) * (-d - 2/3*b + xgi)
+        else:
+            pxygcuadrado = b * h2 * (-h2/2 + ygi) * (-d - b/2 + xgi)
+            pxytriangulo = -b*b * (h1-h2)**2 / 72 + b * (h1-h2) / 2 * (-(h1-h2)/3 - h2 + ygi) * (-d - 1/3*b + xgi)
+        inercia_i[i, 2] = pxygcuadrado + pxytriangulo + ai * (-xg + xgi) * (-yg + ygi)
+
+    ig = sum(inercia_i)
+    ixg = abs(ig[0])
+    iyg = abs(ig[1])
+    pxyg = ig[2]
+    if sum(area_i) >= 0:
+        pxyg = pxyg
+    else:
+        pxyg = -pxyg
+
+    # RADIOS DE GIRO
+    rx = (ixg / area)**0.5
+    ry = (iyg / area)**0.5
+
+    # EJES PRINCIPALES DE INERCIA
+    ic = (ixg + iyg) / 2
+    ir = (((ixg - iyg)/2)**2 + pxyg**2)**0.5
+
+    imax = ic + ir
+    imin = ic - ir
+    rmax = (imax / area)**0.5
+    rmin = (imin / area)**0.5
+
+    # ORIENTACIÓN DE EJES PRINCIPALES
+    rest = ixg - iyg
+    if abs(rest) < 10**-12:
+        tetha = 45
+    else:
+        tetha = 0.5 * math.atan((pxyg*2) / (ixg - iyg))
+        tetha = abs(tetha) * 180 / math.pi
+
+    if pxyg > 0:
+        if ixg > iyg:
+            tetha = -tetha
+        else:
+            tetha = tetha
+    else:
+        if ixg > iyg:
+            tetha = tetha
+        else:
+            tetha = -tetha
+
+    # MÓDULO RESISTENTE ELÁSTICO
+    wel1x = abs(ixg / v1y)
+    wel2x = abs(ixg / v2y)
+    wel1y = abs(iyg / v1x)
+    wel2y = abs(iyg / v2x)
+
+    # AXIL Y MOMENTO ELÁSTICO
+    nel = area * FYD
+    melx = min(wel1x, wel2x) * FYD
+    mely = min(wel1y, wel2y) * FYD
+
+    # CONVERSIÓN A UNIDADES PRÁCTICAS (cm, cm2, cm3, cm4, kN)
+    pot = 2
+    bmax_cm = bmax * 10**pot
+    hmax_cm = hmax * 10**pot
+    perimetro_cm = perimetro * 10**pot
+    xg_cm = xg * 10**pot
+    yg_cm = yg * 10**pot
+    v1y_cm = v1y * 10**pot
+    v2y_cm = v2y * 10**pot
+    v1x_cm = v1x * 10**pot
+    v2x_cm = v2x * 10**pot
+    rx_cm = rx * 10**pot
+    ry_cm = ry * 10**pot
+    rmax_cm = rmax * 10**pot
+    rmin_cm = rmin * 10**pot
+
+    area_cm2 = area * 10**(pot*2)
+
+    wel1x_cm3 = wel1x * 10**(pot*3)
+    wel2x_cm3 = wel2x * 10**(pot*3)
+    wel1y_cm3 = wel1y * 10**(pot*3)
+    wel2y_cm3 = wel2y * 10**(pot*3)
+
+    ixg_cm4 = ixg * 10**(pot*4)
+    iyg_cm4 = iyg * 10**(pot*4)
+    pxyg_cm4 = pxyg * 10**(pot*4)
+    imax_cm4 = imax * 10**(pot*4)
+    imin_cm4 = imin * 10**(pot*4)
+
+    nel_kn = nel / 1000
+    melx_kn = melx / 1000
+    mely_kn = mely / 1000
+
+    return {
+        'area': area_cm2,
+        'perimetro': perimetro_cm,
+        'xg': xg_cm,
+        'yg': yg_cm,
+        'v1y': v1y_cm,
+        'v2y': v2y_cm,
+        'v1x': v1x_cm,
+        'v2x': v2x_cm,
+        'ixg': ixg_cm4,
+        'iyg': iyg_cm4,
+        'pxyg': pxyg_cm4,
+        'imax': imax_cm4,
+        'imin': imin_cm4,
+        'rx': rx_cm,
+        'ry': ry_cm,
+        'rmax': rmax_cm,
+        'rmin': rmin_cm,
+        'wel1x': wel1x_cm3,
+        'wel2x': wel2x_cm3,
+        'wel1y': wel1y_cm3,
+        'wel2y': wel2y_cm3,
+        'tetha': tetha,
+        'peso': peso,
+        'nel': nel_kn,
+        'melx': melx_kn,
+        'mely': mely_kn,
+        'xg_orig': xg,
+        'yg_orig': yg,
+        'bmax': bmax,
+        'hmax': hmax,
+        'points': points
+    }
+
+
+# ==================== APLICACIÓN TKINTER ====================
+
+class SectionDesignerApp(tk.Tk):
+    def __init__(self):
+        super().__init__()
+        self.title("Diseñador de Secciones de Acero")
+        self.geometry("1200x800")
+
+        self.json_path = r"c:\Users\Daniel.p\Documents\Automatizaciones\Propiedades seccion\secciones_config.json"
+        self.load_json()
+        self.current_section_name = None
+        self.current_points = None
+        self.current_properties = None
+
+        self.create_widgets()
+
+    def load_json(self):
+        """Carga el archivo JSON de configuración"""
+        if os.path.exists(self.json_path):
+            with open(self.json_path, 'r') as f:
+                self.secciones_data = json.load(f)
+        else:
+            self.secciones_data = {"secciones": []}
+
+    def save_json(self):
+        """Guarda los cambios al JSON"""
+        with open(self.json_path, 'w') as f:
+            json.dump(self.secciones_data, f, indent=2)
+
+    def create_widgets(self):
+        """Crea la interfaz gráfica"""
+
+        # ========== PANEL IZQUIERDO (CONTROLES) ==========
+        left_panel = ttk.Frame(self, width=250)
+        left_panel.pack(side=tk.LEFT, fill=tk.BOTH, padx=10, pady=10)
+
+        # Selector de sección
+        ttk.Label(left_panel, text="Sección:", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+        self.combo_seccion = ttk.Combobox(
+            left_panel,
+            values=[s['nombre'] for s in self.secciones_data['secciones']],
+            state='readonly',
+            width=20
+        )
+        self.combo_seccion.pack(anchor=tk.W)
+        self.combo_seccion.bind('<<ComboboxSelected>>', self.on_section_changed)
+
+        # Separador
+        ttk.Separator(left_panel, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
+
+        # Selector de tipo de sección
+        ttk.Label(left_panel, text="Tipo de sección:", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+        self.combo_tipo = ttk.Combobox(
+            left_panel,
+            values=["IPE", "Personalizada", "Puente nuevo"],
+            state='readonly',
+            width=20
+        )
+        self.combo_tipo.pack(anchor=tk.W, pady=(0, 10))
+        self.combo_tipo.set("IPE")
+        self.combo_tipo.bind('<<ComboboxSelected>>', self.on_section_type_changed)
+
+        # Frame para parámetros IPE
+        self.frame_ipe = ttk.Frame(left_panel)
+        self.frame_ipe.pack(fill=tk.BOTH, expand=False, pady=(0, 10))
+
+        ttk.Label(self.frame_ipe, text="Parámetros IPE:", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+
+        # H (Altura)
+        ttk.Label(self.frame_ipe, text="H (altura, m):").pack(anchor=tk.W)
+        self.entry_H = ttk.Spinbox(self.frame_ipe, from_=0.0, to=10000.0, increment=0.01, width=15, command=self.on_parameter_change_ipe)
+        self.entry_H.pack(anchor=tk.W, fill=tk.X)
+        self.entry_H.set("0.300")
+        self.entry_H.bind('<FocusOut>', self.on_parameter_change_ipe)
+        self.entry_H.bind('<Return>', self.on_parameter_change_ipe)
+
+        # b (Ancho)
+        ttk.Label(self.frame_ipe, text="b (ancho flange, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_b = ttk.Spinbox(self.frame_ipe, from_=0.0, to=10000.0, increment=0.005, width=15, command=self.on_parameter_change_ipe)
+        self.entry_b.pack(anchor=tk.W, fill=tk.X)
+        self.entry_b.set("0.150")
+        self.entry_b.bind('<FocusOut>', self.on_parameter_change_ipe)
+        self.entry_b.bind('<Return>', self.on_parameter_change_ipe)
+
+        # tf (Espesor flange)
+        ttk.Label(self.frame_ipe, text="tf (espesor flange, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_tf = ttk.Spinbox(self.frame_ipe, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_ipe)
+        self.entry_tf.pack(anchor=tk.W, fill=tk.X)
+        self.entry_tf.set("0.0107")
+        self.entry_tf.bind('<FocusOut>', self.on_parameter_change_ipe)
+        self.entry_tf.bind('<Return>', self.on_parameter_change_ipe)
+
+        # tw (Espesor alma)
+        ttk.Label(self.frame_ipe, text="tw (espesor alma, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_tw = ttk.Spinbox(self.frame_ipe, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_ipe)
+        self.entry_tw.pack(anchor=tk.W, fill=tk.X)
+        self.entry_tw.set("0.0063")
+        self.entry_tw.bind('<FocusOut>', self.on_parameter_change_ipe)
+        self.entry_tw.bind('<Return>', self.on_parameter_change_ipe)
+
+        # Frame para sección personalizada
+        self.frame_personalizada = ttk.Frame(left_panel)
+
+        ttk.Label(self.frame_personalizada, text="Puntos (JSON):", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+        ttk.Label(self.frame_personalizada, text="Formato: [[x1,y1], [x2,y2], ...]", font=("Arial", 8)).pack(anchor=tk.W)
+
+        self.text_puntos = tk.Text(self.frame_personalizada, height=8, width=30, font=("Courier", 8))
+        self.text_puntos.pack(fill=tk.BOTH, expand=True, pady=(0, 5))
+
+        ttk.Button(self.frame_personalizada, text="Cargar puntos", command=self.load_custom_points).pack(fill=tk.X)
+
+        # Frame para puente nuevo
+        self.frame_puente_nuevo = ttk.Frame(left_panel)
+        self.frame_puente_nuevo.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
+
+        ttk.Label(self.frame_puente_nuevo, text="Parámetros puente nuevo:", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+
+        # H (Altura exterior)
+        ttk.Label(self.frame_puente_nuevo, text = "h (altura exterior, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_h_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.01, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_h_puente.pack(anchor=tk.W, fill=tk.X)   
+        self.entry_h_puente.set("0.300")
+        self.entry_h_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_h_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # b (Ancho)
+        ttk.Label(self.frame_puente_nuevo, text="b (ancho, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_b_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_b_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_b_puente.set("0.150")
+        self.entry_b_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_b_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # tf (espesor ala)
+        ttk.Label(self.frame_puente_nuevo, text="tf (espesor ala, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_tf_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_tf_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_tf_puente.set("0.0107")
+        self.entry_tf_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_tf_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # tw (espesor alma)
+        ttk.Label(self.frame_puente_nuevo, text="tw (espesor alma, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_tw_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_tw_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_tw_puente.set("0.0063")
+        self.entry_tw_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_tw_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # ha (altura refuerzo ala)
+        ttk.Label(self.frame_puente_nuevo, text="ha (altura refuerzo ala, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_ha_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_ha_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_ha_puente.set("0.02")
+        self.entry_ha_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_ha_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # ta (espesor refuerzo ala)
+        ttk.Label(self.frame_puente_nuevo, text="ta (espesor refuerzo ala, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_ta_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_ta_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_ta_puente.set("0.01")
+        self.entry_ta_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_ta_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # tr (espesor refuerzo alma)
+        ttk.Label(self.frame_puente_nuevo, text="tr (espesor refuerzo alma, m):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_tr_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=10000.0, increment=0.0005, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_tr_puente.pack(anchor=tk.W, fill=tk.X)
+        self.entry_tr_puente.set("0.01")
+        self.entry_tr_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_tr_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # theta (ángulo refuerzo)
+        ttk.Label(self.frame_puente_nuevo, text="theta (ángulo refuerzo, grados):").pack(anchor=tk.W, pady=(10, 0))
+        self.entry_theta_puente = ttk.Spinbox(self.frame_puente_nuevo, from_=0.0, to=360.0, increment=1.0, width=15, command=self.on_parameter_change_puente_nuevo)
+        self.entry_theta_puente.pack(anchor=tk.W, fill=tk.X)   
+        self.entry_theta_puente.set("45")
+        self.entry_theta_puente.bind('<FocusOut>', self.on_parameter_change_puente_nuevo)
+        self.entry_theta_puente.bind('<Return>', self.on_parameter_change_puente_nuevo)
+
+        # Separador
+        ttk.Separator(left_panel, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
+
+        # Panel de resultados
+        ttk.Label(left_panel, text="Propiedades:", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=(10, 5))
+
+        self.text_results = tk.Text(left_panel, height=20, width=30, state=tk.DISABLED, font=("Courier", 8))
+        self.text_results.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
+
+        # Botón guardar
+        ttk.Button(left_panel, text="Guardar nueva sección", command=self.save_new_section).pack(fill=tk.X)
+
+        # ========== PANEL DERECHO (GRÁFICO) ==========
+        right_panel = ttk.Frame(self)
+        right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)
+
+        self.figure = plt.Figure(figsize=(6, 8), dpi=100)
+        self.ax = self.figure.add_subplot(111)
+
+        self.canvas = FigureCanvasTkAgg(self.figure, master=right_panel)
+        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
+
+        # Cargar la primera sección después de que todos los widgets estén creados
+        if self.secciones_data['secciones']:
+            self.combo_seccion.current(0)
+            self.on_section_changed(None)
+
+        self.frame_puente_nuevo.pack_forget()
+
+    def on_section_type_changed(self, event):
+        """Cambia entre IPE y Personalizada"""
+        section_type = self.combo_tipo.get()
+
+        if section_type == "IPE":
+            self.frame_personalizada.pack_forget()
+            self.frame_puente_nuevo.pack_forget()
+            self.frame_ipe.pack(fill=tk.BOTH, expand=False, pady=(0, 10))
+            self.on_parameter_change_ipe(None)
+        elif section_type == "Puente nuevo":
+            self.frame_ipe.pack_forget()
+            self.frame_personalizada.pack_forget()
+            self.frame_puente_nuevo.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
+            self.on_parameter_change_puente_nuevo(None)
+            # Aquí podrías cargar valores por defecto o actuales para el puente nuevo
+        else:  # Personalizada
+            self.frame_ipe.pack_forget()
+            self.frame_puente_nuevo.pack_forget()
+            self.frame_personalizada.pack(fill=tk.BOTH, expand=True)
+            # Mostrar los puntos actuales en el texto
+            if self.current_points is not None:
+                import json
+                puntos_json = json.dumps(self.current_points.tolist(), indent=2)
+                self.text_puntos.delete(1.0, tk.END)
+                self.text_puntos.insert(1.0, puntos_json)
+
+    def load_custom_points(self):
+        """Carga puntos personalizados desde el texto"""
+        try:
+            import json
+            text = self.text_puntos.get(1.0, tk.END).strip()
+            puntos = json.loads(text)
+            self.current_points = np.array(puntos)
+            self.current_properties = calculate_section_properties(self.current_points)
+            self.update_plot()
+            self.update_results()
+            messagebox.showinfo("Éxito", "Puntos cargados correctamente")
+        except json.JSONDecodeError:
+            messagebox.showerror("Error", "Formato JSON inválido")
+        except Exception as e:
+            messagebox.showerror("Error", f"Error al cargar puntos: {str(e)}")
+
+    def on_section_changed(self, event):
+        """Se ejecuta cuando cambia la sección seleccionada"""
+        idx = self.combo_seccion.current()
+        if idx >= 0:
+            seccion = self.secciones_data['secciones'][idx]
+            self.current_section_name = seccion['nombre']
+            section_type = seccion.get('tipo', 'otro')
+
+            # Actualizar el combo de tipo
+            if section_type == 'ipe':
+                self.combo_tipo.set("IPE")
+            elif section_type == 'puente_nuevo':
+                self.combo_tipo.set("Puente nuevo")
+            else:
+                self.combo_tipo.set("Personalizada")
+
+            # Si tiene puntos directos, cargarlos
+            if 'puntos' in seccion:
+                self.current_points = np.array(seccion['puntos'])
+                if section_type == 'ipe':
+                    self.current_properties = calculate_section_properties(self.current_points)
+                if section_type == 'puente_nuevo':
+                    self.current_properties = calculate_section_properties(self.current_points)
+                self.update_plot()
+                self.update_results()
+
+                # Si además es IPE, establecer los valores
+                if section_type == 'ipe' and 'parametros' in seccion:
+                    params = seccion['parametros']
+                    self.entry_H.delete(0, tk.END)
+                    self.entry_H.insert(0, f"{params['H']:.6f}")
+                    self.entry_b.delete(0, tk.END)
+                    self.entry_b.insert(0, f"{params['b']:.6f}")
+                    self.entry_tf.delete(0, tk.END)
+                    self.entry_tf.insert(0, f"{params['tf']:.6f}")
+                    self.entry_tw.delete(0, tk.END)
+                    self.entry_tw.insert(0, f"{params['tw']:.6f}")
+                    # Mostrar frame IPE
+                    self.frame_personalizada.pack_forget()
+                    self.frame_ipe.pack(fill=tk.BOTH, expand=False, pady=(0, 10))
+                elif section_type == 'puente_nuevo' and 'parametros' in seccion:
+                    params = seccion['parametros']
+                    self.entry_h_puente.delete(0, tk.END)
+                    self.entry_h_puente.insert(0, f"{params['h']:.6f}")
+                    self.entry_b_puente.delete(0, tk.END)
+                    self.entry_b_puente.insert(0, f"{params['b']:.6f}")
+                    self.entry_tf_puente.delete(0, tk.END)
+                    self.entry_tf_puente.insert(0, f"{params['tf']:.6f}")
+                    self.entry_tw_puente.delete(0, tk.END)
+                    self.entry_tw_puente.insert(0, f"{params['tw']:.6f}")
+                    self.entry_ha_puente.delete(0, tk.END)
+                    self.entry_ha_puente.insert(0, f"{params['ha']:.6f}")
+                    self.entry_ta_puente.delete(0, tk.END)
+                    self.entry_ta_puente.insert(0, f"{params['ta']:.6f}")
+                    self.entry_tr_puente.delete(0, tk.END)
+                    self.entry_tr_puente.insert(0, f"{params['tr']:.6f}")
+                    self.entry_theta_puente.delete(0, tk.END)
+                    self.entry_theta_puente.insert(0, f"{params['theta']:.6f}")
+                    # Mostrar frame puente nuevo
+                    self.frame_ipe.pack_forget()
+                    self.frame_personalizada.pack_forget()
+                    self.frame_puente_nuevo.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
+                else:
+                    # Mostrar frame personalizado
+                    self.frame_ipe.pack_forget()
+                    self.frame_puente_nuevo.pack_forget()
+                    self.frame_personalizada.pack(fill=tk.BOTH, expand=True)
+                    import json
+                    puntos_json = json.dumps(self.current_points.tolist(), indent=2)
+                    self.text_puntos.delete(1.0, tk.END)
+                    self.text_puntos.insert(1.0, puntos_json)
+            # Si solo tiene parámetros IPE, generar puntos
+            elif section_type == 'ipe' and 'parametros' in seccion:
+                params = seccion['parametros']
+                self.combo_tipo.set("IPE")
+                self.entry_H.delete(0, tk.END)
+                self.entry_H.insert(0, f"{params['H']:.6f}")
+                self.entry_b.delete(0, tk.END)
+                self.entry_b.insert(0, f"{params['b']:.6f}")
+                self.entry_tf.delete(0, tk.END)
+                self.entry_tf.insert(0, f"{params['tf']:.6f}")
+                self.entry_tw.delete(0, tk.END)
+                self.entry_tw.insert(0, f"{params['tw']:.6f}")
+                self.on_parameter_change_ipe(None)
+            elif section_type == 'puente_nuevo' and 'parametros' in seccion:
+                params = seccion['parametros']
+                self.combo_tipo.set("Puente nuevo")
+                self.entry_h_puente.delete(0, tk.END)
+                self.entry_h_puente.insert(0, f"{params['h']:.6f}")
+                self.entry_b_puente.delete(0, tk.END)
+                self.entry_b_puente.insert(0, f"{params['b']:.6f}")
+                self.entry_tf_puente.delete(0, tk.END)
+                self.entry_tf_puente.insert(0, f"{params['tf']:.6f}")
+                self.entry_tw_puente.delete(0, tk.END)
+                self.entry_tw_puente.insert(0, f"{params['tw']:.6f}")
+                self.entry_ha_puente.delete(0, tk.END)
+                self.entry_ha_puente.insert(0, f"{params['ha']:.6f}")
+                self.entry_ta_puente.delete(0, tk.END)
+                self.entry_ta_puente.insert(0, f"{params['ta']:.6f}")
+                self.entry_tr_puente.delete(0, tk.END)
+                self.entry_tr_puente.insert(0, f"{params['tr']:.6f}")
+                self.entry_theta_puente.delete(0, tk.END)
+                self.entry_theta_puente.insert(0, f"{params['theta']:.6f}")
+                self.on_parameter_change_puente_nuevo(None)
+
+
+    def on_parameter_change_ipe(self, event=None):
+        """Se ejecuta cuando cambien los valores de entrada"""
+        try:
+            H = float(self.entry_H.get())
+            b = float(self.entry_b.get())
+            tf = float(self.entry_tf.get())
+            tw = float(self.entry_tw.get())
+
+            # Validar valores básicos
+            if H <= 0 or b <= 0 or tf <= 0 or tw <= 0:
+                return
+
+            # Generar puntos y calcular
+            self.current_points = generate_ipe_points(H, b, tf, tw)
+            self.current_properties = calculate_section_properties(self.current_points)
+
+            # Actualizar visualización
+            self.update_plot()
+            self.update_results()
+        except ValueError:
+            # Ignorar si los valores no son números válidos
+            pass
+
+    def on_parameter_change_puente_nuevo(self, event=None):
+        """Se ejecuta cuando cambien los valores de entrada"""
+        try:
+            h = float(self.entry_h_puente.get())
+            b = float(self.entry_b_puente.get())
+            tf = float(self.entry_tf_puente.get())
+            tw = float(self.entry_tw_puente.get())
+            ha = float(self.entry_ha_puente.get())
+            ta = float(self.entry_ta_puente.get())
+            tr = float(self.entry_tr_puente.get())
+            theta = float(self.entry_theta_puente.get())
+
+            # Validar valores básicos
+            if h <= 0 or b <= 0 or tf <= 0 or tw <= 0 or ha <= 0 or ta <= 0 or tr <= 0:
+                return
+
+            # Generar puntos y calcular
+            self.current_points = generate_puente_nuevo_points(h, b, tf, tw, ha, ta, tr, theta)
+            self.current_properties = calculate_section_properties(self.current_points)
+
+            # Actualizar visualización
+            self.update_plot()
+            self.update_results()
+        except ValueError:
+            # Ignorar si los valores no son números válidos
+            pass
+
+    def update_plot(self):
+        """Actualiza el gráfico de la sección"""
+        self.ax.clear()
+
+        points = self.current_points
+        props = self.current_properties
+
+        px = points[:, 0]
+        py = points[:, 1]
+        xg = props['xg_orig']
+        yg = props['yg_orig']
+        bmax = props['bmax']
+        hmax = props['hmax']
+        tetha = props['tetha']
+
+        # Dibujar sección
+        self.ax.plot(px, py, 'b-', linewidth=2)
+        self.ax.fill(px, py, facecolor="lightblue", alpha=0.5)
+
+        # Centro de gravedad
+        self.ax.plot(xg, yg, 'ro', markersize=8, label='CDG')
+
+        # Ejes principales
+        tt = min(bmax, hmax)
+        self.ax.arrow(xg, yg, tt/3, 0, head_width=tt/30, head_length=tt/30, fc='green', ec='green')
+        self.ax.arrow(xg, yg, 0, tt/3, head_width=tt/30, head_length=tt/30, fc='green', ec='green')
+
+        tet = tetha * (math.pi) / 180
+        self.ax.arrow(xg, yg, tt/3*math.cos(tet), tt/3*math.sin(tet), head_width=tt/30, head_length=tt/30, fc='red', ec='red')
+        self.ax.arrow(xg, yg, -tt/3*math.sin(tet), tt/3*math.cos(tet), head_width=tt/30, head_length=tt/30, fc='red', ec='red')
+
+        self.ax.set_aspect('equal')
+        self.ax.grid(True, alpha=0.3)
+        self.ax.set_xlabel('X (m)', fontweight='bold')
+        self.ax.set_ylabel('Y (m)', fontweight='bold')
+        self.ax.set_title(self.current_section_name or 'Sección IPE', fontweight='bold')
+        self.ax.legend()
+
+        self.figure.tight_layout()
+        self.canvas.draw()
+
+    def update_results(self):
+        """Actualiza el panel de resultados"""
+        props = self.current_properties
+
+        results_text = f"""PROPIEDADES CALCULADAS
+
+GEOMÉTRICAS:
+ Área: {props['area']:.2f} cm²
+ Perímetro: {props['perimetro']:.2f} cm
+ b_max: {props['bmax']*100:.2f} cm
+ h_max: {props['hmax']*100:.2f} cm
+
+CDG (ref. origen):
+ X: {props['xg']:.2f} cm
+ Y: {props['yg']:.2f} cm
+
+INERCIA (eje Xg-Yg):
+ Ixg: {props['ixg']:.2f} cm⁴
+ Iyg: {props['iyg']:.2f} cm⁴
+ Pxyg: {props['pxyg']:.2f} cm⁴
+ rx: {props['rx']:.2f} cm
+ ry: {props['ry']:.2f} cm
+
+INERCIA (ejes principales):
+ Imax: {props['imax']:.2f} cm⁴
+ Imin: {props['imin']:.2f} cm⁴
+ θ: {props['tetha']:.2f}°
+ rmax: {props['rmax']:.2f} cm
+ rmin: {props['rmin']:.2f} cm
+
+MÓDULO RESISTENTE:
+ Wel1x: {props['wel1x']:.2f} cm³
+ Wel2x: {props['wel2x']:.2f} cm³
+ Wel1y: {props['wel1y']:.2f} cm³
+ Wel2y: {props['wel2y']:.2f} cm³
+
+MECÁNICAS:
+ Peso: {props['peso']:.2f} kg/m
+ Nel: {props['nel']:.2f} kN
+ Melx: {props['melx']:.2f} kN·m
+ Mely: {props['mely']:.2f} kN·m
+"""
+
+        self.text_results.config(state=tk.NORMAL)
+        self.text_results.delete(1.0, tk.END)
+        self.text_results.insert(1.0, results_text)
+        self.text_results.config(state=tk.DISABLED)
+
+    def save_new_section(self):
+        """Guarda la sección actual en el JSON"""
+        if not self.current_section_name or self.current_points is None:
+            messagebox.showerror("Error", "Selecciona una sección primero")
+            return
+
+        # Preguntar nombre de la nueva sección
+        new_name = tk.simpledialog.askstring("Guardar sección", "Nombre de la nueva sección:")
+        if not new_name:
+            return
+
+        # Verificar si ya existe
+        exists = any(s['nombre'] == new_name for s in self.secciones_data['secciones'])
+        if exists:
+            should_overwrite = messagebox.askyesno("Ya existe", f"La sección '{new_name}' ya existe. ¿Sobreescribir?")
+            if not should_overwrite:
+                return
+
+        # Detectar tipo de sección
+        section_type = self.combo_tipo.get().lower()
+        if section_type == "ipe":
+            section_type = "ipe"
+        elif section_type == "puente nuevo":
+            section_type = "puente_nuevo"
+        else:
+            section_type = "otro"
+
+        # Crear nueva sección con puntos
+        new_section = {
+            'nombre': new_name,
+            'tipo': section_type,
+            'puntos': self.current_points.tolist()  # Guardar los puntos actuales
+        }
+
+        # Si es IPE, guardar también los parámetros
+        if section_type == "ipe":
+            try:
+                H = float(self.entry_H.get())
+                b = float(self.entry_b.get())
+                tf = float(self.entry_tf.get())
+                tw = float(self.entry_tw.get())
+
+                if H > 0 and b > 0 and tf > 0 and tw > 0:  # Validar que sean mayores a 0
+                    new_section['parametros'] = {
+                        'H': H,
+                        'b': b,
+                        'tf': tf,
+                        'tw': tw
+                    }
+            except:
+                pass  # Si no se pueden leer, solo guardamos puntos
+        elif section_type == "puente_nuevo":
+            try:
+                h = float(self.entry_h_puente.get())
+                b = float(self.entry_b_puente.get())
+                tf = float(self.entry_tf_puente.get())
+                tw = float(self.entry_tw_puente.get())
+                ha = float(self.entry_ha_puente.get())
+                ta = float(self.entry_ta_puente.get())
+                tr = float(self.entry_tr_puente.get())
+                theta = float(self.entry_theta_puente.get())
+
+                if all(v > 0 for v in [h, b, tf, tw, ha, ta, tr]) and 0 <= theta <= 360:
+                    new_section['parametros'] = {
+                        'h': h,
+                        'b': b,
+                        'tf': tf,
+                        'tw': tw,
+                        'ha': ha,
+                        'ta': ta,
+                        'tr': tr,
+                        'theta': theta
+                    }
+            except:
+                pass  # Si no se pueden leer, solo guardamos puntos
+
+        # Eliminar si existe
+        self.secciones_data['secciones'] = [s for s in self.secciones_data['secciones'] if s['nombre'] != new_name]
+        self.secciones_data['secciones'].append(new_section)
+
+        self.save_json()
+
+        # Actualizar combo
+        self.combo_seccion['values'] = [s['nombre'] for s in self.secciones_data['secciones']]
+        self.combo_seccion.set(new_name)
+
+        messagebox.showinfo("Éxito", f"Sección '{new_name}' guardada correctamente")
+
+
+if __name__ == "__main__":
+    app = SectionDesignerApp()
+    app.mainloop()

+ 423 - 0
secciones.py

@@ -0,0 +1,423 @@
+
+
+import numpy as np
+import matplotlib.pyplot as plt
+import math
+import pandas as pd
+import openpyxl
+
+
+print("")
+print("")
+print("DISEÑO DE SECCIONES")
+print("")
+print("")
+
+#-------------------------------------MATERIALES------------------------------------------------
+dens=7850       #kg/m3
+fy=355          #MPA
+gammas=1.05     #Factor seguridad acero
+Eyoung=210000        #MPa
+nu=0.3          #Coef Poisson
+
+#---
+
+fyd=fy*10**6/gammas         # Pa (resistencia diseño)
+Eyoung=Eyoung*10**6                   # Pa (rigidez)
+G=Eyoung/(2*(1+nu))              # Pa, Módulo Cizalladura
+
+
+
+
+#----------------------------------------------------CARGAR DATOS DESDE EXCEL (debe estar en la misma carpeta que el archivo python)
+archivo_excel = 'C:/Users/Daniel.p/Documents/Automatizaciones/Propiedades seccion/datos_generales.xlsx'
+#base_datos=openpyxl.load_workbook('/datos_generales.xlsx')
+base_datos=openpyxl.load_workbook(archivo_excel)
+
+
+
+#obtener los nombres de las secciones.
+hojas =base_datos.sheetnames
+print("Secciones Disponibles")
+nsec=np.size(hojas)
+
+#Enumera todas las hojas menos la primera con el nombre de la sección
+for i in range(nsec-1):
+    k=str(i+1)
+    print("     - "+k+". "+hojas[i+1])
+
+
+print("")
+print("")
+sec=input("Elige una sección: ")
+sec=int(sec)
+nombreseccion=hojas[sec]
+
+#Obtener el nº de filas de la sección
+filas=base_datos[hojas[sec]].max_row
+#print("filas = "+filas)
+
+npuntos=filas-1
+
+
+#importamos los valores de los puntos
+puntos=np.zeros([npuntos,2])
+for i in range(npuntos):
+    for j in range(2):
+        puntos[i,j]=base_datos[hojas[sec]].cell(row=i+2,column=j+1).value
+
+px=np.zeros([npuntos,1])
+py=np.zeros([npuntos,1])
+for i in range(npuntos):
+    px[i]=puntos[i,0]
+    py[i]=puntos[i,1]
+
+
+#--------------------------CÁLCULO DEL ANCHO y CANTO MÁXIMO (segun ejes de referencia)--------------------------------
+bmax=np.amax(px)-np.amin(px)
+hmax=np.amax(py)-np.amin(py)
+
+
+
+
+#------------------------CALCULO DEL PERÍMETRO----------------------------- ## ERROR DE PERIMETRO EN PIEZAS COMPUESTAS
+long_i=np.zeros([npuntos-1,1])
+for i in range(npuntos-1):
+       long_i[i]=((puntos[i+1,0]-puntos[i,0])**2+(puntos[i+1,1]-puntos[i,1])**2)**(1/2)
+p=abs(sum(long_i))
+perimetro=p[0]
+
+
+
+#--------------------------------CÁLCULO DEL ÁREA-------------------------------------------------------
+area_i=np.zeros([npuntos-1,1])
+for i in range(npuntos-1):
+       area_i[i]=(puntos[i+1,0]-puntos[i,0])*(puntos[i+1,1]+puntos[i,1])/2
+a=(sum(area_i))
+area=abs(a[0])
+
+
+#--------------------------PESO POR METRO DE SECCIÓN-----------------------------------------------
+peso=area*dens 
+
+
+#-----------------------------------------CÁLCULO DEL CENTRO DE GRAVEDAD ----------------------------------------------------------
+cdg_i=np.zeros([npuntos,2])
+for i in range(npuntos-1):
+    h1=puntos[i,1]
+    h2=puntos[i+1,1]
+    b=puntos[i+1,0]-puntos[i,0]
+    d=puntos[i,0]
+
+    #Centro de gravedad y_i
+    if h1+h2==0:
+        cdg_i[i,1]=0
+    else:
+        cdg_i[i,1]=1/3*(h1*h1+h1*h2+h2*h2)/(h1+h2)
+
+    #centro de gravedad x_i
+    if h1+h2==0:
+        cdg_i[i,0]=d+b/2
+    else:
+        cdg_i[i,0]=d+b/3*(h1+2*h2)/(h1+h2)
+       
+
+#Cálculo del momento estático 
+statico_i=np.zeros([npuntos,2])
+for i in range(npuntos-1):
+        #momento_estático respecto eje x (para cdg,y)
+        statico_i[i,1]=area_i[i,0]*cdg_i[i,1]    
+         #momento_estático respecto eje y (para cdg,x)
+        statico_i[i,0]=area_i[i,0]*cdg_i[i,0]   
+
+
+#cdg=sum(statico_i)/sum(area_i)
+cdg=sum(statico_i)/a
+xg=(cdg[0])
+yg=(cdg[1])
+
+
+#Cálculo de las fibras más alejadas desde el CDG:
+v1y=np.amax(py)-yg
+v2y=np.amin(py)-yg
+
+v1x=np.amax(px)-xg
+v2x=np.amin(px)-xg
+
+
+
+
+# ----------------- CÁLCULO MOMENTOS DE INERCIA ----------------------------------
+
+inercia_i=np.zeros([npuntos,3])
+for i in range(npuntos-1):
+    h1=puntos[i,1]
+    h2=puntos[i+1,1]
+    b=puntos[i+1,0]-puntos[i,0]
+    d=puntos[i,0]
+    xgi=cdg_i[i,0]
+    ygi=cdg_i[i,1]
+    ai=area_i[i,0] 
+
+    #momento de inercia Ixg
+    if h2>=h1 :
+        ixcuad_G_local=1/12*b*(h1*h1*h1)+b*h1*(h1/2-ygi)*(h1/2-ygi)
+        ixtriang_G_loc=1/36*b*(h2-h1)*(h2-h1)*(h2-h1)+1/2*b*(h2-h1)*((2*h1+h2)/3-ygi)*((2*h1+h2)/3-ygi)
+    else:
+        ixcuad_G_local=1/12*b*(h2*h2*h2)+b*h2*(h2/2-ygi)*(h2/2-ygi)
+        ixtriang_G_loc=1/36*b*(h1-h2)*(h1-h2)*(h1-h2)+1/2*b*(h1-h2)*((2*h2+h1)/3-ygi)*((2*h2+h1)/3-ygi)
+    inercia_i[i,0]=ixcuad_G_local+ixtriang_G_loc+ai*(yg-ygi)*(yg-ygi)
+   
+    #momento de inercia Iyg
+    if h2>=h1:
+        iycuad=1/12*h1*b*b*b+h1*b*(b/2+d-xgi)*(b/2+d-xgi)
+        iytrian=1/36*(h2-h1)*b*b*b+1/2*b*(h2-h1)*(2/3*b+d-xgi)*(2/3*b+d-xgi)
+    else:
+        iycuad=1/12*h2*b*b*b+h2*b*(b/2+d-xgi)*(b/2+d-xgi)
+        iytrian=1/36*(h1-h2)*b*b*b+1/2*b*(h1-h2)*(1/3*b+d-xgi)*(1/3*b+d-xgi)        
+    inercia_i[i,1]=iycuad+iytrian+ai*(xg-xgi)*(xg-xgi)
+
+    #producto de inercia pxyg
+    if h2>=h1:
+        pxygcuadrado=b*h1*(-h1/2+ygi)*(-d-b/2+xgi)
+        pxytriangulo=b*b*(h2-h1)*(h2-h1)/72+b*(h2-h1)/2*(-(h2-h1)/3-h1+ygi)*(-d-2/3*b+xgi)
+    else:
+        pxygcuadrado=b*h2*(-h2/2+ygi)*(-d-b/2+xgi)
+        pxytriangulo=-b*b*(h1-h2)*(h1-h2)/72+b*(h1-h2)/2*(-(h1-h2)/3-h2+ygi)*(-d-1/3*b+xgi)       
+    inercia_i[i,2]=pxygcuadrado+pxytriangulo+ai*(-xg+xgi)*(-yg+ygi)
+
+
+ig=sum(inercia_i)
+ixg=abs(ig[0])
+iyg=abs(ig[1])
+pxyg=ig[2]
+if a>=0:
+    pxyg=pxyg
+else:
+    pxyg=-pxyg
+
+#Radios de Giro
+rx=(ixg/area)**0.5
+ry=(iyg/area)**0.5
+
+#EJES PRINCIPALES DE INERCIA
+ic= (ixg+iyg)/2
+ir= (((ixg-iyg)/2)**2+pxyg**2)**0.5
+
+imax=ic+ir
+imin=ic-ir
+rmax=(imax/area)**0.5
+rmin=(imin/area)**0.5
+
+
+#obtención de la orientación de los ejes principales de inercia
+rest=ixg-iyg
+
+if abs(rest)<10**-12:
+    tetha=45
+else:
+    tetha=0.5*math.atan((pxyg*2)/(ixg-iyg))
+    tetha=abs(tetha)*180/math.pi
+
+#Criterio de signos producto de inercia
+if pxyg>0:
+    if ixg>iyg:
+         tetha=-tetha
+    else:
+        tetha=tetha
+else:
+    if ixg>iyg:
+         tetha=tetha
+    else:
+        tetha=-tetha
+
+
+#--------------------------------CALCULO DE LOS PUNTOS ROTADOS RESPECTO DE LOS EJES PRINCIPALES---------------
+pxrot=np.zeros([npuntos,1])
+pyrot=np.zeros([npuntos,1])
+for i in range(npuntos):
+    pxrot[i]=puntos[i,0]
+    pyrot[i]=puntos[i,1]
+
+#-----------------------------CÁLCULO DEL MÓDULO RESISTENTE ELÁSTICO---------------------------
+
+##SOLAMENTE ESTÁ EN EJES PRINCIPALES
+wel1x=abs(ixg/v1y)
+wel2x=abs(ixg/v2y)
+wel1y=abs(iyg/v1x)
+wel2y=abs(iyg/v2x)
+
+
+
+
+
+
+
+
+
+#-----------------AXIL ELÁSTICO
+nel=area*fyd
+
+#--------------------MOMENTO ELÁSTICO--------------------
+melx=min(wel1x,wel2x)*fyd
+mely=min(wel1y,wel2y)*fyd
+
+
+
+
+
+
+
+
+
+
+
+#--------------------------------------DIBUJO DE LA SECCIÓN (colocar antes de imprimir resultados para no modificar las unidades)-------------------
+fig, ax=plt.subplots()
+#Nombre de la sección y notación de ejes
+plt.title(nombreseccion +" (m)",color="SteelBlue",fontsize=16,fontweight="bold")
+plt.xlabel('X',fontsize=14,fontweight="bold")
+plt.ylabel('Y',fontsize=14,fontweight="bold")
+ax.set_aspect("equal")
+
+#Dibujamos los puntos
+ax.plot(px,py)
+ax.fill(px,py,facecolor="lightblue")
+#Dibujamos el centro de gravedad
+ax.plot(xg, yg, 'ro',markersize=4, fillstyle='full', markerfacecolor='red')
+
+#Dibujamos los ejes X,Y
+tt=min(bmax,hmax)
+ax.arrow(xg,yg,tt/3,0,head_width=tt/30, head_length=tt/30, fc='green', ec='green')
+ax.arrow(xg,yg,0,tt/3,head_width=tt/30, head_length=tt/30, fc='green', ec='green')
+
+tet=tetha*(math.pi)/180
+#Dibujamos los ejes principales
+ax.arrow(xg,yg,tt/3*math.cos(tet),tt/3*math.sin(tet),head_width=tt/30, head_length=tt/30, fc='red', ec='red')
+ax.arrow(xg,yg,-tt/3*math.sin(tet),tt/3*math.cos(tet),head_width=tt/30, head_length=tt/30, fc='red', ec='red')
+
+
+
+#--------------------------------------------------------------------------IMPRIMIR RESULTADOS (siempre al final)---------------------------------------------------
+
+
+# UNIDADES REDONDEO
+redondeo=2
+
+# ------------------------------------------------CAMBIO DE UNIDADES ------------
+# Pasar a cm
+pot=2
+
+bmax=bmax*10**pot
+hmax=hmax*10**pot
+perimetro = perimetro*10**pot
+xg=xg*10**pot
+yg=yg*10**pot
+v1y=v1y*10**pot
+v2y=v2y*10**pot
+v1x=v1x*10**pot
+v2x=v2x*10**pot
+rx=rx*10**pot
+ry=ry*10**pot
+rmax=rmax*10**pot
+rmin=rmin*10**pot
+
+area=area*10**(pot*2)
+
+wel1x=wel1x*10**(pot*3)
+wel2x=wel2x*10**(pot*3)
+wel1y=wel1y*10**(pot*3)
+wel2y=wel2y*10**(pot*3)
+
+ixg=ixg*10**(pot*4)
+iyg=iyg*10**(pot*4)
+pxyg=pxyg*10**(pot*4)
+imax=imax*10**(pot*4)
+imin=imin*10**(pot*4)
+
+
+# pasar a KN
+nel=nel/1000
+melx=melx/1000
+mely=mely/1000
+
+
+Eyoung=Eyoung/(10**6)
+G=G/(10**6)
+
+
+
+
+print("")
+print("")
+
+print(" ___________________________________________________________________________________________________________")
+print("")
+
+print("  MATERIAL DE LA SECCIÓN")
+print("   - Densidad = "+str(dens)+ " kg/m3")
+print("   - σy = "+str(fy)+ " MPa")
+print("   - γm = "+str(gammas)+ " [ ]")
+print("   - E = "+str(Eyoung)+ " MPa")
+print("   - υ = "+str(nu)+ " [ ]")
+print("   - G = "+str(round(G,0))+ " MPa")
+print(" ___________________________________________________________________________________________________________")
+
+print("  DATOS GEOMÉTRICOS")
+print("   - Área = " + str(round(area,redondeo))+ " cm2")
+print("   - Perímetro = " + str(round(perimetro,redondeo))+ " cm")
+print("")
+
+
+
+print("  DIMENSIONES MÁXIMAS")
+print("   - b_max = "+str(round(bmax,redondeo))+" cm")
+print("   - h_max = "+str(round(hmax,redondeo))+" cm")
+print("")
+
+
+print("  POSICIÓN DEL CDG (respecto ejes de referencia)")
+print("   - CDG = [ "+ str(round(xg,redondeo))+ " ; "+str(round(yg,redondeo))+" ] cm")
+print("")
+
+print("  FIBRAS MÁS ALEJADAS DESDE G")
+print("   - v1y = "+str(round(v1y,redondeo))+" cm")
+print("   - v2y = "+str(round(v2y,redondeo))+" cm")
+print("   - v1x = "+str(round(v1x,redondeo))+" cm")
+print("   - v2x = "+str(round(v2x,redondeo))+" cm")
+print("")
+
+print("  TENSOR DE INERCIA EN G RESPECTO DE LOS EJES Xg E Yg")
+print("   - IxG = "+str(round(ixg,redondeo))+" cm4")
+print("   - IyG = "+str(round(iyg,redondeo))+" cm4")
+print("   - PxyG = "+str(round(pxyg,redondeo))+" cm4")
+print("   - rx = "+str(round(rx,redondeo))+" cm")
+print("   - ry = "+str(round(ry,redondeo))+" cm")
+print("")
+
+print("  TENSOR PRINCIPAL DE INERCIA EN G")
+print("   - Imax = "+str(round(imax,redondeo))+" cm4")
+print("   - Imin = "+str(round(imin,redondeo))+" cm4")
+print("   - ϴ (orientación ejes princiaples respecto Xg e Yg) = "+str(round(tetha,redondeo))+" º")
+print("   - rmax = "+str(round(rmax,redondeo))+" cm")
+print("   - rmin = "+str(round(rmin,redondeo))+" cm")
+print("")
+
+
+print("  MÓDULO RESISTENTE ELÁSTICO")
+print("   - wel1x = "+str(round(wel1x,redondeo))+" cm3")
+print("   - wel2x = "+str(round(wel2x,redondeo))+" cm3")
+print("   - wel1y = "+str(round(wel1y,redondeo))+" cm3")
+print("   - wel2y = "+str(round(wel2y,redondeo))+" cm3")
+print("")
+print(" ___________________________________________________________________________________________________________")
+
+
+
+print("   PROPIEDADES MECÁNICAS")
+print("   - Peso_unitario = "+str(round(peso,redondeo))+" kg/m")
+print("   - Nel = "+str(round(nel,redondeo))+" kN")
+print("   - Melx = "+str(round(melx,redondeo))+" kN·m")
+print("   - Mely = "+str(round(mely,redondeo))+" kN·m")
+print("")
+plt.show()

+ 444 - 0
secciones_config.json

@@ -0,0 +1,444 @@
+{
+  "secciones": [
+    {
+      "nombre": "IPE-100",
+      "tipo": "ipe",
+      "parametros": {
+        "H": 0.1,
+        "b": 0.055,
+        "tf": 0.0067,
+        "tw": 0.0043
+      },
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          0.055,
+          0.0
+        ],
+        [
+          0.055,
+          0.0067
+        ],
+        [
+          0.0305,
+          0.0067
+        ],
+        [
+          0.0305,
+          0.0933
+        ],
+        [
+          0.055,
+          0.0933
+        ],
+        [
+          0.055,
+          0.1
+        ],
+        [
+          0.0,
+          0.1
+        ],
+        [
+          0.0,
+          0.0933
+        ],
+        [
+          0.0245,
+          0.0933
+        ],
+        [
+          0.0245,
+          0.0067
+        ],
+        [
+          0.0,
+          0.0067
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ]
+    },
+    {
+      "nombre": "IPE-200",
+      "tipo": "ipe",
+      "parametros": {
+        "H": 0.2,
+        "b": 0.1,
+        "tf": 0.0085,
+        "tw": 0.0053
+      },
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          0.1,
+          0.0
+        ],
+        [
+          0.1,
+          0.0085
+        ],
+        [
+          0.05265,
+          0.0085
+        ],
+        [
+          0.05265,
+          0.1915
+        ],
+        [
+          0.1,
+          0.1915
+        ],
+        [
+          0.1,
+          0.2
+        ],
+        [
+          0.0,
+          0.2
+        ],
+        [
+          0.0,
+          0.1915
+        ],
+        [
+          0.04735,
+          0.1915
+        ],
+        [
+          0.04735,
+          0.0085
+        ],
+        [
+          0.0,
+          0.0085
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ]
+    },
+    {
+      "nombre": "IPE-300",
+      "tipo": "ipe",
+      "parametros": {
+        "H": 0.3,
+        "b": 0.15,
+        "tf": 0.0107,
+        "tw": 0.0063
+      },
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          0.15,
+          0.0
+        ],
+        [
+          0.15,
+          0.0107
+        ],
+        [
+          0.079,
+          0.0107
+        ],
+        [
+          0.079,
+          0.2893
+        ],
+        [
+          0.15,
+          0.2893
+        ],
+        [
+          0.15,
+          0.3
+        ],
+        [
+          0.0,
+          0.3
+        ],
+        [
+          0.0,
+          0.2893
+        ],
+        [
+          0.071,
+          0.2893
+        ],
+        [
+          0.071,
+          0.0107
+        ],
+        [
+          0.0,
+          0.0107
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ]
+    },
+    {
+      "nombre": "Triangulo",
+      "tipo": "otro",
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          1.0,
+          0.0
+        ],
+        [
+          1.0,
+          1.0
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ]
+    },
+    {
+      "nombre": "Cuadrado",
+      "tipo": "otro",
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          1.0,
+          0.0
+        ],
+        [
+          1.0,
+          1.0
+        ],
+        [
+          0.0,
+          1.0
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ]
+    },
+    {
+      "nombre": "sdf",
+      "tipo": "puente_nuevo",
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          0.055,
+          0.0
+        ],
+        [
+          0.055,
+          0.0067
+        ],
+        [
+          0.02965,
+          0.0067
+        ],
+        [
+          0.02965,
+          0.09330000000000001
+        ],
+        [
+          0.055,
+          0.09330000000000001
+        ],
+        [
+          0.055,
+          0.1
+        ],
+        [
+          0.0,
+          0.1
+        ],
+        [
+          0.0,
+          0.09330000000000001
+        ],
+        [
+          0.02535,
+          0.09330000000000001
+        ],
+        [
+          0.02535,
+          0.0067
+        ],
+        [
+          0.0,
+          0.0067
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ],
+      "parametros": {
+        "h": 0.3,
+        "b": 0.15,
+        "tf": 0.0107,
+        "tw": 0.0063,
+        "ha": 0.02,
+        "ta": 0.01,
+        "tr": 0.01,
+        "theta": 45.0
+      }
+    },
+    {
+      "nombre": "Nuevo",
+      "tipo": "puente_nuevo",
+      "puntos": [
+        [
+          0.0,
+          0.0
+        ],
+        [
+          0.15,
+          0.0
+        ],
+        [
+          0.15,
+          0.0267
+        ],
+        [
+          0.0809,
+          0.11839879717397031
+        ],
+        [
+          0.0809,
+          0.09596665526881679
+        ],
+        [
+          0.13699999999999998,
+          0.021519440775911794
+        ],
+        [
+          0.13699999999999998,
+          0.0122
+        ],
+        [
+          0.0809,
+          0.0122
+        ],
+        [
+          0.0809,
+          0.3378
+        ],
+        [
+          0.13699999999999998,
+          0.3378
+        ],
+        [
+          0.13699999999999998,
+          0.32848055922408814
+        ],
+        [
+          0.0809,
+          0.25403334473118316
+        ],
+        [
+          0.0809,
+          0.23160120282602967
+        ],
+        [
+          0.15,
+          0.3233
+        ],
+        [
+          0.15,
+          0.35
+        ],
+        [
+          0.0,
+          0.35
+        ],
+        [
+          0.0,
+          0.3233
+        ],
+        [
+          0.0691,
+          0.23160120282602967
+        ],
+        [
+          0.0691,
+          0.25403334473118316
+        ],
+        [
+          0.013,
+          0.32848055922408814
+        ],
+        [
+          0.013,
+          0.3378
+        ],
+        [
+          0.0691,
+          0.3378
+        ],
+        [
+          0.0691,
+          0.0122
+        ],
+        [
+          0.013,
+          0.0122
+        ],
+        [
+          0.013,
+          0.021519440775911794
+        ],
+        [
+          0.0691,
+          0.09596665526881679
+        ],
+        [
+          0.0691,
+          0.11839879717397031
+        ],
+        [
+          0.0,
+          0.0267
+        ],
+        [
+          0.0,
+          0.0
+        ]
+      ],
+      "parametros": {
+        "h": 0.35,
+        "b": 0.15,
+        "tf": 0.0122,
+        "tw": 0.0118,
+        "ha": 0.0145,
+        "ta": 0.013,
+        "tr": 0.0135,
+        "theta": 53.0
+      }
+    }
+  ]
+}