wesleytabaka-rpi-led-matrix
Version:
Fork of alexeden/rpi-led-matrix. Node.js/Typescript bindings for hzeller/rpi-rgb-led-matrix
123 lines (101 loc) • 3.04 kB
text/typescript
import { FontInstance } from './types';
export interface Glyph {
h: number;
w: number;
char: string;
}
export interface MappedGlyph extends Glyph {
x: number;
y: number;
}
export type Word = Glyph[];
export type Line = Word[];
const isSeparator = ({ char }: Glyph) => char === ' ';
const glphysToWords = (glphys: Glyph[]): Word[] => {
const index = glphys
.map((g, i) => i === 0 && isSeparator(g) ? null : g.char)
.indexOf(' ');
return index > 0
? [glphys.slice(0, index), ...glphysToWords(glphys.slice(index))]
: [glphys];
};
const calcWordWidth = (gs: Glyph[]) => gs.reduce((sum, { w }) => sum + w, 0);
const wordsToLines = (maxWidth: number, words: Word[]): Line[] => {
const lines: Line[] = [];
let tmpLine: Line = [];
let tmpLineWidth = 0;
words.filter(({ length }) => length > 0).forEach(word => {
const wordWidth = calcWordWidth(word);
if (tmpLineWidth + wordWidth > maxWidth) {
lines.push(tmpLine);
const firstWord = word.filter(g => !isSeparator(g));
tmpLine = [ firstWord ];
tmpLineWidth = calcWordWidth(firstWord);
}
else {
tmpLine.push(word);
tmpLineWidth += wordWidth;
}
});
if (tmpLine.length > 0) lines.push(tmpLine);
return lines;
};
export enum HorizontalAlignment {
Left = 'left',
Center = 'center',
Right = 'right',
}
export enum VerticalAlignment {
Bottom = 'bottom',
Middle = 'middle',
Top = 'top',
}
export class LayoutUtils {
static textToLines(font: FontInstance, maxW: number, text: string): Line[] {
const fontHeight = font.height();
const glphys = text.split('').map(char => ({
char,
h: fontHeight,
w: font.stringWidth(char),
}));
return wordsToLines(maxW, glphysToWords(glphys));
}
static linesToMappedGlyphs(
lines: Line[],
lineH: number,
containerW: number,
containerH: number,
alignH = HorizontalAlignment.Center,
alignV = VerticalAlignment.Middle
): MappedGlyph[] {
const blockH = lineH * lines.length;
const offsetY = (() => {
switch (alignV) {
case VerticalAlignment.Top: return 0;
case VerticalAlignment.Middle: return Math.floor((containerH - blockH) / 2);
case VerticalAlignment.Bottom: return containerH - blockH;
}
})();
return lines.map((words, i) => {
const lineGlyphs = words.reduce((glyphs, word) => [...glyphs, ...word], []);
const lineW = calcWordWidth(lineGlyphs);
let offsetX = (() => {
switch (alignH) {
case HorizontalAlignment.Left: return 0;
case HorizontalAlignment.Center: return Math.floor((containerW - lineW) / 2);
case HorizontalAlignment.Right: return containerW - lineW;
}
})();
return lineGlyphs.map(glyph => {
const mapped = {
...glyph,
x: offsetX,
y: offsetY + i * lineH,
};
offsetX += glyph.w;
return mapped;
});
})
.reduce((glyphs, words) => [...glyphs, ...words], []);
}
}