import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Polygon, Circle, Arc
B="#1f4e8c";BL="#6fa8dc";V="#f2f2f2";G="#555555";A="#c47a12";T="#222222";GP="#16365f"
MODE="double"
if MODE=="double":
    H=[1.7,5.1];OUT="schema_panneau_solaire_hybride_muhleisen_double.png"
    TIT="Panneau hybride Muhleisen, version double (2 étages pour toits) : schéma de principe"
    FOOT="Schéma de principe, proportions approximatives. Estimations en série : ~9 997 kWh/an sur toit côtier (~0,075 €/kWh sur 25 ans), ~5 227 kWh/an sur toit urbain (~0,144 €/kWh). www.mulo.world"
    BASE="14  Lest ou ancrage chimique sur la dalle, plots antivibratiles"
else:
    H=[1.7,5.1,8.5];OUT="schema_panneau_solaire_hybride_muhleisen_tour.png"
    TIT="Panneau hybride Muhleisen, tour au sol de 3 modules : schéma de principe"
    FOOT="Schéma de principe, proportions approximatives. Estimation en série : ~8 803 kWh/an au sol (vent moyen 5,5 m/s à 10 m), ~0,130 €/kWh sur 25 ans, ~9,3 fois un panneau monoface classique. www.mulo.world"
    BASE="14  Fondation béton (massif) ou vis de fondation"
top=H[-1]+1.6
fig=plt.figure(figsize=(16,12.5),dpi=110)
fig.patch.set_facecolor("white")
ax=fig.add_axes([0.01,0.25,0.48,0.65]);ax2=fig.add_axes([0.50,0.25,0.49,0.65])
for a in (ax,ax2):
    a.set_aspect("equal");a.axis("off")
ax.set_xlim(-3.6,3.6);ax.set_ylim(-1.9,top+1.4)
ax2.set_xlim(-3.4,3.8);ax2.set_ylim(-1.9,top+1.4)
fig.text(0.5,0.955,TIT,ha="center",fontsize=19,color=T,weight="bold")
fig.text(0.25,0.915,"Vue de face, volets dépliés (rotors verrouillés)",ha="center",fontsize=13,color=G)
fig.text(0.745,0.915,"Vue de profil (dans l'axe), mode vent",ha="center",fontsize=13,color=G)
def prism(a,cx,cy,s=1.0,arrow=False):
    th=np.linspace(np.radians(250),np.radians(130),14)
    nose=[(-2.45+0.22*np.cos(t_),-1.28+0.22*np.sin(t_)) for t_ in th]
    ang=np.linspace(np.radians(183.3),np.radians(244.3),14);rr=np.linspace(1.753,1.84,14)
    arc=[(r_*np.cos(t_),r_*np.sin(t_)) for r_,t_ in zip(rr,ang)]
    pts=np.array([(-0.8,-1.66),(-2.3,-1.48)]+nose+arc)*s+np.array([cx,cy])
    a.add_patch(Polygon(pts,closed=True,facecolor="#4a4a4a",edgecolor="#2a2a2a",lw=1.2,zorder=4))
    ar=np.array(arc)*s+np.array([cx,cy])
    a.plot(ar[:,0],ar[:,1],color="#888888",lw=4.5,solid_capstyle="butt",zorder=5)
    a.plot(ar[:,0],ar[:,1],color="white",lw=3,solid_capstyle="butt",zorder=6)
    if arrow:
        p0=np.array([-2.95,-1.15])*s+np.array([cx,cy]);p1=np.array([-1.95,0.25])*s+np.array([cx,cy])
        a.annotate("",xy=p1,xytext=p0,arrowprops=dict(arrowstyle="-|>",color="#4a90c2",lw=2,connectionstyle="arc3,rad=-0.15"),zorder=8)
def num(a,x,y,k):
    a.add_patch(Circle((x,y),0.15*(top+3)/9.6,facecolor="white",edgecolor=T,lw=1.3,zorder=20))
    a.text(x,y,str(k),ha="center",va="center",fontsize=10,weight="bold",color=T,zorder=21)
def lead(a,x0,y0,x1,y1):
    a.plot([x0,x1],[y0,y1],color="#666666",lw=0.9,zorder=19)
ax.add_patch(Rectangle((-3.0,-0.95),6.0,0.25,color="#d9d9d9"))
ax.add_patch(Rectangle((-0.35,-0.7),0.7,0.12,color="#999999"))
ax.add_patch(Rectangle((-2.25,-0.58),4.5,0.4,facecolor="#e6e6e6",edgecolor=G,lw=1))
ax.add_patch(Rectangle((-2.25,-0.18),4.5,0.18,facecolor="#bfbfbf",edgecolor=G))
ax.add_patch(Rectangle((-1.6,-0.07),3.2,0.07,facecolor=GP,edgecolor="#0b2a52",zorder=3))
ax.add_patch(Rectangle((-0.25,-0.52),0.5,0.28,facecolor="#7a7a7a",edgecolor=T,zorder=4))
for s in (-1,1):
    xi=s*1.98;xo=s*2.16
    ax.plot([xi,xi],[0,top+0.15],color=G,lw=3.5,zorder=2)
    ax.plot([xo,xo],[0,top+0.15],color=G,lw=3.5,zorder=2)
    ys=np.arange(0.0,top+0.1,0.42)
    for k in range(len(ys)-1):
        ax.plot([xi,xo],[ys[k],ys[k+1]] if k%2==0 else [ys[k+1],ys[k]],color="#8a8a8a",lw=1,zorder=2)
    ax.plot([xi,xo],[top+0.15,top+0.15],color=G,lw=3.5,zorder=2)
for i,hy in enumerate(H):
    ax.add_patch(Polygon([(-1.6,hy+1.6),(1.6,hy+1.6),(1.756,hy+0.99),(-1.756,hy+0.99)],facecolor="#cfe8f3",alpha=0.75,edgecolor=G,lw=1.2,zorder=3))
    ax.add_patch(Polygon([(-1.6,hy-1.6),(1.6,hy-1.6),(1.756,hy-0.99),(-1.756,hy-0.99)],facecolor="#cfe8f3",alpha=0.75,edgecolor=G,lw=1.2,zorder=3))
    ax.add_patch(Rectangle((-1.756,hy-0.99),3.512,1.98,facecolor=BL,edgecolor="#2d5f99",lw=1.2,zorder=3))
    for xx in np.arange(-1.6,1.7,0.24):
        ax.plot([xx,xx],[hy-0.99,hy+0.99],color="#4f86c6",lw=0.6,zorder=3)
    ax.add_patch(Rectangle((-1.45,hy-0.565),2.9,1.13,facecolor=B,edgecolor="#555555",lw=3,zorder=4))
    for k in range(1,16):
        ax.plot([-1.45+0.1812*k]*2,[hy-0.565,hy+0.565],color="#3d6fb0",lw=0.6,zorder=5)
    for j in range(1,6):
        ax.plot([-1.45,1.45],[hy-0.565+0.1883*j]*2,color="#3d6fb0",lw=0.6,zorder=5)
    for sg in (1,-1):
        ax.plot([-1.756,1.756],[hy+sg*0.99]*2,color=A,lw=2.2,ls=(0,(6,3)),zorder=6)
    ax.plot([-2.07,-1.45],[hy,hy],color=T,lw=3.5,zorder=6)
    ax.plot([1.45,2.07],[hy,hy],color=T,lw=3.5,zorder=6)
    for s in (-1,1):
        ax.add_patch(Circle((s*2.07,hy),0.08,facecolor="#dddddd",edgecolor=T,zorder=8))
        ax.add_patch(Circle((s*2.45,hy),0.17,fill=False,edgecolor=T,lw=1.3,ls=(0,(3,2)),zorder=7))
        ax.add_patch(Rectangle((min(s*2.25,s*2.68),hy-0.24),0.43,0.48,facecolor="#e8e8e8",edgecolor=G,lw=1.3,ls=(0,(5,3)),alpha=0.5,zorder=6))
    ax.add_patch(Rectangle((2.7,hy-0.08),0.16,0.16,facecolor=A,edgecolor=T,zorder=8))
ax.plot([0,0],[top+0.15,top+0.75],color=T,lw=1.5,zorder=6)
ax.add_patch(Polygon([[-0.15,top+0.75],[0,top+0.83],[0.3,top+0.81],[0.3,top+0.69],[0,top+0.67]],facecolor="#cccccc",edgecolor=T,zorder=7))
ax.annotate("",xy=(3.25,top),xytext=(3.25,0),arrowprops=dict(arrowstyle="<->",color=G))
ax.text(3.35,top/2,f"~{top:.1f} m".replace(".",","),rotation=90,va="center",fontsize=10,color=G)
lo=H[0];up=H[-1]
lead(ax,-0.6,up+0.2,-3.1,up+0.9);num(ax,-3.1,up+0.9,1)
lead(ax,-0.6,lo+0.2,-3.1,lo+0.9);num(ax,-3.1,lo+0.9,2)
lead(ax,-1.2,up-0.8,-3.1,up-0.5);num(ax,-3.1,up-0.5,3)
lead(ax,0.5,up+1.3,0.9,top+0.9);num(ax,0.9,top+0.9,4)
lead(ax,-2.07,lo+2.4,-3.1,lo+2.6);num(ax,-3.1,lo+2.6,5)
lead(ax,2.45,up+0.17,3.0,up+0.9);num(ax,3.0,up+0.9,6)
lead(ax,2.86,lo,3.0,lo+0.7);num(ax,3.0,lo+0.7,7)
lead(ax,1.2,-0.04,3.0,-0.3);num(ax,3.15,-0.3,9)
lead(ax,-1.6,-0.4,-3.1,-0.4);num(ax,-3.1,-0.4,10)
lead(ax,0.3,top+0.75,1.4,top+1.15);num(ax,1.55,top+1.15,12)
lead(ax,-2.07,lo,-3.1,lo-0.4);num(ax,-3.1,lo-0.4,13)
lead(ax,-2.6,-0.82,-3.1,-1.1);num(ax,-3.1,-1.1,14)
ax.text(0,-1.8,"Rotors de 3,5 m le long de l'axe, cercle balayé 3,2 m, étages espacés de 3,4 m",ha="center",fontsize=10,color=G)
ax2.add_patch(Rectangle((-3.2,-0.95),6.8,0.25,color="#d9d9d9"))
ax2.add_patch(Rectangle((-0.35,-0.7),0.7,0.12,color="#999999"))
ax2.add_patch(Rectangle((-0.9,-0.58),1.8,0.4,facecolor="#e6e6e6",edgecolor=G))
ax2.add_patch(Rectangle((-0.9,-0.18),1.8,0.18,facecolor="#bfbfbf",edgecolor=G))
ax2.add_patch(Rectangle((-0.625,-0.07),1.25,0.07,facecolor=GP,zorder=3))
ax2.add_patch(Rectangle((-0.25,-0.52),0.5,0.28,facecolor="#7a7a7a",edgecolor=T,zorder=4))
for s in (-1,1):
    ax2.plot([s*0.8,s*0.16],[0,top+0.15],color="#9a9a9a",lw=3.5,zorder=1)
nb=8*len(H)
yy=np.linspace(0,top+0.15,nb+1)
def xl(y,s):return s*(0.8-0.64*y/(top+0.15))
for k in range(nb):
    s=1 if k%2==0 else -1
    ax2.plot([xl(yy[k],-s),xl(yy[k+1],s)],[yy[k],yy[k+1]],color="#bdbdbd",lw=1,zorder=1)
def seg(p0,p1,col,lw):
    ax2.plot([p0[0],p1[0]],[p0[1],p1[1]],color=col,lw=lw,solid_capstyle="butt",zorder=5)
def cseg(p0,d,Ln,bend):
    pe=np.array([-d[1],d[0]]);t=np.linspace(0,1,30)
    pts=np.array([(1-tt)**2*p0+2*(1-tt)*tt*(p0+d*Ln/2+pe*bend)+tt**2*(p0+d*Ln) for tt in t])
    ax2.plot(pts[:,0],pts[:,1],color=CV,lw=5,solid_capstyle="butt",zorder=5)
VL=0.61
for i,hy in enumerate(H):
    CV="#7fb6cc"
    c=np.array([0,hy])
    ax2.add_patch(Circle(c,1.6,fill=False,ls=(0,(4,4)),color="#999999",lw=0.8))
    th=np.radians([35,-20,70][i]);u=np.array([np.cos(th),np.sin(th)]);n=np.array([-np.sin(th),np.cos(th)])
    seg(c-0.565*u,c+0.565*u,B,7);seg(c+0.565*u,c+0.99*u,BL,6);seg(c-0.565*u,c-0.99*u,BL,6)
    cseg(c+0.99*u,u,VL,0.07);cseg(c-0.99*u,-n,VL,0.07)
    ax2.add_patch(Circle(c,0.08,facecolor="#7a7a7a",edgecolor=T,zorder=7))
    mc=np.array([0.42,hy])
    ax2.add_patch(Circle(c,0.3,fill=False,edgecolor="#333333",lw=1.2,zorder=7))
    ax2.plot([0,0.42],[hy+0.3,hy+0.12],color="#333333",lw=1.3,zorder=7)
    ax2.plot([0,0.42],[hy-0.3,hy-0.12],color="#333333",lw=1.3,zorder=7)
    ax2.add_patch(Circle(mc,0.12,facecolor="#7a7a7a",edgecolor=T,lw=1.4,zorder=8))
    tear=[(0,hy+0.4),(-0.3,hy+0.28),(-0.4,hy),(-0.3,hy-0.28),(0,hy-0.4),(0.45,hy-0.25),(0.95,hy),(0.45,hy+0.25)]
    ax2.add_patch(Polygon(tear,closed=True,facecolor="#e8e8e8",edgecolor=G,lw=1.4,ls=(0,(5,3)),alpha=0.45,zorder=6))
    prism(ax2,0,hy)
    ax2.add_patch(Arc(c,3.45,3.45,theta1=40,theta2=100,color=A,lw=2))
    e0=c+1.72*np.array([np.cos(np.radians(46)),np.sin(np.radians(46))]);e1=c+1.72*np.array([np.cos(np.radians(36)),np.sin(np.radians(36))])
    ax2.annotate("",xy=e1,xytext=e0,arrowprops=dict(arrowstyle="-|>",color=A,lw=2))
    for yv in (hy+0.4,hy+1.0):
        ax2.annotate("",xy=(-2.1,yv),xytext=(-2.9,yv),arrowprops=dict(arrowstyle="-|>",color="#4a90c2",lw=2.2))
lo=H[0];up=H[-1]
ax2.text(-3.3,top+0.45,"Vent (plus fort en hauteur)",fontsize=12,color="#4a90c2",weight="bold")
P=np.array([-1.75,up-0.6])
lead(ax2,-1.62,up-1.0,-2.6,up-1.55);num(ax2,-2.6,up-1.55,8)
lead(ax2,-1.62,lo-1.0,-2.6,lo-1.55);num(ax2,-2.6,lo-1.55,8)
lead(ax2,0.25,-0.38,1.6,-0.38);num(ax2,1.75,-0.38,11)
lead(ax2,0.55,up,2.2,up+0.3);num(ax2,2.35,up+0.3,6)
lead(ax2,0.5,lo+1.2,2.2,lo+1.9);num(ax2,2.35,lo+1.9,5)
ax2.text(0.3,top+1.05,"Rotation libre quand le vent rapporte plus que le soleil perdu, dès 3-4 m/s la nuit",fontsize=10.5,color=A,ha="center")
ax2.text(0.3,-1.8,"Un déflecteur fixe par étage côté vent, pylônes en treillis en arrière-plan",ha="center",fontsize=10,color=G)
items=["1  Module supérieur (axe à ~%s m), verre-verre biface 2,9 × 1,13 m"%("5,1" if len(H)==2 else "8,5"),"2  Module inférieur (axe à 1,7 m), identique","3  Modules sans verre bifaces (type Sunman) : ailettes et bords","4  Volets transparents à vérins, repliés en mode solaire","5  Pylônes latéraux en treillis galvanisé, demi-axes sur roulements","6  Moteurs de trottinette + courroie 1:6, carters en goutte d'eau","7  Bagues collectrices à balais en fibres + électronique embarquée","8  Déflecteurs en prisme à nez arrondi, face arrière blanche","9  Panneau au sol monoface 3,2 × 1,25 m encastré dans le plateau","10  Plateau pivotant sur couronne, contrôleur + supercondensateurs","11  Moteur d'orientation commun, suivi 2 axes","12  Girouette électronique au sommet","13  Verrous automatiques à goupille, un par étage",BASE]
if len(H)==3:items[1]="2  Modules du milieu (axe 5,1 m) et du bas (axe 1,7 m)"
for k,t in enumerate(items):
    col=k//5;row=k%5
    fig.text(0.02+col*0.335,0.205-row*0.03,t,fontsize=10.5,color=T)
fig.text(0.5,0.025,FOOT,ha="center",fontsize=10.5,color=G)
plt.savefig(OUT,dpi=110,facecolor="white")
