/** * Color represented by RGB (0.0-1.0) components */ export type Color = { r: number; g: number; b: number; }; /** * Displayable character, with background and foreground color taken from the palette */ export type Char = Readonly<{ ch: string; bg: number; fg: number; }>; export type BufferSize = Readonly<{ w: number; h: number }>; export type BufferLocation = Readonly<{ x: number; y: number }>; export const SPACE: Char = { ch: " ", bg: 0, fg: 0 } as const; /** * Rectangular buffer of displayable characters */ export class CharBuffer { private chars: Array; constructor(private size: BufferSize) { this.chars = new Array(size.w * size.h).fill(SPACE); } /** * Get the character buffered at a given at * * This does not properly check for out-of-bounds coordinates, * use BufferDrawing for this */ get(at: BufferLocation): Char { const i = at.y * this.size.w + at.x; if (i > 0 && i < this.chars.length) { return this.chars[i]; } else { return SPACE; } } /** * Change the character buffered at a given location * * This does not properly check for out-of-bounds coordinates, * use BufferDrawing for this */ set(at: BufferLocation, char: Char): void { const i = at.y * this.size.w + at.x; if (i >= 0 && i < this.chars.length) { this.chars[i] = char; } } getSize(): BufferSize { return this.size; } toString(): string { return this.chars.map((c) => c.ch).join(""); } } /** * Tools for drawing inside a display buffer */ export class BufferDrawing { constructor(private readonly buffer: CharBuffer) { } /** * Draw a piece of text of the same color */ text(content: string, from: BufferLocation): void { let { w, h } = this.buffer.getSize(); let { x, y } = from; let buf = this.buffer; if (y >= 0 && y < h) { for (let ch of content) { if (x >= 0 && x < w) { buf.set({ x, y }, { ch, bg: 0, fg: 0 }); } x++; } } } }