Fix catenary layer toggles, tooltip collision, side rail rendering, and typed interruption signs
Deploy & Release / build-and-deploy (push) Failing after 14s

This commit is contained in:
m4xx1m committed 2026-10-21 05:12:42 +03:00
1 parent 68d5058106
commit 0301fde1a9
5 files changed
+202 -91

No files matched your search

+99 -37
View File
@@ -639,8 +639,8 @@ function drawCatenaryLayer(ctx, trackWidth) {
const pixelRatio = MapData.view.pixelRatio;
const scale = MapData.view.scale;
// 1. Contact Wires
if(MapData.layers.catenaryWires && MapData.catenaryPolylines){
// 1. Contact Wires & Neutral Lines (decoupled toggles)
if(MapData.catenaryPolylines && (MapData.layers.catenaryWires || MapData.layers.catenaryNeutrals)){
let minX = MapData.view.unconvertX(0) - Config.viewCullMargin / scale;
let maxX = MapData.view.unconvertX(mapCanvas.width) + Config.viewCullMargin / scale;
let minZ = MapData.view.unconvertY(mapCanvas.height) - Config.viewCullMargin / scale;
@@ -650,6 +650,12 @@ function drawCatenaryLayer(ctx, trackWidth) {
ctx.lineJoin = 'round';
for(const poly of MapData.catenaryPolylines){
if(poly.is_neutral){
if(!MapData.layers.catenaryNeutrals) continue;
} else {
if(!MapData.layers.catenaryWires) continue;
}
let b = poly.bounds;
if(b[2] < minX || b[0] > maxX || b[3] < minZ || b[1] > maxZ) continue;
@@ -663,7 +669,7 @@ function drawCatenaryLayer(ctx, trackWidth) {
}
if(poly.is_neutral){
let w = Math.max(3.5 * pixelRatio, trackWidth * 0.7);
let w = Math.max(3.8 * pixelRatio, trackWidth * 0.75);
ctx.lineWidth = w;
ctx.setLineDash([]);
ctx.strokeStyle = '#ffffff';
@@ -674,20 +680,23 @@ function drawCatenaryLayer(ctx, trackWidth) {
ctx.stroke();
ctx.setLineDash([]);
} else if(poly.category === 'side_rail'){
ctx.lineWidth = Math.max(2.5 * pixelRatio, trackWidth * 0.45);
ctx.setLineDash([8 * pixelRatio, 4 * pixelRatio]);
ctx.lineWidth = Math.max(3.0 * pixelRatio, trackWidth * 0.55);
ctx.setLineDash([10 * pixelRatio, 5 * pixelRatio]);
ctx.strokeStyle = '#111111';
ctx.stroke();
ctx.lineWidth = Math.max(1.8 * pixelRatio, trackWidth * 0.35);
ctx.strokeStyle = poly.color;
ctx.stroke();
ctx.setLineDash([]);
} else if(poly.category === 'quad'){
ctx.lineWidth = Math.max(4 * pixelRatio, trackWidth * 0.85);
ctx.lineWidth = Math.max(4.2 * pixelRatio, trackWidth * 0.85);
ctx.strokeStyle = '#111111';
ctx.stroke();
ctx.lineWidth = Math.max(2.2 * pixelRatio, trackWidth * 0.55);
ctx.lineWidth = Math.max(2.4 * pixelRatio, trackWidth * 0.55);
ctx.strokeStyle = poly.color;
ctx.stroke();
} else if(poly.category === 'dual'){
ctx.lineWidth = Math.max(3.2 * pixelRatio, trackWidth * 0.7);
ctx.lineWidth = Math.max(3.4 * pixelRatio, trackWidth * 0.7);
ctx.strokeStyle = poly.color;
ctx.stroke();
} else if(poly.category === 'trolley'){
@@ -702,64 +711,117 @@ function drawCatenaryLayer(ctx, trackWidth) {
}
}
// 2. Neutral Section Markers
// 2. Neutral Section Markers & Detail Signs
if(MapData.layers.catenaryNeutrals && MapData.catenaryNeutrals){
for(const n of MapData.catenaryNeutrals){
let nx = MapData.view.convertX(n.center.x);
let ny = MapData.view.convertY(n.center.z);
if(nx < -50 || nx > mapCanvas.width + 50 || ny < -50 || ny > mapCanvas.height + 50) continue;
if(nx >= -50 && nx <= mapCanvas.width + 50 && ny >= -50 && ny <= mapCanvas.height + 50){
let badgeSize = 13 * pixelRatio;
ctx.beginPath();
ctx.arc(nx, ny, badgeSize, 0, Math.PI * 2);
ctx.fillStyle = '#e63946';
ctx.fill();
ctx.lineWidth = 2 * pixelRatio;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
let badgeSize = 13 * pixelRatio;
ctx.beginPath();
ctx.arc(nx, ny, badgeSize, 0, Math.PI * 2);
ctx.fillStyle = '#e63946';
ctx.fill();
ctx.lineWidth = 2 * pixelRatio;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.fillStyle = '#ffffff';
ctx.font = `bold ${10 * pixelRatio}px "Noto Sans"`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('0V', nx, ny);
ctx.fillStyle = '#ffffff';
ctx.font = `bold ${10 * pixelRatio}px "Noto Sans"`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('0V', nx, ny);
if(scale >= 0.035){
ctx.font = `bold ${11 * pixelRatio}px "Noto Sans"`;
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2.5 * pixelRatio;
ctx.strokeText(`⛔ ${n.id}`, nx, ny + 18 * pixelRatio);
ctx.fillStyle = '#ffcccc';
ctx.fillText(`⛔ ${n.id}`, nx, ny + 18 * pixelRatio);
}
}
if(scale >= 0.04){
ctx.font = `bold ${11 * pixelRatio}px "Noto Sans"`;
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2.5 * pixelRatio;
ctx.strokeText(`⛔ ${n.id}`, nx, ny + 18 * pixelRatio);
ctx.fillStyle = '#ffcccc';
ctx.fillText(`⛔ ${n.id}`, nx, ny + 18 * pixelRatio);
// High zoom boundary signs (NeutralWarning, NeutralBegin, NeutralEnd)
if(scale >= 0.04 && n.signs){
for(const sign of n.signs){
let sx = MapData.view.convertX(sign.x);
let sy = MapData.view.convertY(sign.z);
if(sx < -20 || sx > mapCanvas.width + 20 || sy < -20 || sy > mapCanvas.height + 20) continue;
let isWarn = sign.type.includes('Warning');
let isBegin = sign.type.includes('Begin');
let signColor = isWarn ? '#f59e0b' : (isBegin ? '#e63946' : '#10b981');
let signSymbol = isWarn ? '⚠' : (isBegin ? '⛔' : '✓');
let r = 8 * pixelRatio;
ctx.beginPath();
ctx.arc(sx, sy, r, 0, Math.PI * 2);
ctx.fillStyle = signColor;
ctx.fill();
ctx.lineWidth = 1.2 * pixelRatio;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.fillStyle = isWarn ? '#000000' : '#ffffff';
ctx.font = `bold ${8 * pixelRatio}px "Noto Sans"`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(signSymbol, sx, sy);
if(scale >= 0.08){
ctx.font = `${9 * pixelRatio}px "Noto Sans"`;
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2 * pixelRatio;
ctx.strokeText(sign.type, sx, sy - 12 * pixelRatio);
ctx.fillStyle = '#ffffff';
ctx.fillText(sign.type, sx, sy - 12 * pixelRatio);
}
}
}
}
}
// 3. Interruption Markers
if(MapData.layers.catenaryInterruptions && MapData.catenaryInterruptions){
if(MapData.layers.catenaryInterruptions && MapData.catenaryInterruptions && scale >= 0.015){
for(const inter of MapData.catenaryInterruptions){
let ix = MapData.view.convertX(inter.center.x);
let iy = MapData.view.convertY(inter.center.z);
if(ix < -50 || ix > mapCanvas.width + 50 || iy < -50 || iy > mapCanvas.height + 50) continue;
let s = 10 * pixelRatio;
let kind = inter.kind || 'drop';
let fillColor = kind === 'warning' ? '#f59e0b' : (kind === 'raise' ? '#10b981' : '#f97316');
let textColor = kind === 'warning' ? '#000000' : '#ffffff';
let icon = kind === 'warning' ? '⚠' : (kind === 'raise' ? '⤒' : '⤓');
let s = Math.max(7 * pixelRatio, Math.min(11 * pixelRatio, 220 * scale * pixelRatio));
ctx.beginPath();
ctx.moveTo(ix, iy - s);
ctx.lineTo(ix + s, iy);
ctx.lineTo(ix, iy + s);
ctx.lineTo(ix - s, iy);
ctx.closePath();
ctx.fillStyle = '#ff9f1c';
ctx.fillStyle = fillColor;
ctx.fill();
ctx.lineWidth = 1.5 * pixelRatio;
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.fillStyle = '#000000';
ctx.font = `bold ${11 * pixelRatio}px "Noto Sans"`;
ctx.fillStyle = textColor;
ctx.font = `bold ${Math.round(s * 1.05)}px "Noto Sans"`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('⤓', ix, iy);
ctx.fillText(icon, ix, iy);
if(scale >= 0.045){
let desc = kind === 'warning' ? '⚠ Разрыв КС' : (kind === 'raise' ? '⤒ Подъем пантографа' : '⤓ Опустить пантограф');
ctx.font = `bold ${10 * pixelRatio}px "Noto Sans"`;
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2.5 * pixelRatio;
ctx.strokeText(desc, ix, iy + s + 7 * pixelRatio);
ctx.fillStyle = fillColor;
ctx.fillText(desc, ix, iy + s + 7 * pixelRatio);
}
}
}
@@ -792,7 +854,7 @@ function drawCatenaryLayer(ctx, trackWidth) {
ctx.textBaseline = 'middle';
ctx.fillText('⚡', sx, sy - 1 * pixelRatio);
if(scale >= 0.03){
if(scale >= 0.025){
let labelText = `${sub.id} (${sub.nominal_voltage}V)`;
ctx.font = `bold ${11 * pixelRatio}px "Noto Sans"`;
ctx.textAlign = 'center';