chore(dashboardAPI): center basin labels, position above/below lines
Threshold labels were sitting right on top of their lines (label center at line_y - 8) and were right-aligned at the tank's right edge. They now: - Sit clearly above the line (label bottom 6 px above) by default, or below the line (label top 6 px below) when an adjacent threshold is closer than 24 px (would crowd both labels above their lines). For the current basin config this puts overflowLevel + inflowLevel + dryRunLevel ABOVE their lines, and highSafety + outflowLevel BELOW. - Are centered horizontally in the tank (name at left:115 width:95 right-aligned, value at left:215 width:80 left-aligned) so the combined phrase "overflowLevel 3.22 m" reads as one centered string. Value width 60 → 80 so 'mm'-formatted small-meter values don't wrap to two lines. Footer floor moved to y:728 to keep clear of the BELOW labels near the tank floor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -309,7 +309,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Label Overflow Name",
|
"name": "Label Overflow Name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": {{ty_overflow}}, "left": 180, "width": 140, "height": 16 },
|
"placement": { "top": {{ty_overflow}}, "left": 115, "width": 95, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "right", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "right", "valign": "middle" }
|
||||||
@@ -317,7 +317,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Label HighSafety Name",
|
"name": "Label HighSafety Name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": {{ty_highSafety}}, "left": 180, "width": 140, "height": 16 },
|
"placement": { "top": {{ty_highSafety}}, "left": 115, "width": 95, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "highSafety" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "right", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "highSafety" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "right", "valign": "middle" }
|
||||||
@@ -325,7 +325,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Label Inflow Name",
|
"name": "Label Inflow Name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": {{ty_inflow}}, "left": 180, "width": 140, "height": 16 },
|
"placement": { "top": {{ty_inflow}}, "left": 115, "width": 95, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "right", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "right", "valign": "middle" }
|
||||||
@@ -333,7 +333,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Label DryRun Name",
|
"name": "Label DryRun Name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": {{ty_dryRun}}, "left": 180, "width": 140, "height": 16 },
|
"placement": { "top": {{ty_dryRun}}, "left": 115, "width": 95, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "right", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "right", "valign": "middle" }
|
||||||
@@ -341,7 +341,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Label Outflow Name",
|
"name": "Label Outflow Name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": {{ty_outflow}}, "left": 180, "width": 140, "height": 16 },
|
"placement": { "top": {{ty_outflow}}, "left": 115, "width": 95, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "right", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "right", "valign": "middle" }
|
||||||
@@ -349,7 +349,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Value Overflow",
|
"name": "Value Overflow",
|
||||||
"type": "metric-value",
|
"type": "metric-value",
|
||||||
"placement": { "top": {{ty_overflow}}, "left": 323, "width": 65, "height": 16 },
|
"placement": { "top": {{ty_overflow}}, "left": 215, "width": 80, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "field", "fixed": "", "field": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "left", "valign": "middle" }
|
"config": { "text": { "mode": "field", "fixed": "", "field": "overflowLevel" }, "color": { "fixed": "#c92020" }, "size": 11, "align": "left", "valign": "middle" }
|
||||||
@@ -357,7 +357,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Value HighSafety",
|
"name": "Value HighSafety",
|
||||||
"type": "metric-value",
|
"type": "metric-value",
|
||||||
"placement": { "top": {{ty_highSafety}}, "left": 323, "width": 65, "height": 16 },
|
"placement": { "top": {{ty_highSafety}}, "left": 215, "width": 80, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "field", "fixed": "", "field": "highVolumeSafetyLevel" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "left", "valign": "middle" }
|
"config": { "text": { "mode": "field", "fixed": "", "field": "highVolumeSafetyLevel" }, "color": { "fixed": "#cf7e20" }, "size": 11, "align": "left", "valign": "middle" }
|
||||||
@@ -365,7 +365,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Value Inflow",
|
"name": "Value Inflow",
|
||||||
"type": "metric-value",
|
"type": "metric-value",
|
||||||
"placement": { "top": {{ty_inflow}}, "left": 323, "width": 65, "height": 16 },
|
"placement": { "top": {{ty_inflow}}, "left": 215, "width": 80, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "field", "fixed": "", "field": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "left", "valign": "middle" }
|
"config": { "text": { "mode": "field", "fixed": "", "field": "inflowLevel" }, "color": { "fixed": "#3d8a5a" }, "size": 11, "align": "left", "valign": "middle" }
|
||||||
@@ -373,7 +373,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Value DryRun",
|
"name": "Value DryRun",
|
||||||
"type": "metric-value",
|
"type": "metric-value",
|
||||||
"placement": { "top": {{ty_dryRun}}, "left": 323, "width": 65, "height": 16 },
|
"placement": { "top": {{ty_dryRun}}, "left": 215, "width": 80, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "field", "fixed": "", "field": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "left", "valign": "middle" }
|
"config": { "text": { "mode": "field", "fixed": "", "field": "dryRunLevel" }, "color": { "fixed": "#3a76a8" }, "size": 11, "align": "left", "valign": "middle" }
|
||||||
@@ -381,7 +381,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Value Outflow",
|
"name": "Value Outflow",
|
||||||
"type": "metric-value",
|
"type": "metric-value",
|
||||||
"placement": { "top": {{ty_outflow}}, "left": 323, "width": 65, "height": 16 },
|
"placement": { "top": {{ty_outflow}}, "left": 215, "width": 80, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "field", "fixed": "", "field": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "left", "valign": "middle" }
|
"config": { "text": { "mode": "field", "fixed": "", "field": "outflowLevel" }, "color": { "fixed": "#6a6a6a" }, "size": 11, "align": "left", "valign": "middle" }
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Footer Floor",
|
"name": "Footer Floor",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placement": { "top": 702, "left": 10, "width": 380, "height": 16 },
|
"placement": { "top": 728, "left": 10, "width": 380, "height": 16 },
|
||||||
"background": { "color": { "fixed": "transparent" } },
|
"background": { "color": { "fixed": "transparent" } },
|
||||||
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
"border": { "color": { "fixed": "transparent" }, "width": 0 },
|
||||||
"config": { "text": { "mode": "fixed", "fixed": "floor (0.00 m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" }
|
"config": { "text": { "mode": "fixed", "fixed": "floor (0.00 m)" }, "color": { "fixed": "#8a8a8a" }, "size": 10, "align": "center", "valign": "middle" }
|
||||||
|
|||||||
@@ -204,26 +204,31 @@ class DashboardApi {
|
|||||||
const y_dryRun = yFor(dryRunLevel);
|
const y_dryRun = yFor(dryRunLevel);
|
||||||
const y_outflow = yFor(outflowLevel);
|
const y_outflow = yFor(outflowLevel);
|
||||||
|
|
||||||
// Label y-positions get min-gap enforcement so labels never overlap even
|
// Label y-positions: labels sit either ABOVE or BELOW their threshold
|
||||||
// when thresholds sit nearly on top of each other (e.g. dryRun=2 % means
|
// line, never on it. Each label is offset by ABOVE_OFFSET=22 px above
|
||||||
// dryRunLevel sits right on outflowLevel; highSafety=98 % puts it under
|
// its line by default (16 px tall label + 6 px clear above the line).
|
||||||
// overflow). Lines stay at proportional y; only the label text moves.
|
// If two thresholds are too close together for both labels to fit ABOVE
|
||||||
// Two-pass (down + up) mirrors editor's basin-diagram.js placement logic.
|
// their lines (label of the lower one would cross the upper line), the
|
||||||
const GAP = 20;
|
// lower one's label flips BELOW its line instead. With the current
|
||||||
const labels = [
|
// basin (dryRun=2% means dryRunLevel sits right on outflowLevel; high-
|
||||||
{ id: 'overflow', y: tyFor(y_overflow) },
|
// Safety=98% puts it just under overflowLevel) this naturally puts
|
||||||
{ id: 'highSafety', y: tyFor(y_highSafety) },
|
// highSafety BELOW and outflow BELOW.
|
||||||
{ id: 'inflow', y: tyFor(y_inflow) },
|
const ABOVE_OFFSET = 22; // label_top = line_y - 22 (label bottom is 6 px clear above the line)
|
||||||
{ id: 'dryRun', y: tyFor(y_dryRun) },
|
const BELOW_OFFSET = 6; // label_top = line_y + 6 (label is 6 px clear below the line)
|
||||||
{ id: 'outflow', y: tyFor(y_outflow) },
|
const MIN_DIST_FOR_ABOVE = 24; // if distance to previous (upper) line < this, go below
|
||||||
].sort((a, b) => a.y - b.y);
|
const lines = [
|
||||||
for (let i = 1; i < labels.length; i++) {
|
{ id: 'overflow', line: y_overflow },
|
||||||
if (labels[i].y < labels[i - 1].y + GAP) labels[i].y = labels[i - 1].y + GAP;
|
{ id: 'highSafety', line: y_highSafety },
|
||||||
|
{ id: 'inflow', line: y_inflow },
|
||||||
|
{ id: 'dryRun', line: y_dryRun },
|
||||||
|
{ id: 'outflow', line: y_outflow },
|
||||||
|
].sort((a, b) => a.line - b.line);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
for (let i = labels.length - 2; i >= 0; i--) {
|
const ty = Object.fromEntries(lines.map((l) => [l.id, +l.y.toFixed(2)]));
|
||||||
if (labels[i].y > labels[i + 1].y - GAP) labels[i].y = labels[i + 1].y - GAP;
|
|
||||||
}
|
|
||||||
const ty = Object.fromEntries(labels.map((l) => [l.id, +l.y.toFixed(2)]));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
heightBasin: +heightBasin.toFixed(2),
|
heightBasin: +heightBasin.toFixed(2),
|
||||||
|
|||||||
Reference in New Issue
Block a user