Spur label: clamp text right edge to trapezoid boundary

Measure actual text width and position so the right side of the text
aligns with the angled edge of the spur, preventing overflow onto
adjacent conveyors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
igurielidze 2026-03-30 21:42:36 +04:00
parent 31aea34361
commit 07ace0d3f4
2 changed files with 14 additions and 14 deletions

View File

@ -556,21 +556,19 @@ function drawConveyanceLabel(ctx: CanvasRenderingContext2D, sym: PlacedSymbol) {
let cx: number, cy: number, availW: number, availH: number;
if (isSpurType(sym.symbolId)) {
const w2 = sym.w2 ?? sym.w;
// Find optimal position: push text down where trapezoid is widest,
// then center horizontally in the available width at that height.
// Trapezoid right edge at height y: w2 + (y/h) * (w - w2)
const fontSize = 14;
const lineCount = lines.length;
const th = fontSize * lineCount;
// Optimal cy: as low as possible (widest area), clamped to fit
// Compute text dimensions at 14px to find optimal placement
ctx.font = 'bold 14px Arial';
const maxTextW = Math.max(...lines.map(l => ctx.measureText(l).width));
const th = 14 * lines.length;
// Push text down where trapezoid is wider, but leave room
const optCy = Math.min(sym.h - th / 2 - 1, sym.h * 0.55);
// Right edge at the TOP of the text block (most constraining)
// Right edge at top of text — text must not exceed this
const textTop = optCy - th / 2;
const rightEdgeAtTop = w2 + (Math.max(0, textTop) / sym.h) * (sym.w - w2);
// Center text in the available width (0 to rightEdgeAtTop)
cx = sym.x + rightEdgeAtTop / 2;
const rightEdge = w2 + (Math.max(0, textTop) / sym.h) * (sym.w - w2);
// Place text so right side of text = rightEdge (hard clamp)
cx = sym.x + Math.min(rightEdge / 2, rightEdge - maxTextW / 2);
cy = sym.y + optCy;
availW = Infinity; // never shrink — position handles fit
availW = Infinity;
availH = sym.h;
} else {
cx = sym.x + sym.w / 2;

View File

@ -48,8 +48,10 @@ function emitConveyanceLabelInner(lines: string[], sym: PlacedSymbol) {
const th = fontSize * textLines.length;
const optCy = Math.min(sym.h - th / 2 - 1, sym.h * 0.55);
const textTop = optCy - th / 2;
const rightEdgeAtTop = w2 + (Math.max(0, textTop) / sym.h) * (sym.w - w2);
labelCx = sym.x + rightEdgeAtTop / 2;
const rightEdge = w2 + (Math.max(0, textTop) / sym.h) * (sym.w - w2);
// Estimate text width (~8px per char at 14px bold)
const estTextW = Math.max(...textLines.map(l => l.length * 8));
labelCx = sym.x + Math.min(rightEdge / 2, rightEdge - estTextW / 2);
labelCy = sym.y + optCy;
availH = sym.h;
} else if (isInductionType(sym.symbolId)) {