Fix SVG export labels: absolute coords, no transforms (Ignition compat)
Ignition strips SVG transforms from text elements, causing labels to float to wrong positions. Now all text uses pre-computed absolute x/y coordinates without any transform attributes. Also fixes positioning for curved (at arc midpoint), spur (trapezoid center), and induction (strip center) symbols. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a38ecaa27
commit
bf0eced44c
@ -17,55 +17,47 @@ function parseConveyanceLabel(label: string): { lines: string[]; stripped: strin
|
|||||||
return { lines: [core], stripped: [core] };
|
return { lines: [core], stripped: [core] };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Emit conveyance label text element inside the shape */
|
/** Emit conveyance label text — absolute coordinates, no transforms (Ignition compatible) */
|
||||||
function emitConveyanceLabel(lines: string[], sym: PlacedSymbol, outerTransform: string) {
|
function emitConveyanceLabel(lines: string[], sym: PlacedSymbol, _outerTransform: string) {
|
||||||
if (!sym.label) return;
|
if (!sym.label) return;
|
||||||
const { lines: textLines, stripped } = parseConveyanceLabel(sym.label);
|
const { lines: textLines } = parseConveyanceLabel(sym.label);
|
||||||
const tAttr = outerTransform ? ` transform="${outerTransform}"` : '';
|
|
||||||
|
// Compute label center position based on shape type
|
||||||
|
let labelCx: number, labelCy: number, availH: number;
|
||||||
|
|
||||||
// Curved symbols: place along arc midpoint
|
|
||||||
if (isCurvedType(sym.symbolId)) {
|
if (isCurvedType(sym.symbolId)) {
|
||||||
const angle = sym.curveAngle || 90;
|
const angle = sym.curveAngle || 90;
|
||||||
const { arcCx, arcCy, outerR, innerR, bandW } = getCurveGeometry(sym.symbolId, sym.x, sym.y, sym.w, sym.h);
|
const { arcCx, arcCy, outerR, innerR, bandW } = getCurveGeometry(sym.symbolId, sym.x, sym.y, sym.w, sym.h);
|
||||||
const midR = (outerR + innerR) / 2;
|
const midR = (outerR + innerR) / 2;
|
||||||
const midAngleRad = ((angle / 2) * Math.PI) / 180;
|
const midAngleRad = ((angle / 2) * Math.PI) / 180;
|
||||||
const textX = arcCx + midR * Math.cos(midAngleRad);
|
labelCx = arcCx + midR * Math.cos(midAngleRad);
|
||||||
const textY = arcCy - midR * Math.sin(midAngleRad);
|
labelCy = arcCy - midR * Math.sin(midAngleRad);
|
||||||
const rotDeg = -angle / 2 + 90;
|
availH = bandW - 4;
|
||||||
const fontSize = Math.min(14, (bandW - 4) / textLines.length);
|
} else if (isSpurType(sym.symbolId)) {
|
||||||
if (fontSize < 4) return;
|
|
||||||
const lineH = fontSize;
|
|
||||||
const curveTransform = `translate(${textX},${textY}) rotate(${rotDeg})${outerTransform ? ' ' + outerTransform : ''}`;
|
|
||||||
for (let i = 0; i < textLines.length; i++) {
|
|
||||||
const dy = -(textLines.length - 1) * lineH / 2 + i * lineH;
|
|
||||||
lines.push(` <text x="0" y="0" dy="${dy + fontSize * 0.35}" text-anchor="middle" font-family="Arial" font-weight="bold" font-size="${fontSize}px" fill="#000000" transform="${curveTransform}">${textLines[i]}</text>`);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spur: center in trapezoid
|
|
||||||
let labelCx: number, labelCy: number, availW: number, availH: number;
|
|
||||||
if (isSpurType(sym.symbolId)) {
|
|
||||||
const w2 = sym.w2 ?? sym.w;
|
const w2 = sym.w2 ?? sym.w;
|
||||||
const midW = (w2 + sym.w) / 2;
|
labelCx = sym.x + (w2 + sym.w) / 4;
|
||||||
labelCx = sym.x + midW / 2;
|
|
||||||
labelCy = sym.y + sym.h / 2;
|
labelCy = sym.y + sym.h / 2;
|
||||||
availW = midW - 4;
|
|
||||||
availH = sym.h - 4;
|
availH = sym.h - 4;
|
||||||
|
} else if (isInductionType(sym.symbolId)) {
|
||||||
|
const stripTopY = sym.y + sym.h * INDUCTION_CONFIG.stripTopFrac;
|
||||||
|
const stripBottomY = sym.y + sym.h * INDUCTION_CONFIG.stripBottomFrac;
|
||||||
|
labelCx = sym.x + (INDUCTION_CONFIG.headWidth + sym.w) / 2;
|
||||||
|
labelCy = (stripTopY + stripBottomY) / 2;
|
||||||
|
availH = stripBottomY - stripTopY - 4;
|
||||||
} else {
|
} else {
|
||||||
labelCx = sym.x + sym.w / 2;
|
labelCx = sym.x + sym.w / 2;
|
||||||
labelCy = sym.y + sym.h / 2;
|
labelCy = sym.y + sym.h / 2;
|
||||||
availW = sym.w - 4;
|
|
||||||
availH = sym.h - 4;
|
availH = sym.h - 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useLines = textLines;
|
const fontSize = Math.min(14, availH / textLines.length);
|
||||||
const fontSize = Math.min(14, availH / useLines.length);
|
|
||||||
if (fontSize < 4) return;
|
if (fontSize < 4) return;
|
||||||
const lineH = fontSize;
|
const lineH = fontSize;
|
||||||
for (let i = 0; i < useLines.length; i++) {
|
// Emit each line at absolute position — y is baseline, offset by 0.35*fontSize to center visually
|
||||||
const dy = -(useLines.length - 1) * lineH / 2 + i * lineH;
|
for (let i = 0; i < textLines.length; i++) {
|
||||||
lines.push(` <text x="${labelCx}" y="${labelCy + dy + fontSize * 0.35}" text-anchor="middle" font-family="Arial" font-weight="bold" font-size="${fontSize}px" fill="#000000"${tAttr}>${useLines[i]}</text>`);
|
const dy = -(textLines.length - 1) * lineH / 2 + i * lineH;
|
||||||
|
const y = labelCy + dy + fontSize * 0.35;
|
||||||
|
lines.push(` <text x="${labelCx}" y="${y}" text-anchor="middle" font-family="Arial" font-weight="bold" font-size="${fontSize}px" fill="#000000">${textLines[i]}</text>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user