/** * v0.4.2 §14 — syn-stripe-45 fill pattern, two density variants. * * High-density (`syn-stripe-45`) for `tier-synthetic-fill` at z14–z17. * Low-density (`syn-stripe-45-low`) for the briefing-prose synthetic-prior * glyph at 9–18 px (the high-density tile becomes noise that small). * * The SVG sources are the canonical 12×12 tiles from the spec page; * registerSynStripe loads each into MapLibre as a named image so * `paint.fill-pattern: 'syn-stripe-45'` resolves at render time. */ import type { Map as MapLibreMap } from 'maplibre-gl'; export const SYN_STRIPE_SVG = ` `; export const SYN_STRIPE_LOWDENSITY_SVG = ` `; async function svgToImage(src: string, density: number): Promise { const blob = new Blob([src], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); try { const img = await new Promise((resolve, reject) => { const i = new Image(density, density); i.onload = () => resolve(i); i.onerror = (err) => reject(err); i.src = url; }); return img; } finally { URL.revokeObjectURL(url); } } /** * Register both density variants of syn-stripe-45 + a 2x retina copy of * the high-density tile. Call once after `map.on('style.load', ...)`. */ export async function registerSynStripe(map: MapLibreMap): Promise { const variants: Array<[string, string, number]> = [ ['syn-stripe-45', SYN_STRIPE_SVG, 12], ['syn-stripe-45-2x', SYN_STRIPE_SVG, 24], ['syn-stripe-45-low', SYN_STRIPE_LOWDENSITY_SVG, 12] ]; for (const [id, src, density] of variants) { if (map.hasImage(id)) continue; try { const img = await svgToImage(src, density); map.addImage(id, img, { pixelRatio: density / 12 }); } catch (err) { console.warn(`syn-stripe registration failed for ${id}`, err); } } }