import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import ConvexHull

# Leer el archivo manualmente
data = []
with open('resultados1.txt', 'r', encoding='utf-8') as f:
    header = f.readline().strip().split('\t')
    for line in f:
        values = line.strip().split('\t')
        if len(values) >= 9:  # Asegurar que hay suficientes columnas
            data.append(values)

# Encontrar índices de columnas
tf_idx = header.index('tf')
tw_idx = header.index('tw')
flecha_idx = header.index('Flecha_Media')

# Convertir datos a números
def convert_value(x):
    """Convierte string a float, manejando formato con coma decimal"""
    try:
        return float(x.replace(',', '.').strip())
    except:
        return np.nan

tf_list = []
tw_list = []
flecha_list = []

for row in data:
    tf_val = convert_value(row[tf_idx])
    tw_val = convert_value(row[tw_idx])
    flecha_val = convert_value(row[flecha_idx])
    
    if not (np.isnan(tf_val) or np.isnan(tw_val) or np.isnan(flecha_val)):
        tf_list.append(tf_val)
        tw_list.append(tw_val)
        flecha_list.append(flecha_val)

tf = np.array(tf_list)
tw = np.array(tw_list)
flecha_media = np.array(flecha_list)

# Función para calcular la frontera de Pareto (versión mejorada)
def pareto_frontier(tf, tw, flecha):
    """
    Calcula los puntos que pertenecen a la frontera de Pareto.
    Queremos minimizar tf, tw y el valor absoluto de flecha (magn itud de deflexión).
    Un punto es Pareto si no hay otro punto que sea mejor en todos los criterios simultáneamente.
    """
    n = len(tf)
    is_pareto = np.ones(n, dtype=bool)
    
    # Usar valor absoluto de flecha para la comparación
    flecha_abs = np.abs(flecha)
    
    for i in range(n):
        for j in range(n):
            if i != j:
                # El punto j domina al punto i si es mejor (≤) en todos los criterios
                # y estrictamente mejor en al menos uno
                if (tf[j] <= tf[i] and tw[j] <= tw[i] and flecha_abs[j] <= flecha_abs[i]) and \
                   (tf[j] < tf[i] or tw[j] < tw[i] or flecha_abs[j] < flecha_abs[i]):
                    is_pareto[i] = False
                    break
    
    return is_pareto

# Calcular frontera de Pareto
pareto_mask = pareto_frontier(tf, tw, flecha_media)
pareto_tf = tf[pareto_mask]
pareto_tw = tw[pareto_mask]
pareto_flecha = flecha_media[pareto_mask]

print(f"Total de puntos: {len(tf)}")
print(f"Puntos en la frontera de Pareto: {len(pareto_tf)}")

# Crear figura 3D
fig = plt.figure(figsize=(14, 10))
ax = fig.add_subplot(111, projection='3d')

# Graficar todos los puntos
scatter_all = ax.scatter(tf, tw, flecha_media, c='lightblue', marker='o', s=50, alpha=0.6, label='Todos los puntos')

# Graficar la frontera de Pareto
scatter_pareto = ax.scatter(pareto_tf, pareto_tw, pareto_flecha, c='red', marker='*', s=200, alpha=0.9, label='Frontera de Pareto')

# Intentar graficar la superficie convexa de la frontera de Pareto
if len(pareto_tf) >= 4:
    try:
        # Crear matriz de coordenadas para la frontera de Pareto
        points_pareto = np.column_stack((pareto_tf, pareto_tw, pareto_flecha))
        
        # Calcular la envolvente convexa (convex hull)
        hull = ConvexHull(points_pareto)
        
        # Graficar la superficie
        for simplex in hull.simplices:
            # Crear triángulos de la superficie
            triangle = points_pareto[simplex]
            # Graficar los bordes del triángulo
            for i in range(3):
                ax.plot(triangle[[i, (i+1)%3], 0], 
                       triangle[[i, (i+1)%3], 1], 
                       triangle[[i, (i+1)%3], 2], 'r-', alpha=0.3, linewidth=0.5)
    except Exception as e:
        print(f"No se pudo graficar la superficie convexa: {e}")

# Etiquetas de ejes
ax.set_xlabel('tf (espesor del ala)', fontsize=12, fontweight='bold')
ax.set_ylabel('tw (espesor del alma)', fontsize=12, fontweight='bold')
ax.set_zlabel('Flecha_Media (deflexión)', fontsize=12, fontweight='bold')
ax.set_title('Nube de Puntos 3D y Frontera de Pareto\ntf vs tw vs Flecha_Media', fontsize=14, fontweight='bold')

# Leyenda
ax.legend(loc='upper right', fontsize=11)

# Ajustar vista
ax.view_init(elev=20, azim=45)

plt.tight_layout()
plt.savefig('grafico_3d_pareto.png', dpi=300, bbox_inches='tight')
print("\nGráfico guardado como 'grafico_3d_pareto.png'")

# Mostrar detalles de los puntos de Pareto
print("\n" + "="*60)
print("PUNTOS EN LA FRONTERA DE PARETO:")
print("="*60)
print(f"{'tf':<12} {'tw':<12} {'Flecha_Media':<15}")
print("-"*60)
for i in range(len(pareto_tf)):
    print(f"{pareto_tf[i]:<12.6f} {pareto_tw[i]:<12.6f} {pareto_flecha[i]:<15.6f}")

plt.show()
