343 lines
16 KiB
Python
343 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""Plan d'étage — murs en traits épais, ouvertures nettes."""
|
|
|
|
import matplotlib
|
|
matplotlib.use('Agg')
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.patches import FancyBboxPatch, Arc, Circle, Rectangle
|
|
import numpy as np
|
|
|
|
fig, ax = plt.subplots(1, 1, figsize=(26, 20))
|
|
ax.set_aspect('equal')
|
|
ax.axis('off')
|
|
|
|
# ── Palette ────────────────────────────────────────────────────
|
|
MUR = '#3a3a5c'
|
|
MUR_EXT = '#1a1a2e'
|
|
PIECE = {
|
|
'salon': '#fff8e7', 'cuisine': '#eef5e8', 'chambre': '#eaf0f8',
|
|
'sdb': '#e0ecf2', 'wc': '#f5ecec', 'bureau': '#f0f0f5',
|
|
'it': '#e8e8f0', 'couloir': '#f5f3ee',
|
|
}
|
|
MEUBLE = '#8b7355'
|
|
MEUBLE_IT = '#4a4a6a'
|
|
PORTE_C = '#8b4513'
|
|
FENETRE_C = '#7ec8e3'
|
|
COTE = '#888888'
|
|
TEXTE = '#1a1a2e'
|
|
LW = 8.0 # linewidth pour murs (épais, bien visible)
|
|
LW_EXT = 10.0 # murs extérieurs plus épais
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LAYOUT
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Bas (y=0..8) : Salon(0-8) | Cuisine(8-13) | SalleIT(13-15.5) | Ch1(15.5-20)
|
|
# Couloir (8..9.5)
|
|
# Haut (9.5..15) : SDB(0-4) | WC(4-6) | Ch2(6-11) | Ch3(11-16) | Bureau(16-20)
|
|
|
|
# ── Fond des pièces ────────────────────────────────────────────
|
|
for (x, y, w, h, c) in [
|
|
(0, 0, 8, 8, PIECE['salon']), (8, 0, 5, 8, PIECE['cuisine']),
|
|
(13, 0, 2.5, 8, PIECE['it']), (15.5, 0, 4.5, 8, PIECE['chambre']),
|
|
(0, 8, 20, 1.5, PIECE['couloir']),
|
|
(0, 9.5, 4, 5.5, PIECE['sdb']), (4, 9.5, 2, 5.5, PIECE['wc']),
|
|
(6, 9.5, 5, 5.5, PIECE['chambre']), (11, 9.5, 5, 5.5, PIECE['chambre']),
|
|
(16, 9.5, 4, 5.5, PIECE['bureau']),
|
|
]:
|
|
ax.add_patch(Rectangle((x, y), w, h, facecolor=c, edgecolor='none', zorder=1))
|
|
|
|
# ── Helpers ────────────────────────────────────────────────────
|
|
|
|
def mur_ligne(x1, y1, x2, y2, lw=LW):
|
|
"""Mur en trait épais avec bouts plats (solid_capstyle='butt')."""
|
|
ax.plot([x1, x2], [y1, y2], color=MUR, linewidth=lw,
|
|
solid_capstyle='butt', zorder=3)
|
|
|
|
def mur_ext_ligne(x1, y1, x2, y2):
|
|
"""Mur extérieur en trait très épais."""
|
|
ax.plot([x1, x2], [y1, y2], color=MUR_EXT, linewidth=LW_EXT,
|
|
solid_capstyle='butt', zorder=3)
|
|
|
|
def porte_h(x, y, largeur=0.9):
|
|
"""Porte dans un mur horizontal — battant vers le haut."""
|
|
ax.plot([x, x], [y, y+largeur], color=PORTE_C, linewidth=2.5, zorder=5)
|
|
arc = Arc((x, y), largeur*2, largeur*2, angle=90, theta1=0, theta2=90,
|
|
color=PORTE_C, linewidth=1.2, linestyle='--', zorder=4)
|
|
ax.add_patch(arc)
|
|
ax.add_patch(Circle((x, y), 0.06, facecolor=PORTE_C, edgecolor='none', zorder=5))
|
|
|
|
def porte_v(x, y, largeur=0.9):
|
|
"""Porte dans un mur vertical — battant vers la droite."""
|
|
ax.plot([x, x+largeur], [y, y], color=PORTE_C, linewidth=2.5, zorder=5)
|
|
arc = Arc((x, y), largeur*2, largeur*2, angle=0, theta1=0, theta2=90,
|
|
color=PORTE_C, linewidth=1.2, linestyle='--', zorder=4)
|
|
ax.add_patch(arc)
|
|
ax.add_patch(Circle((x, y), 0.06, facecolor=PORTE_C, edgecolor='none', zorder=5))
|
|
|
|
def fenetre_h(x1, x2, y):
|
|
"""Fenêtre dans un mur horizontal extérieur."""
|
|
# Petits traits de mur aux extrémités
|
|
for x in [x1, x2]:
|
|
ax.plot([x, x], [y-0.15, y+0.15], color=MUR_EXT, linewidth=2, zorder=4)
|
|
ax.plot([x1, x2], [y, y], color=FENETRE_C, linewidth=4, solid_capstyle='butt', zorder=4)
|
|
|
|
def fenetre_v(y1, y2, x):
|
|
"""Fenêtre dans un mur vertical extérieur."""
|
|
for y in [y1, y2]:
|
|
ax.plot([x-0.15, x+0.15], [y, y], color=MUR_EXT, linewidth=2, zorder=4)
|
|
ax.plot([x, x], [y1, y2], color=FENETRE_C, linewidth=4, solid_capstyle='butt', zorder=4)
|
|
|
|
def meuble(x, y, w, h, label='', couleur=MEUBLE, txt_color='white'):
|
|
ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.04",
|
|
facecolor=couleur, edgecolor='#3d2e1a', linewidth=0.8, alpha=0.75, zorder=6))
|
|
if label:
|
|
ax.text(x+w/2, y+h/2, label, ha='center', va='center',
|
|
fontsize=6.5, color=txt_color, fontweight='bold', zorder=7)
|
|
|
|
def label_piece(x, y, nom, surface, sz_nom=11, sz_surf=8):
|
|
ax.text(x, y+0.35, nom, ha='center', va='center', fontsize=sz_nom,
|
|
color=TEXTE, fontweight='bold', zorder=8)
|
|
ax.text(x, y-0.35, surface, ha='center', va='center', fontsize=sz_surf, color=COTE, zorder=8)
|
|
|
|
def cote(x1, y1, x2, y2, label, decal=0.5):
|
|
ax.plot([x1, x2], [y1, y2], color=COTE, linewidth=0.7, zorder=2)
|
|
dx, dy = x2-x1, y2-y1
|
|
L = np.sqrt(dx*dx+dy*dy)
|
|
if L > 0:
|
|
nx, ny = -dy/L*0.12, dx/L*0.12
|
|
ax.plot([x1+nx, x1-nx], [y1+ny, y1-ny], color=COTE, linewidth=0.7, zorder=2)
|
|
ax.plot([x2+nx, x2-nx], [y2+ny, y2-ny], color=COTE, linewidth=0.7, zorder=2)
|
|
mx, my = (x1+x2)/2, (y1+y2)/2
|
|
px, py = (-dy/L*decal, dx/L*decal) if L > 0 else (0, decal)
|
|
ax.text(mx+px, my+py, label, ha='center', va='center', fontsize=7.5, color=COTE, zorder=2)
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MURS EXTÉRIEURS (traits épais, segments avec gaps)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# Bas (y=0)
|
|
mur_ext_ligne(0, 0, 2, 0) # coin → fenêtre salon
|
|
mur_ext_ligne(6, 0, 9.1, 0) # fenêtre salon → porte entrée
|
|
mur_ext_ligne(10, 0, 13, 0) # porte entrée → fenêtre cuisine
|
|
mur_ext_ligne(13, 0, 14, 0) # → fenêtre IT
|
|
mur_ext_ligne(15, 0, 17, 0) # fenêtre IT → fenêtre ch1
|
|
mur_ext_ligne(19, 0, 20, 0) # fenêtre ch1 → coin
|
|
|
|
# Droite (x=20)
|
|
mur_ext_ligne(20, 0, 20, 2)
|
|
mur_ext_ligne(20, 6, 20, 11)
|
|
mur_ext_ligne(20, 13.5, 20, 15)
|
|
|
|
# Haut (y=15)
|
|
mur_ext_ligne(0, 15, 5, 15)
|
|
mur_ext_ligne(5.5, 15, 8, 15)
|
|
mur_ext_ligne(10, 15, 13, 15)
|
|
mur_ext_ligne(15, 15, 20, 15)
|
|
|
|
# Gauche (x=0)
|
|
mur_ext_ligne(0, 0, 0, 2)
|
|
mur_ext_ligne(0, 5, 0, 11)
|
|
mur_ext_ligne(0, 13.5, 0, 15)
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MURS INTÉRIEURS (traits épais, segments avec gaps pour portes)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# Salon | Cuisine (x=8)
|
|
mur_ligne(8, 0, 8, 8)
|
|
|
|
# Cuisine | Salle IT (x=13)
|
|
mur_ligne(13, 0, 13, 8)
|
|
|
|
# Salle IT | Chambre 1 (x=15.5)
|
|
mur_ligne(15.5, 0, 15.5, 8)
|
|
|
|
# Couloir BAS (y=8) — segments avec gaps pour portes
|
|
mur_ligne(0, 8, 3.05, 8) # gauche → porte salon
|
|
mur_ligne(3.95, 8, 9.55, 8) # porte salon → porte cuisine
|
|
mur_ligne(10.45, 8, 13.55, 8) # porte cuisine → porte IT
|
|
mur_ligne(14.45, 8, 17.05, 8) # porte IT → porte ch1
|
|
mur_ligne(17.95, 8, 20, 8) # porte ch1 → droite
|
|
|
|
# Couloir HAUT (y=9.5) — segments avec gaps pour portes
|
|
mur_ligne(0, 9.5, 1.55, 9.5) # gauche → porte SDB
|
|
mur_ligne(2.45, 9.5, 4.55, 9.5) # porte SDB → porte WC
|
|
mur_ligne(5.45, 9.5, 8.05, 9.5) # porte WC → porte ch2
|
|
mur_ligne(8.95, 9.5, 13.05, 9.5) # porte ch2 → porte ch3
|
|
mur_ligne(13.95, 9.5, 17.55, 9.5) # porte ch3 → porte bureau
|
|
mur_ligne(18.45, 9.5, 20, 9.5) # porte bureau → droite
|
|
|
|
# Murs verticaux partie haute
|
|
mur_ligne(4, 9.5, 4, 15) # SDB | WC
|
|
mur_ligne(6, 9.5, 6, 15) # WC | Ch2
|
|
mur_ligne(11, 9.5, 11, 15) # Ch2 | Ch3
|
|
mur_ligne(16, 9.5, 16, 15) # Ch3 | Bureau
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PORTES
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# Entrée (mur du bas)
|
|
porte_v(9.55, 0)
|
|
|
|
# Couloir bas → pièces
|
|
porte_h(3.5, 8) # salon
|
|
porte_h(10, 8) # cuisine
|
|
porte_h(14, 8) # salle IT
|
|
porte_h(17.5, 8) # chambre 1
|
|
|
|
# Couloir haut → pièces
|
|
porte_h(2, 9.5) # SDB
|
|
porte_h(5, 9.5) # WC
|
|
porte_h(8.5, 9.5) # chambre 2
|
|
porte_h(13.5, 9.5) # chambre 3
|
|
porte_h(18, 9.5) # bureau
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# FENÊTRES
|
|
# ═══════════════════════════════════════════════════════════════
|
|
fenetre_h(2, 6, 0) # salon — baie vitrée
|
|
fenetre_v(2, 5, 0) # salon — côté gauche
|
|
fenetre_h(10, 13, 0) # cuisine
|
|
fenetre_h(14, 15, 0) # salle IT
|
|
fenetre_h(17, 19, 0) # chambre 1
|
|
fenetre_v(2, 6, 20) # chambre 1 — côté droit
|
|
fenetre_v(11, 13.5, 0) # SDB
|
|
fenetre_h(5, 5.5, 15) # WC — haut
|
|
fenetre_h(8, 10, 15) # chambre 2 — haut
|
|
fenetre_h(13, 15, 15) # chambre 3 — haut
|
|
fenetre_v(11, 13.5, 20) # bureau — côté droit
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MEUBLES
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# Salon
|
|
meuble(1, 1.5, 3, 1, 'Canapé d\'angle')
|
|
meuble(1.5, 3.2, 1.2, 0.6, 'Table basse')
|
|
meuble(5.5, 1.5, 1.8, 0.7, 'Meuble TV')
|
|
meuble(1, 5, 1.5, 0.6, 'Bibliothèque')
|
|
meuble(3, 5, 1.5, 0.6, 'Bibliothèque')
|
|
|
|
# Cuisine
|
|
meuble(9, 1.5, 2.5, 0.6, 'Plan de travail')
|
|
meuble(9, 2.5, 1, 0.6, 'Évier')
|
|
meuble(10.5, 2.5, 1, 0.6, 'Plaques')
|
|
meuble(9, 3.8, 2.5, 1, 'Îlot central')
|
|
meuble(12, 1.5, 0.8, 0.6, 'Frigo')
|
|
meuble(9, 5.5, 1.5, 0.6, 'Cellier')
|
|
|
|
# Salle IT
|
|
meuble(13.5, 1, 1.5, 0.6, 'Rack 42U', couleur=MEUBLE_IT)
|
|
meuble(13.5, 2.2, 1.5, 0.6, 'Rack 24U', couleur=MEUBLE_IT)
|
|
meuble(13.5, 3.5, 1, 0.6, 'Onduleur', couleur=MEUBLE_IT)
|
|
meuble(14.5, 3.5, 0.8, 0.6, 'NAS', couleur=MEUBLE_IT)
|
|
meuble(13.5, 5, 1.2, 0.5, 'Bureau IT', couleur=MEUBLE_IT)
|
|
|
|
# Chambre 1
|
|
meuble(16.5, 1.5, 1.6, 2, 'Lit 160')
|
|
meuble(18.5, 1.5, 0.5, 0.5, '')
|
|
meuble(19.5, 1.5, 0.5, 0.5, '')
|
|
meuble(16.5, 5, 1.2, 0.5, 'Commode')
|
|
meuble(18.5, 5, 1, 0.5, 'Bureau')
|
|
|
|
# Salle de bain
|
|
meuble(0.5, 10.5, 1.5, 0.8, 'Douche ital.')
|
|
meuble(2.5, 10.5, 1, 0.6, 'Lavabo')
|
|
meuble(0.5, 12, 1.5, 0.7, 'Baignoire')
|
|
meuble(2.5, 12.5, 0.8, 0.6, 'WC')
|
|
|
|
# WC
|
|
meuble(4.5, 10.5, 0.8, 0.6, 'WC')
|
|
meuble(4.5, 12, 0.6, 0.5, 'Lavabo')
|
|
|
|
# Chambre 2
|
|
meuble(7, 10.5, 1.4, 1.9, 'Lit 140')
|
|
meuble(8.5, 10.5, 0.5, 0.5, '')
|
|
meuble(7, 13, 1.2, 0.5, 'Commode')
|
|
meuble(9, 13, 1, 0.5, 'Bureau')
|
|
|
|
# Chambre 3
|
|
meuble(12, 10.5, 1.6, 2, 'Lit 160')
|
|
meuble(14, 10.5, 0.5, 0.5, '')
|
|
meuble(15, 10.5, 0.5, 0.5, '')
|
|
meuble(12, 13.5, 1.2, 0.5, 'Armoire')
|
|
|
|
# Bureau
|
|
meuble(17, 10.5, 1.8, 0.7, 'Bureau')
|
|
meuble(17, 12, 1, 0.5, 'Étagère')
|
|
meuble(18.5, 12, 0.8, 0.5, 'Imprimante')
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ÉTIQUETTES
|
|
# ═══════════════════════════════════════════════════════════════
|
|
label_piece(4, 4, 'SALON / SÉJOUR', '32 m²')
|
|
label_piece(10.5, 4, 'CUISINE', '20 m²')
|
|
label_piece(14.25,4, 'SALLE IT', '10 m²', sz_nom=10)
|
|
label_piece(17.75,4, 'CHAMBRE 1', '18 m²')
|
|
label_piece(2, 12.5, 'SALLE DE BAIN', '11 m²', sz_nom=9)
|
|
label_piece(5, 12.5, 'WC', '5.5 m²', sz_nom=9)
|
|
label_piece(8.5, 12.5, 'CHAMBRE 2', '13.5 m²')
|
|
label_piece(13.5, 12.5, 'CHAMBRE 3', '13.5 m²')
|
|
label_piece(18, 12.5, 'BUREAU', '11 m²')
|
|
|
|
# Entrée
|
|
ax.text(9.55, -0.5, 'ENTRÉE', ha='center', fontsize=10, fontweight='bold', color=TEXTE, zorder=8)
|
|
ax.plot(9.55, -0.25, marker='v', color=PORTE_C, markersize=8, zorder=5)
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# COTES
|
|
# ═══════════════════════════════════════════════════════════════
|
|
cote(0, -1.2, 20, -1.2, '20.00 m')
|
|
cote(-1.2, 0, -1.2, 15, '15.00 m')
|
|
cote(0, -0.7, 8, -0.7, '8.00 m')
|
|
cote(8, -0.7, 13, -0.7, '5.00 m')
|
|
cote(13, -0.7, 15.5, -0.7, '2.50 m')
|
|
cote(15.5, -0.7, 20, -0.7, '4.50 m')
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LÉGENDE
|
|
# ═══════════════════════════════════════════════════════════════
|
|
lx, ly = 21.5, 14
|
|
ax.text(lx, ly, 'LÉGENDE', fontsize=10, fontweight='bold', color=TEXTE, zorder=9)
|
|
items = [
|
|
(MUR_EXT, 'Mur extérieur'),
|
|
(MUR, 'Mur intérieur'),
|
|
(FENETRE_C, 'Fenêtre'),
|
|
(PORTE_C, 'Porte'),
|
|
(MEUBLE_IT, 'Équipement IT'),
|
|
]
|
|
for i, (c, lbl) in enumerate(items):
|
|
y = ly - 0.7 - i*0.6
|
|
if lbl == 'Fenêtre':
|
|
ax.plot([lx, lx+1.5], [y, y], color=c, linewidth=4, zorder=9)
|
|
elif lbl == 'Porte':
|
|
ax.plot([lx, lx+0.8], [y, y], color=c, linewidth=2.5, zorder=9)
|
|
ax.add_patch(Circle((lx, y), 0.06, facecolor=c, edgecolor='none', zorder=9))
|
|
elif lbl == 'Mur extérieur':
|
|
ax.plot([lx, lx+1.5], [y, y], color=c, linewidth=LW_EXT, solid_capstyle='butt', zorder=9)
|
|
elif lbl == 'Mur intérieur':
|
|
ax.plot([lx, lx+1.5], [y, y], color=c, linewidth=LW, solid_capstyle='butt', zorder=9)
|
|
else:
|
|
ax.add_patch(Rectangle((lx, y-0.15), 1.5, 0.3, facecolor=c, edgecolor='none', zorder=9))
|
|
ax.text(lx+1.8, y, lbl, fontsize=7.5, color=TEXTE, va='center', zorder=9)
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TITRE
|
|
# ═══════════════════════════════════════════════════════════════
|
|
ax.text(10, 16.2, 'PLAN D\'ÉTAGE — MAISON FAMILIALE AVEC SALLE IT', ha='center',
|
|
fontsize=16, fontweight='bold', color=TEXTE, zorder=9)
|
|
ax.text(10, 15.7, 'Rez-de-chaussée | Surface totale : ~150 m² | Échelle indicative',
|
|
ha='center', fontsize=9, color=COTE, zorder=9)
|
|
|
|
ax.set_facecolor('#f0ede5')
|
|
fig.patch.set_facecolor('#e8e4dc')
|
|
ax.set_xlim(-2, 24)
|
|
ax.set_ylim(-2, 17.5)
|
|
|
|
output = '/root/workspace/plan_grande_maison.pdf'
|
|
plt.savefig(output, format='pdf', dpi=200, bbox_inches='tight',
|
|
facecolor=fig.get_facecolor(), edgecolor='none')
|
|
plt.close()
|
|
print(f"✅ PDF : {output}")
|