import { Modules, Excavation, ImageSettings, } from '@digigov/ui/app/QrCodeViewer/types'; const DEFAULT_IMG_SCALE = 0.1; export const MARGIN_SIZE = 4; export function generatePath(modules: Modules, margin = 0): string { const ops: string[] = []; modules.forEach(function (row, y) { let start: number | null = null; row.forEach(function (cell, x) { if (!cell && start !== null) { // M0 0h7v1H0z injects the space with the move and drops the comma, // saving a char per operation ops.push( `M${start + margin} ${y + margin}h${x - start}v1H${start + margin}z` ); start = null; return; } // end of row, clean up or skip if (x === row.length - 1) { if (!cell) { // We would have closed the op above already so this can only mean // 2+ light modules in a row. return; } if (start === null) { // Just a single dark module. ops.push(`M${x + margin},${y + margin} h1v1H${x + margin}z`); } else { // Otherwise finish the current line. ops.push( `M${start + margin},${y + margin} h${x + 1 - start}v1H${ start + margin }z` ); } return; } if (cell && start === null) { start = x; } }); }); return ops.join(''); } // We could just do this in generatePath, except that we want to support // non-Path2D canvas, so we need to keep it an explicit step. export function excavateModules( modules: Modules, excavation: Excavation ): Modules { return modules.slice().map((row, y) => { if (y < excavation.y || y >= excavation.y + excavation.h) { return row; } return row.map((cell, x) => { if (x < excavation.x || x >= excavation.x + excavation.w) { return cell; } return false; }); }); } export function getImageSettings( cells: Modules, size: number, includeMargin: boolean, imageSettings?: ImageSettings ): null | { x: number; y: number; h: number; w: number; excavation: Excavation | null; } { if (imageSettings == null) { return null; } const margin = includeMargin ? MARGIN_SIZE : 0; const numCells = cells.length + margin * 2; const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE); const scale = numCells / size; const w = (imageSettings.width || defaultSize) * scale; const h = (imageSettings.height || defaultSize) * scale; const x = imageSettings.x == null ? cells.length / 2 - w / 2 : imageSettings.x * scale; const y = imageSettings.y == null ? cells.length / 2 - h / 2 : imageSettings.y * scale; let excavation; if (imageSettings.excavate) { const floorX = Math.floor(x); const floorY = Math.floor(y); const ceilW = Math.ceil(w + x - floorX); const ceilH = Math.ceil(h + y - floorY); excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH }; } return { x, y, h, w, excavation }; }