diff --git a/config/pumpingStation.json b/config/pumpingStation.json index 5228354..25420f4 100644 --- a/config/pumpingStation.json +++ b/config/pumpingStation.json @@ -234,7 +234,7 @@ { "name": "Zone Spill", "type": "rectangle", - "placement": { "top": 20, "left": 10, "width": 380, "height": {{h_spill}} }, + "placement": { "top": 40, "left": 10, "width": 380, "height": {{h_spill}} }, "background": { "color": { "fixed": "rgba(229, 67, 67, 0.18)" } }, "border": { "color": { "fixed": "transparent" }, "width": 0 }, "config": { "text": { "mode": "fixed", "fixed": "" } } @@ -266,7 +266,7 @@ { "name": "Tank Outline", "type": "rectangle", - "placement": { "top": 20, "left": 10, "width": 380, "height": 680 }, + "placement": { "top": 40, "left": 10, "width": 380, "height": 680 }, "background": { "color": { "fixed": "transparent" } }, "border": { "color": { "fixed": "#8a8a8a" }, "width": 2 }, "config": { "text": { "mode": "fixed", "fixed": "" } } @@ -389,7 +389,7 @@ { "name": "Header Rim", "type": "text", - "placement": { "top": 2, "left": 10, "width": 380, "height": 16 }, + "placement": { "top": 20, "left": 10, "width": 380, "height": 16 }, "background": { "color": { "fixed": "transparent" } }, "border": { "color": { "fixed": "transparent" }, "width": 0 }, "config": { "text": { "mode": "fixed", "fixed": "rim ({{heightBasin}} m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" } @@ -397,7 +397,7 @@ { "name": "Footer Floor", "type": "text", - "placement": { "top": 728, "left": 10, "width": 380, "height": 16 }, + "placement": { "top": 724, "left": 10, "width": 380, "height": 16 }, "background": { "color": { "fixed": "transparent" } }, "border": { "color": { "fixed": "transparent" }, "width": 0 }, "config": { "text": { "mode": "fixed", "fixed": "floor (0.00 m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" } diff --git a/src/specificClass.js b/src/specificClass.js index 06185fa..3ada38e 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -189,12 +189,11 @@ class DashboardApi { const dryRunLevel = outflowLevel * (1 + dryRunPct / 100); const highSafetyLevel = overflowLevel * (highPct / 100); - // Canvas tank: rim at y=20px, floor at y=700px (680px tall). Must match - // hard-coded tank rectangle placement in config/pumpingStation.json - // (basin row is h:20 grid rows; canvas root frame is 400x760 px — taller - // than wide to match the card's aspect ratio so the tank fills the card - // vertically with no letterboxing). - const TANK_TOP = 20, TANK_BOT = 700, TANK_H = TANK_BOT - TANK_TOP; + // Canvas tank: rim at y=40px, floor at y=720px (680px tall). Centered + // vertically in a 760px tall frame with 40px top/bottom margins for the + // header ('rim (X m)') and footer ('floor (0.00 m)') labels. Must match + // hard-coded tank rectangle placement in config/pumpingStation.json. + const TANK_TOP = 40, TANK_BOT = 720, TANK_H = TANK_BOT - TANK_TOP; const yFor = (v) => +(TANK_BOT - (v / heightBasin) * TANK_H).toFixed(2); const tyFor = (yLine) => +(yLine - 8).toFixed(2); // centre 16px text on the line @@ -213,9 +212,10 @@ class DashboardApi { // basin (dryRun=2% means dryRunLevel sits right on outflowLevel; high- // Safety=98% puts it just under overflowLevel) this naturally puts // highSafety BELOW and outflow BELOW. - const ABOVE_OFFSET = 22; // label_top = line_y - 22 (label bottom is 6 px clear above the line) - const BELOW_OFFSET = 6; // label_top = line_y + 6 (label is 6 px clear below the line) - const MIN_DIST_FOR_ABOVE = 24; // if distance to previous (upper) line < this, go below + const LABEL_H = 16; + const ABOVE_OFFSET = 22; // label_top = line_y - 22 (6 px clear above line) + const BELOW_OFFSET = 6; // label_top = line_y + 6 (6 px clear below line) + const MIN_DIST_FOR_ABOVE = 24; // if distance to upper line < this, try below const lines = [ { id: 'overflow', line: y_overflow }, { id: 'highSafety', line: y_highSafety }, @@ -226,7 +226,20 @@ class DashboardApi { for (let i = 0; i < lines.length; i++) { const prev = i > 0 ? lines[i - 1] : null; const tooClose = prev && (lines[i].line - prev.line) < MIN_DIST_FOR_ABOVE; - lines[i].y = tooClose ? (lines[i].line + BELOW_OFFSET) : (lines[i].line - ABOVE_OFFSET); + if (tooClose) { + // Default to BELOW unless the label would be clipped by the tank + // floor (thresholds at the very bottom — dryRun=tiny% means + // dryRunLevel sits right on the floor). Then stack ABOVE the + // previous label instead, even if it slightly crowds its own line. + const belowY = lines[i].line + BELOW_OFFSET; + if (belowY + LABEL_H <= TANK_BOT) { + lines[i].y = belowY; + } else { + lines[i].y = prev.y + LABEL_H + 2; // stack above with 2 px gap + } + } else { + lines[i].y = lines[i].line - ABOVE_OFFSET; + } } const ty = Object.fromEntries(lines.map((l) => [l.id, +l.y.toFixed(2)]));