feat(web): présentation client simplifiée (état structurel + plan)
Page non-technique pour le client final : verdict structurel feu tricolore, plan 2D coté chargé depuis assets/map_2d.csv, coupe hauteur d'eau, livrables. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
402
web/client.html
Normal file
402
web/client.html
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Cartographie de votre chambre de moulin à marée — Rapport client</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--navy:#0b2545;--navy2:#13315c;--blue:#1d5d9b;--blue-soft:#e7eef6;
|
||||||
|
--ink:#1a2433;--muted:#5c6b80;--line:#dce3ec;--bg:#f4f7fb;--card:#ffffff;
|
||||||
|
--green:#1f9d55;--green-bg:#e4f6ec;--orange:#e08a00;--orange-bg:#fdf1dd;
|
||||||
|
--red:#cc3b34;--red-bg:#fbe5e3;--water:#2a78b8;--water2:#8fc3e8;
|
||||||
|
--shadow:0 1px 3px rgba(11,37,69,.08),0 6px 20px rgba(11,37,69,.06);
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html,body{margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||||
|
color:var(--ink);background:var(--bg);line-height:1.55;
|
||||||
|
}
|
||||||
|
.wrap{max-width:1040px;margin:0 auto;padding:0 20px}
|
||||||
|
header.hero{
|
||||||
|
background:linear-gradient(135deg,var(--navy),var(--blue));
|
||||||
|
color:#fff;padding:48px 0 56px;
|
||||||
|
}
|
||||||
|
header.hero .wrap{display:flex;flex-direction:column;gap:10px}
|
||||||
|
.badge{align-self:flex-start;background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.25);
|
||||||
|
padding:5px 12px;border-radius:999px;font-size:13px;letter-spacing:.3px}
|
||||||
|
header.hero h1{margin:6px 0 0;font-size:30px;font-weight:700;line-height:1.2}
|
||||||
|
header.hero p.sub{margin:0;font-size:17px;color:#dbe7f5;max-width:680px}
|
||||||
|
header.hero .meta{margin-top:8px;font-size:13px;color:#b9cce3}
|
||||||
|
|
||||||
|
section{padding:40px 0}
|
||||||
|
section:nth-of-type(even){background:#fff}
|
||||||
|
h2{font-size:22px;margin:0 0 6px;color:var(--navy)}
|
||||||
|
.lead{color:var(--muted);margin:0 0 24px;max-width:720px}
|
||||||
|
|
||||||
|
.grid{display:grid;gap:18px}
|
||||||
|
.cards4{grid-template-columns:repeat(4,1fr)}
|
||||||
|
.cards2{grid-template-columns:repeat(2,1fr)}
|
||||||
|
@media(max-width:880px){.cards4{grid-template-columns:repeat(2,1fr)}}
|
||||||
|
@media(max-width:560px){.cards4,.cards2{grid-template-columns:1fr}}
|
||||||
|
|
||||||
|
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;
|
||||||
|
padding:20px;box-shadow:var(--shadow)}
|
||||||
|
.card .ico{width:44px;height:44px;border-radius:11px;background:var(--blue-soft);
|
||||||
|
display:flex;align-items:center;justify-content:center;margin-bottom:12px}
|
||||||
|
.card .ico svg{width:24px;height:24px;stroke:var(--blue);fill:none;stroke-width:2;
|
||||||
|
stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
.card h3{margin:0 0 4px;font-size:16px}
|
||||||
|
.card p{margin:0;font-size:14px;color:var(--muted)}
|
||||||
|
|
||||||
|
/* plan */
|
||||||
|
.plan-box{background:var(--card);border:1px solid var(--line);border-radius:14px;
|
||||||
|
padding:16px;box-shadow:var(--shadow)}
|
||||||
|
#plan{width:100%;height:auto;display:block;border-radius:8px;background:#fbfdff}
|
||||||
|
.plan-stats{display:flex;flex-wrap:wrap;gap:14px;margin-top:16px}
|
||||||
|
.stat{flex:1;min-width:140px;background:var(--blue-soft);border-radius:10px;padding:12px 14px}
|
||||||
|
.stat .v{font-size:22px;font-weight:700;color:var(--navy)}
|
||||||
|
.stat .l{font-size:13px;color:var(--muted)}
|
||||||
|
|
||||||
|
/* table feu tricolore */
|
||||||
|
.verdict{display:flex;align-items:center;gap:14px;background:var(--green-bg);
|
||||||
|
border:1px solid #bfe6cd;border-left:6px solid var(--green);
|
||||||
|
border-radius:12px;padding:16px 18px;margin-bottom:20px}
|
||||||
|
.verdict .dot{width:14px;height:14px;border-radius:50%;background:var(--green);flex:0 0 auto}
|
||||||
|
.verdict strong{color:#13653a}
|
||||||
|
table.struct{width:100%;border-collapse:collapse;background:#fff;border-radius:12px;
|
||||||
|
overflow:hidden;box-shadow:var(--shadow)}
|
||||||
|
table.struct th,table.struct td{padding:13px 16px;text-align:left;font-size:14px;
|
||||||
|
border-bottom:1px solid var(--line)}
|
||||||
|
table.struct th{background:var(--navy);color:#fff;font-weight:600}
|
||||||
|
table.struct tr:last-child td{border-bottom:none}
|
||||||
|
.pill{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:13px;
|
||||||
|
padding:4px 11px;border-radius:999px}
|
||||||
|
.pill .d{width:9px;height:9px;border-radius:50%}
|
||||||
|
.pill.ok{background:var(--green-bg);color:#13653a}.pill.ok .d{background:var(--green)}
|
||||||
|
.pill.warn{background:var(--orange-bg);color:#8a5500}.pill.warn .d{background:var(--orange)}
|
||||||
|
.pill.bad{background:var(--red-bg);color:#922b25}.pill.bad .d{background:var(--red)}
|
||||||
|
.legend{display:flex;gap:18px;flex-wrap:wrap;margin-top:14px;font-size:13px;color:var(--muted)}
|
||||||
|
.legend span{display:inline-flex;align-items:center;gap:6px}
|
||||||
|
.legend i{width:11px;height:11px;border-radius:50%;display:inline-block}
|
||||||
|
|
||||||
|
/* coupe */
|
||||||
|
.coupe-box{background:var(--card);border:1px solid var(--line);border-radius:14px;
|
||||||
|
padding:16px;box-shadow:var(--shadow)}
|
||||||
|
#coupe{width:100%;height:auto;display:block}
|
||||||
|
|
||||||
|
/* livrables */
|
||||||
|
ul.deliv{list-style:none;padding:0;margin:0;display:grid;gap:12px;
|
||||||
|
grid-template-columns:repeat(2,1fr)}
|
||||||
|
@media(max-width:560px){ul.deliv{grid-template-columns:1fr}}
|
||||||
|
ul.deliv li{background:#fff;border:1px solid var(--line);border-radius:12px;
|
||||||
|
padding:14px 16px;display:flex;gap:12px;align-items:flex-start;box-shadow:var(--shadow)}
|
||||||
|
ul.deliv .chk{width:24px;height:24px;border-radius:7px;background:var(--green-bg);
|
||||||
|
color:var(--green);font-weight:700;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
|
||||||
|
ul.deliv b{display:block;font-size:15px}
|
||||||
|
ul.deliv small{color:var(--muted)}
|
||||||
|
|
||||||
|
.note{font-size:12px;color:var(--muted);font-style:italic;margin-top:14px}
|
||||||
|
|
||||||
|
footer{background:var(--navy);color:#cdd8e8;padding:26px 0;text-align:center;font-size:14px}
|
||||||
|
footer b{color:#fff}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="hero">
|
||||||
|
<div class="wrap">
|
||||||
|
<span class="badge">Rapport client</span>
|
||||||
|
<h1>Cartographie de votre chambre de moulin à marée</h1>
|
||||||
|
<p class="sub">Mesure complète réalisée <strong>sans vider l'eau</strong> et <strong>sans aucun travaux</strong>.
|
||||||
|
Un robot sous-marin a relevé les murs, la profondeur et l'état des surfaces. Voici ce qu'on a trouvé.</p>
|
||||||
|
<p class="meta">Document de présentation — non technique</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- CE QU'ON MESURE -->
|
||||||
|
<section>
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Ce qu'on mesure</h2>
|
||||||
|
<p class="lead">Quatre relevés simples pour comprendre l'état de votre chambre, en restant à la surface.</p>
|
||||||
|
<div class="grid cards4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="ico"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 3v18"/></svg></div>
|
||||||
|
<h3>Le plan de la pièce</h3>
|
||||||
|
<p>Le contour exact des murs, vu de dessus, avec les longueurs et la surface au sol.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ico"><svg viewBox="0 0 24 24"><path d="M12 3v18M7 7l5-4 5 4M7 17l5 4 5-4"/></svg></div>
|
||||||
|
<h3>La hauteur d'eau</h3>
|
||||||
|
<p>La profondeur entre la surface et le fond, mesurée en continu pendant le relevé.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ico"><svg viewBox="0 0 24 24"><path d="M6 3v18M6 21h12M9 6l9 0M9 11l7 0M9 16l9 0"/></svg></div>
|
||||||
|
<h3>La verticalité des murs</h3>
|
||||||
|
<p>On vérifie si les murs sont bien droits ou s'ils penchent (le « dévers »).</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ico"><svg viewBox="0 0 24 24"><path d="M4 4h16v16H4z"/><path d="M8 4l3 16M14 4l2 16"/></svg></div>
|
||||||
|
<h3>L'état des surfaces</h3>
|
||||||
|
<p>Détection des fissures, des bosses et des décrochements sur les parois.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- PLAN -->
|
||||||
|
<section>
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Le plan de la pièce</h2>
|
||||||
|
<p class="lead">Vue de dessus reconstruite à partir du relevé du robot. Chaque point correspond à un mur détecté.</p>
|
||||||
|
<div class="plan-box">
|
||||||
|
<canvas id="plan" width="980" height="640"></canvas>
|
||||||
|
<div class="plan-stats">
|
||||||
|
<div class="stat"><div class="v" id="st-len">—</div><div class="l">Longueur max</div></div>
|
||||||
|
<div class="stat"><div class="v" id="st-wid">—</div><div class="l">Largeur max</div></div>
|
||||||
|
<div class="stat"><div class="v" id="st-area">—</div><div class="l">Surface au sol estimée</div></div>
|
||||||
|
<div class="stat"><div class="v" id="st-h">2,4 m</div><div class="l">Hauteur d'eau moyenne</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="note" id="plan-note">Plan à l'échelle — graduations en mètres.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ETAT STRUCTUREL -->
|
||||||
|
<section>
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>État structurel</h2>
|
||||||
|
<p class="lead">Synthèse en code couleur. Vert = conforme, Orange = à surveiller, Rouge = action nécessaire.</p>
|
||||||
|
<div class="verdict">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<div><strong>Structure jugée saine</strong> — aucun défaut majeur détecté. Une surveillance périodique est recommandée.</div>
|
||||||
|
</div>
|
||||||
|
<table class="struct">
|
||||||
|
<thead><tr><th>Critère</th><th>Mesure</th><th>État</th><th>Commentaire</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Verticalité des murs (dévers)</td><td>1,8° max</td>
|
||||||
|
<td><span class="pill ok"><span class="d"></span>Conforme</span></td>
|
||||||
|
<td>Murs globalement droits, sous le seuil de tolérance.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Planéité des parois</td><td>écart 2,3 cm</td>
|
||||||
|
<td><span class="pill ok"><span class="d"></span>Conforme</span></td>
|
||||||
|
<td>Surfaces régulières, pas de bombement notable.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Fissures détectées</td><td>1 zone mineure</td>
|
||||||
|
<td><span class="pill warn"><span class="d"></span>À surveiller</span></td>
|
||||||
|
<td>Microfissure verticale ~30 cm sur mur Est, sans ouverture.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Affaissement du fond</td><td>−1,5 cm</td>
|
||||||
|
<td><span class="pill ok"><span class="d"></span>Conforme</span></td>
|
||||||
|
<td>Fond stable, pas de cuvette d'affaissement.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Intégrité globale</td><td>—</td>
|
||||||
|
<td><span class="pill ok"><span class="d"></span>Conforme</span></td>
|
||||||
|
<td>Aucun désaffleurement ni rupture structurelle observé.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="legend">
|
||||||
|
<span><i style="background:var(--green)"></i>Conforme</span>
|
||||||
|
<span><i style="background:var(--orange)"></i>À surveiller</span>
|
||||||
|
<span><i style="background:var(--red)"></i>Action nécessaire</span>
|
||||||
|
</div>
|
||||||
|
<p class="note">Valeurs d'exemple — seront remplacées par les mesures réelles après traitement final du relevé.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- COUPE -->
|
||||||
|
<section>
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Coupe / hauteur d'eau</h2>
|
||||||
|
<p class="lead">Vue en coupe verticale : la surface, la colonne d'eau et le fond de la chambre.</p>
|
||||||
|
<div class="coupe-box">
|
||||||
|
<canvas id="coupe" width="980" height="360"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- LIVRABLES -->
|
||||||
|
<section>
|
||||||
|
<div class="wrap">
|
||||||
|
<h2>Ce que vous recevez</h2>
|
||||||
|
<p class="lead">L'ensemble des documents remis à la fin de la prestation.</p>
|
||||||
|
<ul class="deliv">
|
||||||
|
<li><span class="chk">✓</span><div><b>Plan 2D coté</b><small>Contour de la pièce avec dimensions — formats PDF et DXF (CAO).</small></div></li>
|
||||||
|
<li><span class="chk">✓</span><div><b>Nuage de points 3D</b><small>Modèle complet de la chambre — formats .ply / .las.</small></div></li>
|
||||||
|
<li><span class="chk">✓</span><div><b>Rapport structurel</b><small>Synthèse de l'état des murs, fissures et intégrité.</small></div></li>
|
||||||
|
<li><span class="chk">✓</span><div><b>Tableau de mesures</b><small>Longueurs, surface, hauteur d'eau et dévers des parois.</small></div></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="wrap"><b>Silent Flow</b> / <b>Cosma</b> — projet moulin à marée</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// ---- Fallback contour (échantillon réel lu dans assets/map_2d.csv) ----
|
||||||
|
var FALLBACK = [[0.01,4.02],[-0.54,-0.97],[2.65,4.0],[-3.95,1.52],[3.98,0.74],[-0.55,4.05],[2.87,-1.12],[-0.83,3.82],[-0.21,-1.21],[-1.0,4.92],[-3.03,2.79],[-1.44,2.75],[-0.81,5.26],[-3.85,4.42],[4.18,-0.28],[-2.86,6.65],[-3.02,2.52],[-1.97,6.28],[-1.39,2.36],[-2.37,6.0],[-0.61,3.34],[-2.29,5.79],[-3.58,1.36],[-0.69,2.96],[-3.5,3.24],[-0.81,-0.18],[-1.74,2.48],[-2.31,0.6],[0.99,3.57],[-2.77,-1.17],[1.24,3.76],[-3.52,0.65],[3.34,-1.05],[-0.81,2.99],[-0.4,-1.1],[4.29,2.2],[-1.24,-1.08],[4.12,1.69],[-3.82,5.22],[-0.06,-1.16],[3.98,3.52],[-4.01,2.97],[3.97,1.7],[0.54,3.51],[-1.1,0.6],[0.35,3.51],[-3.04,0.54],[4.12,1.61],[-1.78,2.46],[0.44,-1.68]];
|
||||||
|
|
||||||
|
function parseCSV(txt){
|
||||||
|
var pts=[],lines=txt.split(/\r?\n/);
|
||||||
|
for(var i=1;i<lines.length;i++){
|
||||||
|
var l=lines[i].trim();if(!l)continue;
|
||||||
|
var c=l.split(',');if(c.length<2)continue;
|
||||||
|
var x=parseFloat(c[0]),y=parseFloat(c[1]);
|
||||||
|
if(isFinite(x)&&isFinite(y))pts.push([x,y]);
|
||||||
|
}
|
||||||
|
return pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convex hull (Andrew monotone chain) pour estimer surface + cotes
|
||||||
|
function hull(pts){
|
||||||
|
var p=pts.slice().sort(function(a,b){return a[0]-b[0]||a[1]-b[1]});
|
||||||
|
if(p.length<3)return p;
|
||||||
|
function cross(o,a,b){return (a[0]-o[0])*(b[1]-o[1])-(a[1]-o[1])*(b[0]-o[0]);}
|
||||||
|
var lo=[],up=[];
|
||||||
|
for(var i=0;i<p.length;i++){while(lo.length>=2&&cross(lo[lo.length-2],lo[lo.length-1],p[i])<=0)lo.pop();lo.push(p[i]);}
|
||||||
|
for(var j=p.length-1;j>=0;j--){while(up.length>=2&&cross(up[up.length-2],up[up.length-1],p[j])<=0)up.pop();up.push(p[j]);}
|
||||||
|
lo.pop();up.pop();return lo.concat(up);
|
||||||
|
}
|
||||||
|
function polyArea(poly){var a=0;for(var i=0;i<poly.length;i++){var j=(i+1)%poly.length;a+=poly[i][0]*poly[j][1]-poly[j][0]*poly[i][1];}return Math.abs(a)/2;}
|
||||||
|
|
||||||
|
function bbox(pts){
|
||||||
|
var xn=1e9,xx=-1e9,yn=1e9,yx=-1e9;
|
||||||
|
for(var i=0;i<pts.length;i++){var p=pts[i];
|
||||||
|
if(p[0]<xn)xn=p[0];if(p[0]>xx)xx=p[0];if(p[1]<yn)yn=p[1];if(p[1]>yx)yx=p[1];}
|
||||||
|
return {xn:xn,xx:xx,yn:yn,yx:yx};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt(n){return n.toFixed(1).replace('.',',');}
|
||||||
|
|
||||||
|
function drawPlan(pts){
|
||||||
|
var cv=document.getElementById('plan'),ctx=cv.getContext('2d');
|
||||||
|
var W=cv.width,H=cv.height;
|
||||||
|
ctx.clearRect(0,0,W,H);ctx.fillStyle='#fbfdff';ctx.fillRect(0,0,W,H);
|
||||||
|
var b=bbox(pts);
|
||||||
|
var wx=b.xx-b.xn,wy=b.yx-b.yn;
|
||||||
|
var pad=70;
|
||||||
|
var sx=(W-2*pad)/wx,sy=(H-2*pad)/wy;
|
||||||
|
var s=Math.min(sx,sy);
|
||||||
|
var ox=(W-wx*s)/2,oy=(H-wy*s)/2;
|
||||||
|
// y inversé (haut = +y)
|
||||||
|
function X(x){return ox+(x-b.xn)*s;}
|
||||||
|
function Y(y){return H-(oy+(y-b.yn)*s);}
|
||||||
|
|
||||||
|
// grille metrique
|
||||||
|
ctx.strokeStyle='#e6edf5';ctx.lineWidth=1;ctx.fillStyle='#9aa9bd';
|
||||||
|
ctx.font='11px system-ui';ctx.textAlign='center';
|
||||||
|
for(var gx=Math.ceil(b.xn);gx<=b.xx;gx++){
|
||||||
|
var px=X(gx);ctx.beginPath();ctx.moveTo(px,oy);ctx.lineTo(px,H-oy);ctx.stroke();
|
||||||
|
ctx.fillText(gx+' m',px,H-oy+16);
|
||||||
|
}
|
||||||
|
ctx.textAlign='right';ctx.textBaseline='middle';
|
||||||
|
for(var gy=Math.ceil(b.yn);gy<=b.yx;gy++){
|
||||||
|
var py=Y(gy);ctx.beginPath();ctx.moveTo(ox,py);ctx.lineTo(W-ox,py);ctx.stroke();
|
||||||
|
ctx.fillText(gy+' m',ox-8,py);
|
||||||
|
}
|
||||||
|
ctx.textBaseline='alphabetic';
|
||||||
|
|
||||||
|
// hull (contour estimé)
|
||||||
|
var h=hull(pts);
|
||||||
|
if(h.length>2){
|
||||||
|
ctx.beginPath();ctx.moveTo(X(h[0][0]),Y(h[0][1]));
|
||||||
|
for(var i=1;i<h.length;i++)ctx.lineTo(X(h[i][0]),Y(h[i][1]));
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStyle='rgba(29,93,155,.10)';ctx.fill();
|
||||||
|
ctx.strokeStyle='#1d5d9b';ctx.lineWidth=2.5;ctx.stroke();
|
||||||
|
}
|
||||||
|
// points murs
|
||||||
|
ctx.fillStyle='#0b2545';
|
||||||
|
for(var k=0;k<pts.length;k++){ctx.fillRect(X(pts[k][0])-1,Y(pts[k][1])-1,2,2);}
|
||||||
|
|
||||||
|
// cotes longueur/largeur sur bbox
|
||||||
|
ctx.strokeStyle='#cc3b34';ctx.lineWidth=1.5;ctx.fillStyle='#922b25';
|
||||||
|
ctx.font='bold 13px system-ui';ctx.textAlign='center';
|
||||||
|
// largeur (axe x) en bas
|
||||||
|
var yb=H-oy+34;
|
||||||
|
ctx.beginPath();ctx.moveTo(X(b.xn),yb);ctx.lineTo(X(b.xx),yb);ctx.stroke();
|
||||||
|
ctx.fillText(fmt(wx)+' m',(X(b.xn)+X(b.xx))/2,yb-5);
|
||||||
|
// longueur (axe y) a droite
|
||||||
|
var xr=W-ox+34;
|
||||||
|
ctx.beginPath();ctx.moveTo(xr,Y(b.yn));ctx.lineTo(xr,Y(b.yx));ctx.stroke();
|
||||||
|
ctx.save();ctx.translate(xr+13,(Y(b.yn)+Y(b.yx))/2);ctx.rotate(-Math.PI/2);
|
||||||
|
ctx.fillText(fmt(wy)+' m',0,0);ctx.restore();
|
||||||
|
|
||||||
|
// echelle / regle 1m bas-gauche
|
||||||
|
var sl=s;ctx.strokeStyle='#0b2545';ctx.lineWidth=3;
|
||||||
|
ctx.beginPath();ctx.moveTo(18,H-22);ctx.lineTo(18+sl,H-22);ctx.stroke();
|
||||||
|
ctx.fillStyle='#0b2545';ctx.font='11px system-ui';ctx.textAlign='left';
|
||||||
|
ctx.fillText('1 m',18,H-28);
|
||||||
|
|
||||||
|
// stats
|
||||||
|
document.getElementById('st-len').textContent=fmt(wy)+' m';
|
||||||
|
document.getElementById('st-wid').textContent=fmt(wx)+' m';
|
||||||
|
var area=h.length>2?polyArea(h):wx*wy;
|
||||||
|
document.getElementById('st-area').textContent=fmt(area)+' m²';
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPlan(){
|
||||||
|
fetch('assets/map_2d.csv').then(function(r){
|
||||||
|
if(!r.ok)throw new Error('http '+r.status);return r.text();
|
||||||
|
}).then(function(t){
|
||||||
|
var pts=parseCSV(t);
|
||||||
|
if(pts.length<3)throw new Error('vide');
|
||||||
|
drawPlan(pts);
|
||||||
|
}).catch(function(e){
|
||||||
|
document.getElementById('plan-note').textContent=
|
||||||
|
'Plan à l\'échelle (aperçu hors-ligne) — graduations en mètres.';
|
||||||
|
drawPlan(FALLBACK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCoupe(){
|
||||||
|
var cv=document.getElementById('coupe'),ctx=cv.getContext('2d');
|
||||||
|
var W=cv.width,H=cv.height;ctx.clearRect(0,0,W,H);
|
||||||
|
var mL=70,mR=70,top=50,bot=H-50;
|
||||||
|
var surfY=top+10, fondY=bot-30;
|
||||||
|
// ciel
|
||||||
|
ctx.fillStyle='#fbfdff';ctx.fillRect(0,0,W,H);
|
||||||
|
// eau
|
||||||
|
var grad=ctx.createLinearGradient(0,surfY,0,fondY);
|
||||||
|
grad.addColorStop(0,'#8fc3e8');grad.addColorStop(1,'#2a78b8');
|
||||||
|
ctx.fillStyle=grad;ctx.fillRect(mL,surfY,W-mL-mR,fondY-surfY);
|
||||||
|
// fond (hachuré)
|
||||||
|
ctx.fillStyle='#7a6a55';ctx.fillRect(mL,fondY,W-mL-mR,bot-fondY);
|
||||||
|
ctx.strokeStyle='#5e5040';ctx.lineWidth=1;
|
||||||
|
for(var x=mL;x<W-mR;x+=14){ctx.beginPath();ctx.moveTo(x,fondY);ctx.lineTo(x+8,bot);ctx.stroke();}
|
||||||
|
// surface ligne + ondulation
|
||||||
|
ctx.strokeStyle='#1b5a8c';ctx.lineWidth=2;ctx.beginPath();
|
||||||
|
for(var sx=mL;sx<=W-mR;sx+=4){var yy=surfY+Math.sin((sx)/22)*2.5;sx==mL?ctx.moveTo(sx,yy):ctx.lineTo(sx,yy);}
|
||||||
|
ctx.stroke();
|
||||||
|
// labels
|
||||||
|
ctx.fillStyle='#0b2545';ctx.font='13px system-ui';ctx.textAlign='left';
|
||||||
|
ctx.fillText('Surface',mL+8,surfY-8);
|
||||||
|
ctx.fillStyle='#fff';ctx.fillText('Colonne d\'eau',mL+8,(surfY+fondY)/2);
|
||||||
|
ctx.fillStyle='#3a3328';ctx.fillText('Fond de la chambre',mL+8,bot-8);
|
||||||
|
// cote hauteur d'eau a gauche
|
||||||
|
ctx.strokeStyle='#cc3b34';ctx.lineWidth=1.5;ctx.fillStyle='#922b25';
|
||||||
|
var cx=mL-22;
|
||||||
|
ctx.beginPath();ctx.moveTo(cx,surfY);ctx.lineTo(cx,fondY);ctx.stroke();
|
||||||
|
function arr(y,d){ctx.beginPath();ctx.moveTo(cx,y);ctx.lineTo(cx-5,y+d);ctx.moveTo(cx,y);ctx.lineTo(cx+5,y+d);ctx.stroke();}
|
||||||
|
arr(surfY,8);arr(fondY,-8);
|
||||||
|
ctx.save();ctx.translate(cx-12,(surfY+fondY)/2);ctx.rotate(-Math.PI/2);
|
||||||
|
ctx.textAlign='center';ctx.font='bold 14px system-ui';ctx.fillText('2,4 m',0,0);ctx.restore();
|
||||||
|
// tirets cote vers surface/fond
|
||||||
|
ctx.strokeStyle='#cc3b34';ctx.setLineDash([4,3]);
|
||||||
|
ctx.beginPath();ctx.moveTo(cx,surfY);ctx.lineTo(mL,surfY);ctx.moveTo(cx,fondY);ctx.lineTo(mL,fondY);ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPlan();
|
||||||
|
drawCoupe();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user