Editor: nudge dashed lines themselves, revert tank height
Reverts the tank-bigger approach from last commit. Instead of
scaling the tank and keeping strict proportionality, the dashed
threshold lines are now nudged apart directly so each gets a
guaranteed 36-px vertical gap. Inputs and labels align with the
lines (no more leader lines needed).
Trade-off: the diagram is now an ordered schematic, not a strictly
to-scale rendering. Values are still shown next to each line via
the input boxes, and the value ordering is preserved. For an editor
where the goal is entering parameters, readability wins over scale
fidelity.
Sizing reverted:
viewBox 620 → 430
tank h 520 → 340
botY 560 → 380
Behavior:
GAP 30 → 36 (more visible space between dashed lines)
placeItem takes a single y now (line + input + label + unit
share it); leader-line mechanism kept as hidden
plumbing in case we switch back to proportional later
Dead-volume band now anchors to the (possibly-nudged) outflow line
instead of the proportional y so it still visually meets the line
cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,7 @@
|
||||
// (dryRunLevel, overfillLevel) that are shown both in the diagram
|
||||
// and next to the safety-% fields. Same formulas as
|
||||
// specificClass._validateThresholdOrdering.
|
||||
const DIAG = { topY: 40, botY: 560 };
|
||||
const DIAG = { topY: 40, botY: 380 };
|
||||
const fNum = (id) => {
|
||||
const v = parseFloat(document.getElementById(`node-input-${id}`)?.value);
|
||||
return Number.isFinite(v) ? v : null;
|
||||
@@ -188,32 +188,24 @@
|
||||
const y = DIAG.botY - (val / basinH) * (DIAG.botY - DIAG.topY);
|
||||
return Math.max(DIAG.topY - 8, Math.min(DIAG.botY + 8, y));
|
||||
};
|
||||
// Place a right-column item. yLine is the threshold's true
|
||||
// proportional position on the tank; yInput is where the label
|
||||
// and input box land (may be nudged away from yLine to avoid
|
||||
// overlap with neighbouring items). A dashed leader line is
|
||||
// shown only when the two differ by more than a pixel or two.
|
||||
const placeItem = (id, yLine, yInput) => {
|
||||
// Place a row — line, label, input, unit all share the same y.
|
||||
// The diagram is a schematic ordered list (value order is
|
||||
// preserved, but the y-positions are distributed with a
|
||||
// guaranteed minimum gap for readability), not a strictly
|
||||
// proportional rendering.
|
||||
const placeItem = (id, y) => {
|
||||
const line = document.getElementById(`ps-line-${id}`);
|
||||
const label = document.getElementById(`ps-label-${id}`);
|
||||
const unit = document.getElementById(`ps-unit-${id}`);
|
||||
const fo = document.getElementById(`ps-fo-${id}`);
|
||||
const sub = document.getElementById(`ps-sub-${id}`);
|
||||
const lead = document.getElementById(`ps-leader-${id}`);
|
||||
if (line) { line.setAttribute('y1', yLine); line.setAttribute('y2', yLine); }
|
||||
if (label) label.setAttribute('y', yInput + 4);
|
||||
if (unit) unit.setAttribute('y', yInput + 4);
|
||||
if (fo) fo.setAttribute('y', yInput - 11);
|
||||
if (sub) sub.setAttribute('y', yInput + 15);
|
||||
if (lead) {
|
||||
if (Math.abs(yLine - yInput) > 2) {
|
||||
lead.setAttribute('x1', 325); lead.setAttribute('y1', yLine);
|
||||
lead.setAttribute('x2', 420); lead.setAttribute('y2', yInput);
|
||||
lead.setAttribute('visibility', 'visible');
|
||||
} else {
|
||||
lead.setAttribute('visibility', 'hidden');
|
||||
}
|
||||
}
|
||||
if (line) { line.setAttribute('y1', y); line.setAttribute('y2', y); }
|
||||
if (label) label.setAttribute('y', y + 4);
|
||||
if (unit) unit.setAttribute('y', y + 4);
|
||||
if (fo) fo.setAttribute('y', y - 11);
|
||||
if (sub) sub.setAttribute('y', y + 15);
|
||||
if (lead) lead.setAttribute('visibility', 'hidden');
|
||||
};
|
||||
|
||||
const redraw = () => {
|
||||
@@ -229,36 +221,29 @@
|
||||
const ovfLvl = (ovf != null && ovfPct != null) ? ovf * (ovfPct / 100) : null;
|
||||
|
||||
// Build the right-column items. basinHeight is pinned at the
|
||||
// rim (DIAG.topY); others float on the proportional axis and
|
||||
// get nudged apart so the input boxes don't overlap.
|
||||
// tank rim; others are sorted by their proportional y and then
|
||||
// pushed apart so every dashed line gets a minimum vertical
|
||||
// gap for readability. The diagram is a schematic ordered
|
||||
// list, not a strictly to-scale rendering.
|
||||
const items = [
|
||||
{ id: 'basinHeight', yLine: DIAG.topY, pinned: true },
|
||||
{ id: 'overflowLevel', yLine: yForLevel(fNum('overflowLevel'), basinH) },
|
||||
{ id: 'maxLevel', yLine: yForLevel(fNum('maxLevel'), basinH) },
|
||||
{ id: 'startLevel', yLine: yForLevel(fNum('startLevel'), basinH) },
|
||||
{ id: 'minLevel', yLine: yForLevel(fNum('minLevel'), basinH) },
|
||||
{ id: 'dryRunLevel', yLine: yForLevel(dryLvl, basinH) },
|
||||
{ id: 'outflowLevel', yLine: yForLevel(fNum('outflowLevel'), basinH) },
|
||||
].filter(it => it.yLine != null);
|
||||
{ id: 'basinHeight', yIdeal: DIAG.topY, pinned: true },
|
||||
{ id: 'overflowLevel', yIdeal: yForLevel(fNum('overflowLevel'), basinH) },
|
||||
{ id: 'maxLevel', yIdeal: yForLevel(fNum('maxLevel'), basinH) },
|
||||
{ id: 'startLevel', yIdeal: yForLevel(fNum('startLevel'), basinH) },
|
||||
{ id: 'minLevel', yIdeal: yForLevel(fNum('minLevel'), basinH) },
|
||||
{ id: 'dryRunLevel', yIdeal: yForLevel(dryLvl, basinH) },
|
||||
{ id: 'outflowLevel', yIdeal: yForLevel(fNum('outflowLevel'), basinH) },
|
||||
].filter(it => it.yIdeal != null);
|
||||
|
||||
const GAP = 30;
|
||||
items.sort((a, b) => a.yLine - b.yLine);
|
||||
const GAP = 36;
|
||||
items.sort((a, b) => a.yIdeal - b.yIdeal);
|
||||
let prev = -Infinity;
|
||||
for (const it of items) {
|
||||
if (it.pinned) { it.yInput = it.yLine; prev = it.yInput; continue; }
|
||||
it.yInput = Math.max(it.yLine, prev + GAP);
|
||||
prev = it.yInput;
|
||||
if (it.pinned) { it.y = it.yIdeal; prev = it.y; continue; }
|
||||
it.y = Math.max(it.yIdeal, prev + GAP);
|
||||
prev = it.y;
|
||||
}
|
||||
|
||||
// Hide leader lines for items whose input is cleared
|
||||
const active = new Set(items.map(it => it.id));
|
||||
['overflowLevel','maxLevel','startLevel','minLevel','dryRunLevel','outflowLevel'].forEach(id => {
|
||||
if (!active.has(id)) {
|
||||
const lead = document.getElementById(`ps-leader-${id}`);
|
||||
if (lead) lead.setAttribute('visibility', 'hidden');
|
||||
}
|
||||
});
|
||||
for (const it of items) placeItem(it.id, it.yLine, it.yInput);
|
||||
for (const it of items) placeItem(it.id, it.y);
|
||||
|
||||
// Inlet arrow — sole item on the left, no stacking concerns
|
||||
const inflowY = yForLevel(fNum('inflowLevel'), basinH);
|
||||
@@ -275,12 +260,14 @@
|
||||
if (unit) unit.setAttribute('y', inflowY + 4);
|
||||
}
|
||||
|
||||
// Dead-volume band: from outflowLevel down to the floor
|
||||
const outflowY = yForLevel(fNum('outflowLevel'), basinH);
|
||||
const deadvol = document.getElementById('ps-deadvol');
|
||||
if (deadvol && outflowY != null) {
|
||||
deadvol.setAttribute('y', outflowY);
|
||||
deadvol.setAttribute('height', Math.max(0, DIAG.botY - outflowY));
|
||||
// Dead-volume band: from the (possibly-nudged) outflow line
|
||||
// down to the floor. Use the nudged y so the band meets the
|
||||
// outflow line exactly.
|
||||
const outflowItem = items.find(it => it.id === 'outflowLevel');
|
||||
const deadvol = document.getElementById('ps-deadvol');
|
||||
if (deadvol && outflowItem) {
|
||||
deadvol.setAttribute('y', outflowItem.y);
|
||||
deadvol.setAttribute('height', Math.max(0, DIAG.botY - outflowItem.y));
|
||||
}
|
||||
|
||||
// dryRunLevel label text (derived, read-only)
|
||||
@@ -385,7 +372,7 @@
|
||||
#ps-basin-diagram input[type=number]:focus { outline: 1px solid #0c99d9; border-color: #0c99d9; }
|
||||
</style>
|
||||
|
||||
<svg id="ps-basin-diagram" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 620"
|
||||
<svg id="ps-basin-diagram" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 430"
|
||||
style="display:block;width:100%;max-width:540px;margin:0 0 12px 0;background:#fff;border:1px solid #e5e5e5;border-radius:4px;"
|
||||
font-family="Arial,sans-serif" font-size="11">
|
||||
<defs>
|
||||
@@ -395,7 +382,7 @@
|
||||
</defs>
|
||||
|
||||
<!-- Tank body -->
|
||||
<rect x="200" y="40" width="120" height="520" fill="#F0F8FF" stroke="#333" stroke-width="1.5" />
|
||||
<rect x="200" y="40" width="120" height="340" fill="#F0F8FF" stroke="#333" stroke-width="1.5" />
|
||||
<!-- Dead-volume band (y + height updated dynamically below outflowLevel) -->
|
||||
<rect id="ps-deadvol" x="201" width="118" fill="#AACCE0" />
|
||||
|
||||
@@ -462,8 +449,8 @@
|
||||
<text id="ps-unit-outflowLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- Floor / datum -->
|
||||
<line x1="195" y1="560" x2="325" y2="560" stroke="#000" stroke-width="2" />
|
||||
<text x="330" y="564" fill="#000">0 m (datum)</text>
|
||||
<line x1="195" y1="380" x2="325" y2="380" stroke="#000" stroke-width="2" />
|
||||
<text x="330" y="384" fill="#000">0 m (datum)</text>
|
||||
|
||||
<!-- Leader lines: shown when the input row had to be nudged off its threshold's ideal y -->
|
||||
<line id="ps-leader-basinHeight" x1="0" y1="0" x2="0" y2="0" stroke="#bbb" stroke-width="0.6" stroke-dasharray="2 2" visibility="hidden" />
|
||||
@@ -475,7 +462,7 @@
|
||||
<line id="ps-leader-outflowLevel" x1="0" y1="0" x2="0" y2="0" stroke="#bbb" stroke-width="0.6" stroke-dasharray="2 2" visibility="hidden" />
|
||||
|
||||
<!-- Ordering-warning ribbon -->
|
||||
<text id="ps-warning" x="260" y="600" text-anchor="middle" fill="#C0392B" font-size="10" font-style="italic" visibility="hidden"></text>
|
||||
<text id="ps-warning" x="260" y="410" text-anchor="middle" fill="#C0392B" font-size="10" font-style="italic" visibility="hidden"></text>
|
||||
</svg>
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
Reference in New Issue
Block a user