#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================================
INSTITUTO NACIONAL DE CIÊNCIA E TECNOLOGIA EM TECNOLOGIAS FOTÔNICAS (IN-Foton)
LABORATÓRIO DE TELECOMUNICAÇÕES (LabTel) / DEPARTAMENTO DE ENGENHARIA ELÉTRICA (DEE)
UNIVERSIDADE FEDERAL DO ESPÍRITO SANTO (UFES)
================================================================================
Projeto Educacional: Portal IN-Foton / LabTel (in-foton.org)
Módulo: [MOD-MTL] Análise de Linhas de Transmissão com Multicondutores (Material Bônus)
Coordenação e Autoria: Prof. Marcelo Eduardo Vieira Segatto

Descrição:
Simulador e renderizador da animação tridimensional volumétrica da propagação
temporal de potência de RF ao longo do eixo z em 2.4 GHz:
1. Decompõe a excitação terminal da linha central exclusivamente nos Modos 1 e 3;
2. Simula o batimento direcional espacial Delta_beta = |beta_3 - beta_1| = 10.53 rad/m;
3. Gera a animação 3D da densidade de potência (|v_i(z,t)|^2) trafegando sobre o topo
   das fitas de cobre com controle estrito de camadas (zorder) sem oclusões;
4. Demonstra a difração lateral em z ~ 30 cm e a reconvergência total em z ~ 60 cm;
5. Exporta o arquivo animado em formato GIF e instantâneo estático em PNG.

Código 100% autônomo e autocontido (não requer dependências proprietárias).
Licença: Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional (CC BY-SA 4.0)
         e Licença MIT para execução e reutilização do código computacional.
Requisitos: Python 3.8+, numpy, matplotlib, pillow
Uso: python gerar_animacao_3d_potencia.py
================================================================================
"""

import os
import sys
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

# Diretório de saída local
DIR_OUTPUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outputs")
os.makedirs(DIR_OUTPUT, exist_ok=True)

# -------------------------------------------------------------------------
# MOTOR GEOMÉTRICO ISOMÉTRICO 3D (PADRÃO IN-Foton / LabTel)
# -------------------------------------------------------------------------
COS30 = np.cos(np.radians(30.0))
SIN30 = np.sin(np.radians(30.0))

def iso(x, y, z):
    """Converte coordenadas 3D (x, y, z) em projeção isométrica 2D plana."""
    px = (x - y) * COS30
    py = (x + y) * SIN30 + z
    return px, py

COLOR_SUB_TOP   = "#D5E8D4"
COLOR_SUB_FRONT = "#82B366"
COLOR_SUB_LEFT  = "#5A8246"
COLOR_SUB_EDGE  = "#274E13"

COLOR_CU_TOP    = "#F59E0B"
COLOR_CU_FRONT  = "#D97706"
COLOR_CU_LEFT   = "#B45309"
COLOR_CU_EDGE   = "#78350F"

def draw_substrate_and_gnd(ax, L, W, h, t_gnd=0.15):
    pts_gnd_f = [iso(0, 0, -t_gnd), iso(L, 0, -t_gnd), iso(L, 0, 0), iso(0, 0, 0)]
    ax.add_patch(patches.Polygon(pts_gnd_f, closed=True, facecolor=COLOR_CU_FRONT, edgecolor=COLOR_CU_EDGE, lw=0.8, zorder=1))
    pts_gnd_l = [iso(0, 0, -t_gnd), iso(0, W, -t_gnd), iso(0, W, 0), iso(0, 0, 0)]
    ax.add_patch(patches.Polygon(pts_gnd_l, closed=True, facecolor=COLOR_CU_LEFT, edgecolor=COLOR_CU_EDGE, lw=0.8, zorder=1))

    pts_sub_f = [iso(0, 0, 0), iso(L, 0, 0), iso(L, 0, h), iso(0, 0, h)]
    ax.add_patch(patches.Polygon(pts_sub_f, closed=True, facecolor=COLOR_SUB_FRONT, edgecolor=COLOR_SUB_EDGE, lw=1.1, zorder=2))
    pts_sub_l = [iso(0, 0, 0), iso(0, W, 0), iso(0, W, h), iso(0, 0, h)]
    ax.add_patch(patches.Polygon(pts_sub_l, closed=True, facecolor=COLOR_SUB_LEFT, edgecolor=COLOR_SUB_EDGE, lw=1.1, zorder=2))
    pts_sub_t = [iso(0, 0, h), iso(L, 0, h), iso(L, W, h), iso(0, W, h)]
    ax.add_patch(patches.Polygon(pts_sub_t, closed=True, facecolor=COLOR_SUB_TOP, edgecolor=COLOR_SUB_EDGE, lw=1.1, zorder=3))

def draw_planar_trace(ax, poly_xy, h=1.2, zorder_base=6):
    top_2d = [iso(x, y, h) for x, y in poly_xy]
    ax.add_patch(patches.Polygon(top_2d, closed=True, facecolor=COLOR_CU_TOP, edgecolor=COLOR_CU_EDGE, lw=1.0, zorder=zorder_base))

def main():
    print("="*80)
    print("IN-Foton / LabTel / UFES — ANIMAÇÃO 3D DA DENSIDADE DE POTÊNCIA (2.4 GHz)")
    print("Coordenação: Prof. Marcelo Eduardo Vieira Segatto")
    print("="*80)

    L_sub = 18.0   # comprimento longitudinal equivalente a 60 cm
    W_sub = 10.0   # largura transversal
    h_sub = 1.2    # altura do substrato
    t_gnd = 0.15

    w_strip = 1.6
    s_sep   = 1.1

    y_c2 = W_sub / 2.0                 # Linha 2 (Centro) = 5.0
    y_c1 = y_c2 - (w_strip + s_sep)    # Linha 1 = 2.3
    y_c3 = y_c2 + (w_strip + s_sep)    # Linha 3 = 7.7

    Nx = 250
    x_vals = np.linspace(0.0, L_sub, Nx)
    z_cm = x_vals / L_sub * 60.0

    L_beat = 59.7  # cm (~596.6 mm)
    delta_beta = 2.0 * np.pi / (L_beat * 1e-2)
    z_m = z_cm * 1e-2

    p2_frac = np.cos(delta_beta * z_m / 2.0)**2
    p1_frac = 0.5 * np.sin(delta_beta * z_m / 2.0)**2
    p3_frac = p1_frac

    N_frames = 50
    z_centers = np.linspace(-4.0, 64.0, N_frames)
    sigma_cm = 7.0
    max_h_pow = 3.6

    print(f"Renderizando {N_frames} frames em alta resolução...")
    frames = []

    for f_idx, z_cen in enumerate(z_centers):
        fig, ax = plt.subplots(figsize=(11.5, 6.6), dpi=120)
        ax.set_aspect("equal")
        ax.axis("off")
        fig.patch.set_facecolor('#0F172A')

        envelope = np.exp(-((z_cm - z_cen) / (2.0 * sigma_cm))**2)
        pow_1 = envelope * p1_frac
        pow_2 = envelope * p2_frac
        pow_3 = envelope * p3_frac

        # 1. Substrato e Terra
        draw_substrate_and_gnd(ax, L=L_sub, W=W_sub, h=h_sub, t_gnd=t_gnd)

        # 2. Pistas de Cobre
        for y_c in [y_c1, y_c2, y_c3]:
            y_bot = y_c - w_strip / 2.0
            y_top = y_c + w_strip / 2.0
            poly = [(0.0, y_bot), (L_sub, y_bot), (L_sub, y_top), (0.0, y_top)]
            draw_planar_trace(ax, poly, h=h_sub, zorder_base=5)

        # 3. Cortinas de Potência (Linha 3 -> Linha 2 -> Linha 1)
        lines_cfg = [
            (y_c3, pow_3, "#059669", "#047857", "#6EE7B7", 15),
            (y_c2, pow_2, "#DC2626", "#B91C1C", "#FCA5A5", 20),
            (y_c1, pow_1, "#0284C7", "#0369A1", "#7DD3FC", 25)
        ]

        for y_c, pow_data, color_main, color_edge, color_top, zbase in lines_cfg:
            y_bot = y_c - w_strip / 2.0
            y_top = y_c + w_strip / 2.0

            poly_wall = []
            for x in x_vals:
                poly_wall.append(iso(x, y_bot, h_sub))
            for x, p in zip(reversed(x_vals), reversed(pow_data)):
                poly_wall.append(iso(x, y_bot, h_sub + max_h_pow * p))
            ax.add_patch(patches.Polygon(poly_wall, closed=True, facecolor=color_main, edgecolor=color_edge, lw=0.7, alpha=0.92, zorder=zbase))

            poly_top = []
            for x, p in zip(x_vals, pow_data):
                poly_top.append(iso(x, y_bot, h_sub + max_h_pow * p))
            for x, p in zip(reversed(x_vals), reversed(pow_data)):
                poly_top.append(iso(x, y_top, h_sub + max_h_pow * p))
            ax.add_patch(patches.Polygon(poly_top, closed=True, facecolor=color_top, edgecolor=color_edge, lw=0.7, alpha=0.95, zorder=zbase+1))

            crest_pts = [iso(x, y_c, h_sub + max_h_pow * p) for x, p in zip(x_vals, pow_data)]
            ax.plot([pt[0] for pt in crest_pts], [pt[1] for pt in crest_pts], color="#FFFFFF", lw=1.3, alpha=0.9, zorder=zbase+2)

        if 0 <= z_cen <= 60:
            x_marker = z_cen / 60.0 * L_sub
            m_start = iso(x_marker, 0.0, h_sub + 0.05)
            m_end   = iso(x_marker, W_sub, h_sub + 0.05)
            ax.plot([m_start[0], m_end[0]], [m_start[1], m_end[1]], color="#F59E0B", linestyle="--", lw=1.2, zorder=30)
            ax.text(m_start[0] + 0.4, m_start[1] - 0.35, f"$z = {z_cen:.1f}\\text{{ cm}}$", color="#FDE68A", fontsize=8, fontweight="bold", zorder=30)

        # Rótulos
        p3_lbl = iso(0.0, y_c3, h_sub)
        ax.text(p3_lbl[0] - 0.7, p3_lbl[1] + 0.35, "Linha 3 (Lateral)", color="#34D399", fontsize=8, fontweight="bold", ha="right",
                bbox=dict(boxstyle="round,pad=0.2", facecolor="#064E3B", edgecolor="#10B981", alpha=0.85), zorder=35)
        p2_lbl = iso(0.0, y_c2, h_sub)
        ax.text(p2_lbl[0] - 0.7, p2_lbl[1] + 0.25, "Linha 2 (Centro / Excitada)", color="#FCA5A5", fontsize=8, fontweight="bold", ha="right",
                bbox=dict(boxstyle="round,pad=0.2", facecolor="#7F1D1D", edgecolor="#EF4444", alpha=0.85), zorder=35)
        p1_lbl = iso(0.0, y_c1, h_sub)
        ax.text(p1_lbl[0] - 0.7, p1_lbl[1] + 0.15, "Linha 1 (Lateral)", color="#7DD3FC", fontsize=8, fontweight="bold", ha="right",
                bbox=dict(boxstyle="round,pad=0.2", facecolor="#0C4A6E", edgecolor="#0284C7", alpha=0.85), zorder=35)

        p_top_corner = iso(L_sub, W_sub, h_sub)
        ax.text(0.0, p_top_corner[1] + 2.4,
                "Visualização 3D da Densidade de Potência de RF Trafegando ao Longo de $z$ (2.4 GHz)",
                fontsize=12, fontweight="bold", color="#38BDF8", ha="center")

        if z_cen < 12:
            estado = "Fase Inicial ($z \\approx 0$): 100% da energia concentrada na Linha Central"
        elif z_cen < 24:
            estado = "Transição ($0 < z < L_{\\mathrm{beat}}/2$): Difração e acoplamento progressivo para as laterais"
        elif z_cen < 36:
            estado = "Máxima Transferência ($z \\approx L_{\\mathrm{beat}}/2 \\approx 30\\text{ cm}$): Mínimo na central e pico nas laterais"
        elif z_cen < 48:
            estado = "Reconvergência ($L_{\\mathrm{beat}}/2 < z < L_{\\mathrm{beat}}$): Retorno da energia para a linha central"
        else:
            estado = "Ciclo Completo ($z \\approx L_{\\mathrm{beat}} \\approx 60\\text{ cm}$): 100% da energia reconverge ao centro"

        ax.text(0.0, p_top_corner[1] + 1.4, estado, fontsize=9.5, color="#F1F5F9", ha="center")
        ax.set_xlim(-12.0, 10.0)
        ax.set_ylim(-3.0, p_top_corner[1] + 3.2)

        plt.tight_layout()
        fig.canvas.draw()
        image_rgba = np.asarray(fig.canvas.buffer_rgba())
        frames.append(Image.fromarray(image_rgba))
        plt.close(fig)

        if (f_idx + 1) % 15 == 0:
            print(f"  -> Frame {f_idx + 1}/{N_frames} concluído...")

    out_gif = os.path.join(DIR_OUTPUT, "mtl_3d_power_beat_propagation.gif")
    out_png = os.path.join(DIR_OUTPUT, "mtl_3d_power_snapshot_mid.png")

    frames[0].save(out_gif, save_all=True, append_images=frames[1:], duration=75, loop=0)
    frames[len(frames)//2].save(out_png)

    print(f"✔ Animação 3D e snapshots gerados com sucesso:")
    print(f"  -> GIF: {out_gif}")
    print(f"  -> PNG: {out_png}")
    print("="*80)

if __name__ == "__main__":
    main()
