diff --git a/dashboards/forza_composite/index.html b/dashboards/forza_composite/index.html index 72e5088..f58b9e4 100644 --- a/dashboards/forza_composite/index.html +++ b/dashboards/forza_composite/index.html @@ -90,35 +90,63 @@ function draw(cur,max){ ctx.clearRect(0,0,W,H); const sx=Ca/640*W,sy=Cb/480*H,sr=Cr/640*W; const gw=Math.max(36,W/18); - const ae=d2r(Ae),ex=sx+sr*Math.cos(ae),ey=sy+sr*Math.sin(ae); + const ae=d2r(Ae),ex=sx+sr*Math.cos(ae),ey=sy+sr*Math.sin(ae),lx=W*.93; + const arcLen=Math.PI*2*sr*((Ae-As)/360),lineLen=lx-ex,totalLen=arcLen+lineLen; const rl=max*.85; + // point on total path (0→1 mapped to arc+line) + function pathPt(t){ + const ad=t*totalLen; + if(ad<=arcLen){ + const a=d2r(As+(Ae-As)*(ad/arcLen)); + return{x:sx+sr*Math.cos(a),y:sy+sr*Math.sin(a)}; + } + const lp=(ad-arcLen)/lineLen; + return{x:ex+lp*(lx-ex),y:ey}; + } + // background: arc + line ctx.beginPath(); ctx.arc(sx,sy,sr,d2r(As),d2r(Ae),false); - ctx.lineTo(W*.93,ey); + ctx.lineTo(lx,ey); ctx.strokeStyle='rgba(255,255,255,.04)'; ctx.lineWidth=gw; ctx.stroke(); - // progress: ARC ONLY (0→max RPM fills the 90° arc) + // progress along full path const pc=Math.min(cur/max,1); if(pc>.005){ + const ep=pathPt(pc); ctx.beginPath(); - ctx.arc(sx,sy,sr,d2r(As),d2r(As+(Ae-As)*pc),false); + const ad=pc*totalLen; + if(ad<=arcLen){ + ctx.arc(sx,sy,sr,d2r(As),d2r(As+(Ae-As)*(ad/arcLen)),false); + }else{ + ctx.arc(sx,sy,sr,d2r(As),d2r(Ae),false); + ctx.lineTo(ep.x,ey); + } ctx.strokeStyle=cur>=rl?'rgba(255,50,35,.95)':'rgba(255,255,255,.85)'; ctx.lineWidth=gw; ctx.stroke(); } - // scale marks + // scale marks along TOTAL path (arc + line) const mr=sr-gw/2-6,pr=sr+gw/2+6; for(let r=0;r<=max;r+=2000){ - const a=d2r(As+(Ae-As)*(r/max)); - ctx.beginPath();ctx.moveTo(sx+mr*Math.cos(a),sy+mr*Math.sin(a));ctx.lineTo(sx+pr*Math.cos(a),sy+pr*Math.sin(a)); - ctx.strokeStyle=r>=rl?'rgba(255,255,255,.15)':'rgba(255,255,255,.08)'; - ctx.lineWidth=r%4000===0?2.5:1;ctx.stroke(); - if(r%4000===0){ctx.fillStyle='rgba(255,255,255,.22)';ctx.font=`${Math.max(13,W/38)|0}px 'NEXT ART',sans-serif`;ctx.textAlign='center';ctx.fillText(Math.round(r/1000),sx+(pr+22)*Math.cos(a),sy+(pr+22)*Math.sin(a)+5)} + const t=r/max,p=pathPt(t); + if(r*totalLen/max<=arcLen){ + const a=d2r(As+(Ae-As)*t); + ctx.beginPath();ctx.moveTo(sx+mr*Math.cos(a),sy+mr*Math.sin(a));ctx.lineTo(sx+pr*Math.cos(a),sy+pr*Math.sin(a)); + ctx.strokeStyle=r>=rl?'rgba(255,255,255,.15)':'rgba(255,255,255,.08)'; + ctx.lineWidth=r%4000===0?2.5:1;ctx.stroke(); + if(r%4000===0){ctx.fillStyle='rgba(255,255,255,.22)';ctx.font=`${Math.max(13,W/38)|0}px 'NEXT ART',sans-serif`;ctx.textAlign='center';ctx.fillText(Math.round(r/1000),sx+(pr+22)*Math.cos(a),sy+(pr+22)*Math.sin(a)+5)} + }else{ + // on line portion: vertical marks + ctx.beginPath();ctx.moveTo(p.x,ey-gw/2-4);ctx.lineTo(p.x,ey+gw/2+4); + ctx.strokeStyle=r>=rl?'rgba(255,255,255,.15)':'rgba(255,255,255,.08)'; + ctx.lineWidth=r%4000===0?2.5:1;ctx.stroke(); + if(r%4000===0){ctx.fillStyle='rgba(255,255,255,.22)';ctx.font=`${Math.max(13,W/38)|0}px 'NEXT ART',sans-serif`;ctx.textAlign='center';ctx.fillText(Math.round(r/1000),p.x,ey+gw/2+22)} + } } // RPM number