import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Polygon, Circle, Wedge, Arc
B="#1f4e8c";GP="#16365f";G="#555555";A="#c47a12";T="#222222";W="#4a90c2";GR="#e3dfd3";SL="#b9a97e";BF="#2e7d5b"
fig=plt.figure(figsize=(17,11),dpi=110)
fig.patch.set_facecolor("white")
ax=fig.add_axes([0.01,0.30,0.56,0.58]);ax2=fig.add_axes([0.58,0.30,0.41,0.58])
for a in (ax,ax2):
    a.set_aspect("equal");a.axis("off")
fig.text(0.5,0.955,"Panneau hybride Muhleisen Corridor : le corridor naturel et la couronne biface",ha="center",fontsize=19,color=T,weight="bold")
fig.text(0.29,0.905,"Coupe : machine en haut d'une pente face au vent dominant",ha="center",fontsize=13,color=G)
fig.text(0.785,0.905,"Vue en plan : champ, chemin de gravier et couronne",ha="center",fontsize=13,color=G)
def num(a,x,y,k,r=0.9):
    a.add_patch(Circle((x,y),r,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.set_xlim(-36,36);ax.set_ylim(-9,42)
xs=np.linspace(-36,36,400)
def ground(x):
    return np.where(x<-30,0,np.where(x<8,(x+30)*18/38,np.where(x<16,18,18-(x-16)*0.9)))
ys=ground(xs)
ax.fill_between(xs,-9,ys,color=SL,zorder=1)
ax.plot(xs,ys,color="#8a7a50",lw=1.5,zorder=2)
ax.fill_between(xs,-9,ys,where=(xs<-30),color="#9ec6e6",zorder=1)
ax.text(-33,1.2,"mer",ha="center",fontsize=10,color="#2a5d8a")
for xp in np.arange(-24,-4,2.6):
    yg=ground(xp)
    ax.plot([xp+0.9,xp+0.9],[yg,yg+1.0],color="#777777",lw=1.5,zorder=3)
    ax.plot([xp,xp+2.0],[yg+1.0,yg+2.1],color=GP,lw=4,solid_capstyle="butt",zorder=4)
for xp in np.arange(-3.2,7,2.6):
    yg=ground(xp)
    ax.plot([xp+0.9,xp+0.9],[yg,yg+0.8],color="#777777",lw=1.5,zorder=3)
    ax.plot([xp,xp+2.0],[yg+0.8,yg+1.15],color=GP,lw=4,solid_capstyle="butt",zorder=4)
ax.add_patch(Rectangle((-28.6,ground(-27.3)-0.2),2.6,0.5,facecolor=GR,edgecolor="#999999",zorder=3))
ax.plot([-28.4,-26.6],[ground(-27.3)+1.6,ground(-27.3)+0.5],color=BF,lw=4,solid_capstyle="butt",zorder=4)
ax.plot([-27.5,-27.5],[ground(-27.3)+0.3,ground(-27.3)+1.0],color="#777777",lw=1.5,zorder=3)
ax.add_patch(Rectangle((8.2,17.8),2.6,0.5,facecolor=GR,edgecolor="#999999",zorder=3))
ax.plot([8.4,10.4],[19.2,18.4],color=BF,lw=4,solid_capstyle="butt",zorder=4)
ax.plot([9.3,9.3],[18.3,19.0],color="#777777",lw=1.5,zorder=3)
mx=1.5;gy=ground(mx)
ax.add_patch(Polygon([[mx-0.5,gy],[mx+0.5,gy],[mx+0.35,gy+5],[mx-0.35,gy+5]],facecolor="#9a9a9a",edgecolor=G,zorder=5))
ax.add_patch(Rectangle((mx-1.2,gy-1.2),2.4,1.2,facecolor="#bdbdbd",edgecolor=G,zorder=5))
hy=gy+9
ax.add_patch(Circle((mx,hy),3.3,facecolor="#bfe3f5",edgecolor="#4a90c2",lw=1.2,alpha=0.35,zorder=5))
ax.add_patch(Circle((mx,hy),3,fill=False,ls=(0,(4,4)),color="#999999",lw=0.9,zorder=6))
th=np.radians(35);u=np.array([np.cos(th),np.sin(th)])
c=np.array([mx,hy]);rs=1.86
ax.plot(*zip(c-rs*u,c+rs*u),color=B,lw=7,solid_capstyle="butt",zorder=7)
n_=np.array([-np.sin(th),np.cos(th)])
for p0,d in ((c+rs*u,u),(c-rs*u,-n_)):
    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*0.57+pe*0.14)+tt**2*(p0+d*1.14) for tt in t])
    ax.plot(pts[:,0],pts[:,1],color="#7fb6cc",lw=5,solid_capstyle="butt",zorder=7)
ax.plot([mx,mx],[gy+5,hy],color="#9a9a9a",lw=4,zorder=4)
ax.add_patch(Circle(c,0.25,facecolor="#7a7a7a",edgecolor=T,zorder=8))
for yy,ln in ((22,1.0),(26,1.0),(30,1.0),(34,1.0)):
    ax.annotate("",xy=(-18+6*ln,yy),xytext=(-33,yy),arrowprops=dict(arrowstyle="-|>",color=W,lw=2.0),zorder=9)
for i,(x0,y0,x1,y1) in enumerate(((-12,22,-4,24.3),(-8,26,-1,27.2),(-4,30,2,30.6))):
    ax.annotate("",xy=(x1,y1),xytext=(x0,y0),arrowprops=dict(arrowstyle="-|>",color=W,lw=2.6),zorder=9)
ax.text(-33,36.5,"Vent dominant",fontsize=12,color=W,weight="bold")
ax.text(-9,37.5,"L'air accélère sur la pente :\n+10 à +30 % à la crête, sur toute la hauteur du rotor",fontsize=10.5,color=T,ha="left")
ax.text(-9,-2.6,"pente de 10 à 20 % face au vent, colline de 20 à 50 m",ha="center",fontsize=10,color=G)
lead(ax,-14,ground(-14)+2.6,-20,14);num(ax,-20,14,1)
lead(ax,-27.4,ground(-27.3)+1.8,-30,8);num(ax,-30,8,2)
lead(ax,mx+0.4,gy+2.5,14,10);num(ax,14,10,3)
lead(ax,c[0]+2.3,c[1]+2.2,16,34);num(ax,16,34,4)
lead(ax,9.4,19.3,20,24);num(ax,20,24,5)
lead(ax,3.9,ground(3.9)+1.3,22,13);num(ax,22,13,6)
ax2.set_xlim(-19,19);ax2.set_ylim(-19,19)
ax2.add_patch(Circle((0,0),14.6,facecolor=GR,edgecolor="#999999",lw=1,zorder=1))
ax2.add_patch(Circle((0,0),9.5,facecolor="#dce8d2",edgecolor="#999999",lw=1,zorder=2))
for y in np.arange(-8.6,9.0,2.2):
    half=np.sqrt(max(9.5**2-y*y,0))-0.3
    if half<1:continue
    for x in np.arange(-half,half-1.0,1.05):
        if abs(x+0.5)<1.6 and abs(y)<1.6:continue
        ax2.add_patch(Rectangle((x,y-0.9),0.95,1.8,facecolor=GP,edgecolor="#0b2a52",lw=0.4,zorder=3))
ax2.add_patch(Rectangle((-1.2,-1.2),2.4,2.4,facecolor="#bdbdbd",edgecolor=G,zorder=4))
ax2.add_patch(Circle((0,0),0.5,facecolor="#7a7a7a",edgecolor=T,zorder=5))
ax2.plot([-7.5,7.5],[-7.5,7.5],color="#8c8c8c",lw=7,solid_capstyle="round",alpha=0.9,zorder=4)
ax2.plot([-7.5,7.5],[-7.5,7.5],color=B,lw=4,solid_capstyle="round",zorder=5)
for t_ in (-6.6,-2.2,2.2,6.6):
    ax2.plot([t_-0.9,t_+0.9],[t_+0.9,t_-0.9],color="#4a90c2",lw=2.2,zorder=6)
ax2.add_patch(Wedge((0,0),11.1,55,125,width=1.6,facecolor=BF,edgecolor="#1b4d38",lw=0.8,zorder=3))
ax2.add_patch(Wedge((0,0),16.3,-40,40,width=1.7,facecolor=BF,edgecolor="#1b4d38",lw=0.8,zorder=3))
ax2.add_patch(Wedge((0,0),16.3,140,220,width=1.7,facecolor=BF,edgecolor="#1b4d38",lw=0.8,zorder=3))
ax2.annotate("",xy=(-6,-6),xytext=(-16,-16),arrowprops=dict(arrowstyle="-|>",color=W,lw=2.6),zorder=9)
ax2.text(-18.5,-18.2,"Vent dominant (sud-ouest)",fontsize=10.5,color=W,weight="bold")
ax2.text(0,-17.6,"S",ha="center",fontsize=12,color=A,weight="bold");ax2.text(0,17.0,"N",ha="center",fontsize=12,color=T,weight="bold")
ax2.text(17.2,-0.4,"E",fontsize=12,color=T,weight="bold");ax2.text(-18.4,-0.4,"O",fontsize=12,color=T,weight="bold")
ax2.text(0,12.3,"chemin de gravier blanc, 3 m",ha="center",fontsize=9.5,color=G,zorder=6)
lead(ax2,4,4,10,-9);num(ax2,10,-9,3)
lead(ax2,-3.2,-5.6,-10,-11.5);num(ax2,-10,-11.5,7)
lead(ax2,0,10.2,-6,14.5);num(ax2,-6,14.5,8)
lead(ax2,15.4,2,13.5,11);num(ax2,13.5,11,9)
lead(ax2,-15.4,2,-13.5,11);num(ax2,-13.5,11,10)
lead(ax2,12.6,-5,8,-14.5);num(ax2,8,-14.5,11)
items=["1  Champ fixe à 10° sud, serré, en amphithéâtre sur la pente","2  Rangée de pied de pente, biface, face arrière sur le gravier","3  Machine : rotor 6 × 18 m avec ses 4 joues, mât de 5 m, à la crête","4  Survitesse de la colline : +23 à +67 % de vent sur le rotor selon la pente","5  Couronne biface à 30°, face arrière sur le chemin","6  Plateau derrière la crête : rangées sans étagement","7  Champ au sol de 48 kWc sous le rotor (inchangé)","8  Arc nord, face sud : +33 % par m² (gravier et ciel)","9  Arc est, face est : +64 % le matin","10  Arc ouest, face ouest : +55 % le soir","11  Chemin de gravier de 3 m : accès, parking, pas de végétation"]
for k,t in enumerate(items):
    col=k//4;row=k%4
    fig.text(0.02+col*0.34,0.235-row*0.038,t,fontsize=10.5,color=T)
fig.text(0.5,0.035,"Proportions approximatives. Brest, 5,5 m/s de vent régional : machine et champ 85 100 kWh/an (joues comprises), couronne +27 100, pente face au vent +9 300, amphithéâtre +2 300 : ~122 000 kWh/an à ~0,052 €/kWh, 0,048 sur une côte très ventée. www.mulo.world",ha="center",fontsize=10,color=G)
plt.savefig("schema_panneau_solaire_hybride_muhleisen_corridor.png",dpi=110,facecolor="white")
