import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Polygon, Circle, Arc
from matplotlib.path import Path
from matplotlib.patches import PathPatch
B="#1f4e8c";BL="#6fa8dc";V="#f2f2f2";G="#555555";A="#c47a12";T="#222222";GR="#2e7d32";GP="#16365f"
fig=plt.figure(figsize=(16,13.4),dpi=110)
fig.patch.set_facecolor("white")
ax=fig.add_axes([0.01,0.417,0.53,0.4925]);ax2=fig.add_axes([0.55,0.417,0.44,0.4925])
for a in (ax,ax2):
    a.set_aspect("equal");a.axis("off")
ax.set_xlim(-2.55,2.55);ax.set_ylim(-1.15,3.62)
ax2.set_xlim(-3.0,2.6);ax2.set_ylim(-1.15,3.75)
fig.text(0.5,0.963,"Panneau hybride Muhleisen, version simple : schéma de principe",ha="center",fontsize=19,color=T,weight="bold")
fig.text(0.27,0.922,"Vue de face, volets dépliés (mode vent ; en mode solaire, voir plus bas)",ha="center",fontsize=13,color=G)
fig.text(0.77,0.922,"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.115,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)
hy=1.7;L=1.2;R=1.6;VL=0.61;LV=1.55
ax.add_patch(Rectangle((-2.2,-0.75),4.4,0.2,color="#d9d9d9"))
ax.add_patch(Rectangle((-0.3,-0.55),0.6,0.1,color="#999999"))
ax.add_patch(Rectangle((-1.65,-0.45),3.3,0.3,facecolor="#e6e6e6",edgecolor=G,lw=1))
ax.add_patch(Rectangle((-1.65,-0.15),3.3,0.15,facecolor="#bfbfbf",edgecolor=G))
ax.add_patch(Rectangle((-1.6,-0.06),3.2,0.06,facecolor=GP,edgecolor="#0b2a52",zorder=3))
for s in (-1,1):
    ax.plot([s*1.62,s*1.82],[hy-0.05,0.0],color=G,lw=4.5,solid_capstyle="round")
ax.plot([-2.06,-1.55],[hy,hy],color=T,lw=4,zorder=5)
ax.plot([1.55,2.06],[hy,hy],color=T,lw=4,zorder=5)
y0p=hy-0.8;y1p=hy+0.8
ax.add_patch(Rectangle((-1.55,y0p),3.1,1.6,facecolor=B,edgecolor="#555555",lw=3.5,zorder=3))
for a_,b_ in (((-1.55,y0p),(1.55,y1p)),((-1.55,y1p),(1.55,y0p))):
    ax.plot([a_[0],b_[0]],[a_[1],b_[1]],color="#cfd8e3",lw=1.1,ls=(0,(6,3)),zorder=5)
for i in range(1,17):
    ax.plot([-1.55+0.1824*i]*2,[y0p,y1p],color="#3d6fb0",lw=0.6,zorder=4)
for j in range(1,8):
    ax.plot([-1.55,1.55],[y0p+0.2*j]*2,color="#3d6fb0",lw=0.6,zorder=4)
for sg in (1,-1):
    ya,yb=(y1p,y1p+0.8) if sg==1 else (y0p-0.8,y0p)
    ax.add_patch(Rectangle((-1.55,ya),3.1,0.8,facecolor="#cfe8f3",alpha=0.75,edgecolor=G,lw=1.2,zorder=3))
    hz=y1p if sg==1 else y0p
    ax.plot([-1.55,1.55],[hz,hz],color=A,lw=2.4,ls=(0,(6,3)),zorder=5)
for s in (-1,1):
    ax.add_patch(Circle((s*1.62,hy),0.07,facecolor="#dddddd",edgecolor=T,zorder=7))
    ax.add_patch(Circle((s*1.88,hy),0.17,fill=False,edgecolor=T,lw=1.3,ls=(0,(3,2)),zorder=6))
ax.add_patch(Rectangle((2.06,hy-0.08),0.16,0.16,facecolor=A,edgecolor=T,zorder=8))
ax.add_patch(Circle((0.85,2.05),0.04,color="#e53935",zorder=8))
lead(ax,-0.3,1.45,-0.3,1.45);num(ax,-0.3,1.45,1)
lead(ax,0.6,3.0,0.6,3.42);num(ax,0.6,3.42,2)
lead(ax,0.1,y1p,0.1,3.42);num(ax,0.1,3.42,3)
lead(ax,-0.9,hy+0.33,-2.35,2.4);num(ax,-2.35,2.4,4)
lead(ax,-1.95,hy-0.12,-2.35,1.4);num(ax,-2.35,1.4,5)
lead(ax,2.14,hy,2.4,2.2);num(ax,2.4,2.2,6)
lead(ax,0.85,2.05,1.55,3.0);num(ax,1.55,3.0,7)
lead(ax,1.75,0.6,2.35,1.0);num(ax,2.35,1.0,8)
lead(ax,0.4,-0.03,0.6,0.45);num(ax,0.6,0.45,9)
lead(ax,-1.6,-0.08,-2.35,0.25);num(ax,-2.35,0.25,10)
lead(ax,0.9,-0.3,2.2,-0.3);num(ax,2.32,-0.3,11)
lead(ax,-1.62,hy+0.06,-2.35,1.9);num(ax,-2.35,1.9,12)
ax.plot([2.06,2.06],[hy+0.08,hy+0.75],color=T,lw=1.5,zorder=6)
ax.add_patch(Polygon([[1.92,hy+0.75],[2.06,hy+0.82],[2.3,hy+0.8],[2.3,hy+0.7],[2.06,hy+0.68]],facecolor="#cccccc",edgecolor=T,zorder=7))
lead(ax,2.3,hy+0.75,2.4,2.75);num(ax,2.4,2.75,16)
for s_ in (-1,1):
    ax.add_patch(Rectangle((min(s_*1.72,s_*2.04),hy-0.22),0.32,0.44,facecolor="#e8e8e8",edgecolor="#555555",lw=1.3,ls=(0,(5,3)),alpha=0.5,zorder=5))
lead(ax,-2.0,hy-0.2,-2.35,1.1);num(ax,-2.35,1.1,15)
ax.add_patch(Rectangle((-0.2,-0.42),0.4,0.22,facecolor="#7a7a7a",edgecolor=T,zorder=4))
ax.annotate("",xy=(1.77,-0.92),xytext=(-1.77,-0.92),arrowprops=dict(arrowstyle="<->",color=G))
ax.text(0,-1.07,"Panneau et volets 3,1 m le long de l'axe (nord-sud en mode solaire), plateau 3,3 m",ha="center",fontsize=10,color=G)
ax2.add_patch(Rectangle((-2.1,-0.75),4.6,0.2,color="#d9d9d9"))
ax2.add_patch(Rectangle((-0.3,-0.55),0.6,0.1,color="#999999"))
ax2.add_patch(Rectangle((-0.65,-0.45),1.3,0.3,facecolor="#e6e6e6",edgecolor=G))
ax2.add_patch(Rectangle((-0.65,-0.15),1.3,0.15,facecolor="#bfbfbf",edgecolor=G))
ax2.add_patch(Rectangle((-0.625,-0.06),1.25,0.06,facecolor=GP,zorder=3))
c=np.array([0,hy])
for s in (-1,1):
    ax2.plot([0,s*0.6],[hy-0.05,0.0],color="#9a9a9a",lw=4,solid_capstyle="round",zorder=1)
ax2.add_patch(Circle(c,R,fill=False,ls=(0,(4,4)),color="#999999",lw=0.8))
th=np.radians(35);u=np.array([np.cos(th),np.sin(th)]);n=np.array([-np.sin(th),np.cos(th)])
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)
seg(c-0.8*u,c+0.8*u,B,7)
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="#7fb6cc",lw=5,solid_capstyle="butt",zorder=5)
p=c+0.8*u;cseg(p,u,0.8,0.09)
q=c-0.8*u;cseg(q,-n,0.8,0.09)
kp=c+0.3*n
ax2.plot([c[0],kp[0]],[c[1],kp[1]],color="#333333",lw=1.5,zorder=6)
for e in (c+1.6*u,c-0.8*u):
    ax2.plot([kp[0],e[0]],[kp[1],e[1]],color="#333333",lw=0.8,zorder=6)
lead(ax2,kp[0],kp[1],-0.7,2.75);num(ax2,-0.7,2.75,14)
ax2.add_patch(Circle(c,0.08,facecolor="#7a7a7a",edgecolor=T,zorder=7))
mc=np.array([0.42,hy])
ax2.plot([0,mc[0]],[hy+0.3,hy+0.12],color="#333333",lw=1.3,zorder=7)
ax2.plot([0,mc[0]],[hy-0.3,hy-0.12],color="#333333",lw=1.3,zorder=7)
ax2.add_patch(Circle(c,0.3,fill=False,edgecolor="#333333",lw=1.2,zorder=7))
ax2.add_patch(Circle(mc,0.12,facecolor="#7a7a7a",edgecolor=T,lw=1.4,zorder=8))
ax2.add_patch(Circle(mc,0.05,facecolor="#dddddd",zorder=9))
ax2.plot([0.42,0.3],[hy-0.12,0.95],color="#9a9a9a",lw=2,zorder=1)
lead(ax2,0.5,hy+0.08,1.05,2.05);num(ax2,1.05,2.05,5)
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="#555555",lw=1.4,ls=(0,(5,3)),alpha=0.45,zorder=6))
lead(ax2,0.8,hy+0.05,1.6,1.85);num(ax2,1.6,1.85,15)
ax2.add_patch(Rectangle((-0.2,-0.42),0.4,0.22,facecolor="#7a7a7a",edgecolor=T,zorder=4))
lead(ax2,0.2,-0.31,1.2,-0.31);num(ax2,1.32,-0.31,13)
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 yy in (1.95,2.35,2.75):
    ax2.annotate("",xy=(-1.55,yy),xytext=(-2.2,yy),arrowprops=dict(arrowstyle="-|>",color="#4a90c2",lw=2.2))
ax2.text(-2.2,3.0,"Vent",fontsize=12,color="#4a90c2",weight="bold")
prism(ax2,0,1.7,1.0,True)
lead(ax2,-1.36,0.45,-1.0,0.42);num(ax2,-0.98,0.42,17)
ax2.text(-0.2,3.62,"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")
pt=p+0.3*u
ax2.plot([pt[0],1.45],[pt[1],2.75],color="#666666",lw=0.9)
ax2.text(1.5,2.75,"Volet cambré ouvert :\npoussé par le vent",fontsize=10.5,color=T,va="center")
qt=q-0.3*n
ax2.plot([qt[0],0.85],[qt[1],0.45],color="#666666",lw=0.9)
ax2.text(0.9,0.42,"Volet replié de chant :\nrevient contre le vent",fontsize=10.5,color=T,va="center")
ax2.text(-0.2,-1.07,"Cercle balayé 3,2 m : 0,8 m de panneau + 0,8 m de volet de chaque côté, arceaux droits en arrière-plan",ha="center",fontsize=10,color=G)
ax3=fig.add_axes([0.02,0.245,0.36,0.165]);ax3.set_aspect("equal");ax3.axis("off")
ax3.set_xlim(-3.0,3.4);ax3.set_ylim(-0.35,3.0)
ax3.add_patch(Rectangle((-2.4,-0.32),5.6,0.12,color="#d9d9d9"))
ax3.add_patch(Rectangle((-0.65,-0.2),1.3,0.2,facecolor="#bfbfbf",edgecolor=G))
ax3.add_patch(Rectangle((-0.625,-0.06),1.25,0.06,facecolor=GP,zorder=3))
e=np.radians(40);d=np.array([np.cos(e),np.sin(e)]);nn_=d;uu=np.array([-np.sin(e),np.cos(e)]);c3=np.array([0,1.7])
for s_ in (-1,1):
    ax3.plot([s_*0.35,0],[0,1.7],color="#b0b0b0",lw=2.5,zorder=1)
ax3.plot(*zip(c3-uu*0.8,c3+uu*0.8),color=BL,lw=6,solid_capstyle="butt",zorder=5)
ax3.plot(*zip(c3-uu*0.8,c3+uu*0.8),color=B,lw=7,solid_capstyle="butt",zorder=6)
for a_ in (c3-uu*0.8,c3+uu*0.8):
    ax3.plot(*zip(a_,a_-nn_*0.8),color="#7fb6cc",lw=5,solid_capstyle="butt",zorder=5)
ax3.add_patch(Circle(c3,0.07,facecolor="#7a7a7a",edgecolor=T,zorder=7))
prism(ax3,0,1.7)
ax3.add_patch(Circle((3.05,2.6),0.22,color="#f5b301",zorder=3))
for x0 in (-0.2,0.15,0.5):
    p0=np.array([x0,0.02]);p1=p0+d*2.6
    ax3.annotate("",xy=p0,xytext=p1,arrowprops=dict(arrowstyle="-|>",color="#e0a000",lw=1.6),zorder=8)
for o in (-0.5,0.0,0.5):
    p0=c3+uu*o+nn_*0.05;p1=p0+d*1.3
    ax3.annotate("",xy=p0,xytext=p1,arrowprops=dict(arrowstyle="-|>",color="#e0a000",lw=1.6),zorder=8)
fig.text(0.39,0.385,"Vue de profil, mode solaire sans vent : volets repliés",fontsize=12.5,color=T,weight="bold")
for j_,l_ in enumerate(['Vent faible : le rotor est verrouillé face au soleil et les vérins replient', 'les volets transparents à 90° derrière le panneau : parallèles aux rayons,', "ils ne font pas d'ombre et laissent passer la lumière diffuse. Le soleil", "atteint le panneau au sol (3,2 × 1,25 m) dès qu'il descend sous ~40°.", "Panneau au sol : ~3,1 fois plus de lumière qu'avec des volets blancs à plat."]):
    fig.text(0.39,0.355-j_*0.024,l_,fontsize=11,color=T)
items=['1  Panneau verre-verre biface 3,1 × 1,6 m (~5 m²), fixations caoutchouc', '2  Volet transparent polycarbonate cambré 0,8 m, butées amorties', '3  Charnière à ressort et petit vérin électrique', '4  Cadre rigide, demi-axes aux bouts, câbles inox en croix au dos', '5  Moteur de trottinette, poulie et courroie, ×2', '6  Bague collectrice à balais en fibres + électronique embarquée', '7  Sonde de température (volets écartés si surchauffe)', '8  Arceaux droits en tube galvanisé', '9  Panneau au sol monoface 3,2 × 1,25 m, encastré', '10  Plateau pivotant réduit (3,3 × 1,3 m) sur couronne', '11  Caisson : contrôleur, supercondensateurs, frein de survitesse', '12  Verrou automatique à goupille sur le roulement', "13  Moteur d'orientation : vent, retour nord-sud, suivi 2 axes", '14  Haubans inox sur poinçon, aux extrémités du rotor', "15  Carter de courroie en goutte d'eau, pointe sous le vent", '16  Girouette électronique', '17  Déflecteur en prisme, nez arrondi, face arrière blanche']
for k,t in enumerate(items):
    col=k//6;row=k%6
    fig.text(0.03+col*0.33,(0.235-row*0.03)*0.8209,t,fontsize=10.5,color=T)
fig.text(0.5,0.0205,"Schéma de principe, proportions approximatives. Estimations en série : ~4 829 kWh/an sur toit côtier (vent 6,5 m/s, ~0,088 €/kWh sur 25 ans), ~3 368 kWh/an sur mât de 6 m (5,2 m/s). www.mulo.world",ha="center",fontsize=10.5,color=G)
plt.savefig("schema_panneau_solaire_hybride_muhleisen_simple.png",dpi=110,facecolor="white")
