BCNLAT: marca de tope separada del cuerpo por hueco visible
Rediseño del símbolo de baliza lateral para reflejar la estructura real IALA: palo continuo delgado + cuerpo rectangular (parte baja) + tramo de palo libre visible (hueco) + marca de tope separada arriba. Babor (IALA-B verde): marca de tope = cuadrado. Estribor (IALA-B rojo): marca de tope = triángulo apuntando arriba. La marca de tope es levemente más ancha que el cuerpo. El hueco entre cuerpo y marca queda ocupado solo por el poste delgado. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+65
-42
@@ -1146,12 +1146,12 @@ function _encLightCanvas(colours, region, sz = 32) {
|
||||
return c;
|
||||
}
|
||||
|
||||
// ── Lateral beacon (BCNLAT) — rectángulo delgado alto + tope cuadrado/triangular ──
|
||||
// Símbolo: cuerpo principal es un rectángulo delgado y alto en el color IALA lateral.
|
||||
// Babor (port, IALA-B verde): tope plano → rectángulo puro.
|
||||
// Estribor (stbd, IALA-B rojo): tope triangular apuntando hacia arriba.
|
||||
// Claramente distinguible de boyas flotantes. Funciona igual para faros de orilla
|
||||
// colombianos y daymarks del ICW de Florida.
|
||||
// ── Lateral beacon (BCNLAT) — poste + cuerpo + HUECO + marca de tope ─────────
|
||||
// Estructura real IALA: palo delgado continuo + cuerpo rectangular en la parte
|
||||
// inferior + tramo de palo libre (hueco visible) + marca de tope separada arriba.
|
||||
// Babor (port, IALA-B verde): marca de tope = cuadrado.
|
||||
// Estribor (stbd, IALA-B rojo): marca de tope = triángulo apuntando arriba.
|
||||
// La marca de tope es levemente más ancha que el cuerpo y queda separada de él.
|
||||
function _encBeaconCanvas(colours, catlam, region, sz = 36) {
|
||||
const c = _mkC(sz); const ctx = c.getContext('2d'); const cx = sz / 2;
|
||||
let cc = (colours || []).slice();
|
||||
@@ -1159,62 +1159,85 @@ function _encBeaconCanvas(colours, catlam, region, sz = 36) {
|
||||
const col = cc[0] ? _s57css(cc[0]) : '#78909c';
|
||||
const isPort = catlam === 1 || catlam === 3; // IALA-B: babor=verde, estribor=rojo
|
||||
|
||||
// ── Dimensiones del cuerpo ────────────────────────────────────────────────
|
||||
const bW = sz * 0.30; // ancho del cuerpo (delgado)
|
||||
const bTop = sz * 0.07; // tope del cuerpo
|
||||
const bBot = sz * 0.90; // base (nivel del suelo)
|
||||
const triH = bW * 1.05; // altura del triángulo = ≈ ancho (proporcional)
|
||||
const bodyTop = isPort ? bTop : bTop + triH; // tope del rectángulo (debajo del tri)
|
||||
// ── Layout: poste continuo + cuerpo (bajo) + hueco + marca de tope (arriba) ─
|
||||
const groundY = sz * 0.92; // base / nivel del suelo
|
||||
const poleTopY = sz * 0.04; // cima del poste
|
||||
const poleW = sz * 0.055; // grosor del poste delgado
|
||||
|
||||
// ── Sombra en la base ─────────────────────────────────────────────────────
|
||||
// Cuerpo (shaft): rectángulo en la mitad inferior del poste
|
||||
const bodyW = sz * 0.28;
|
||||
const bodyTopY = sz * 0.48; // tope del cuerpo → aquí empieza el hueco visible
|
||||
const bodyBotY = groundY;
|
||||
|
||||
// Marca de tope: separada del cuerpo, levemente más ancha
|
||||
const tmW = sz * 0.36; // ancho de la marca de tope
|
||||
const tmBotY = sz * 0.27; // base de la marca (límite superior del hueco)
|
||||
const tmTopY = sz * 0.05; // cima de la marca (= ≈ tope del poste)
|
||||
const tmH = tmBotY - tmTopY;
|
||||
|
||||
// ── Sombra elíptica en la base ────────────────────────────────────────────
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, bBot, sz * 0.12, sz * 0.022, 0, 0, Math.PI * 2);
|
||||
ctx.ellipse(cx, groundY, sz * 0.12, sz * 0.020, 0, 0, Math.PI * 2);
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.22)';
|
||||
ctx.fill();
|
||||
|
||||
// ── Gradiente lateral (efecto 3-D suave) ─────────────────────────────────
|
||||
const grd = ctx.createLinearGradient(cx - bW / 2, 0, cx + bW / 2, 0);
|
||||
grd.addColorStop(0, _lighten3D(col, 0.35));
|
||||
grd.addColorStop(0.45, col);
|
||||
grd.addColorStop(1, _darken3D(col, 0.28));
|
||||
// ── Poste continuo (todo el alto, dibujado primero = detrás) ─────────────
|
||||
const poleGrd = ctx.createLinearGradient(cx - poleW, 0, cx + poleW, 0);
|
||||
poleGrd.addColorStop(0, '#999');
|
||||
poleGrd.addColorStop(0.4, '#ddd');
|
||||
poleGrd.addColorStop(1, '#777');
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - poleW / 2, poleTopY, poleW, groundY - poleTopY);
|
||||
ctx.fillStyle = poleGrd;
|
||||
ctx.fill();
|
||||
|
||||
// ── Gradiente de color IALA (compartido por cuerpo y marca de tope) ───────
|
||||
const mkGrd = (w) => {
|
||||
const g = ctx.createLinearGradient(cx - w / 2, 0, cx + w / 2, 0);
|
||||
g.addColorStop(0, _lighten3D(col, 0.35));
|
||||
g.addColorStop(0.45, col);
|
||||
g.addColorStop(1, _darken3D(col, 0.28));
|
||||
return g;
|
||||
};
|
||||
|
||||
ctx.strokeStyle = 'rgba(0,0,0,0.65)';
|
||||
ctx.lineWidth = 0.9;
|
||||
|
||||
// ── Cuerpo (shaft rectangulo, parte inferior) ─────────────────────────────
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bodyW / 2, bodyTopY, bodyW, bodyBotY - bodyTopY);
|
||||
ctx.fillStyle = mkGrd(bodyW);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Highlight lateral izquierdo
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bodyW / 2, bodyTopY, bodyW * 0.20, bodyBotY - bodyTopY);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.15)';
|
||||
ctx.fill();
|
||||
|
||||
// ── Marca de tope (separada del cuerpo por el hueco = tramo de poste libre) ─
|
||||
if (isPort) {
|
||||
// ── Babor: rectángulo plano (tope cuadrado) ───────────────────────────
|
||||
// Babor: cuadrado (square daymark)
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop, bW, bBot - bTop);
|
||||
ctx.fillStyle = grd;
|
||||
ctx.rect(cx - tmW / 2, tmTopY, tmW, tmH);
|
||||
ctx.fillStyle = mkGrd(tmW);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Highlight lateral izquierdo
|
||||
// Highlight lateral
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop, bW * 0.22, bBot - bTop);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.16)';
|
||||
ctx.rect(cx - tmW / 2, tmTopY, tmW * 0.20, tmH);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.15)';
|
||||
ctx.fill();
|
||||
} else {
|
||||
// ── Estribor: triángulo (tope apuntado) + rectángulo (cuerpo) ──────────
|
||||
// Triángulo apuntando hacia arriba
|
||||
// Estribor: triángulo apuntando hacia arriba (triangle daymark)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, bTop); // vértice superior
|
||||
ctx.lineTo(cx + bW / 2, bTop + triH); // esquina derecha de la base del tri
|
||||
ctx.lineTo(cx - bW / 2, bTop + triH); // esquina izquierda de la base del tri
|
||||
ctx.moveTo(cx, tmTopY); // vértice superior (apex)
|
||||
ctx.lineTo(cx + tmW / 2, tmBotY); // esquina inferior derecha
|
||||
ctx.lineTo(cx - tmW / 2, tmBotY); // esquina inferior izquierda
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fillStyle = mkGrd(tmW);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Cuerpo rectangular debajo del triángulo
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop + triH, bW, bBot - (bTop + triH));
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Highlight lateral izquierdo
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop + triH, bW * 0.22, bBot - (bTop + triH));
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.16)';
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
Reference in New Issue
Block a user