Skip to content

Commit

Permalink
Merge pull request #587 from tuanchauict/board
Browse files Browse the repository at this point in the history
[WIP] Convert Board to TS
  • Loading branch information
tuanchauict authored Feb 25, 2024
2 parents 49e384b + 4a8115f commit 8533a7e
Show file tree
Hide file tree
Showing 14 changed files with 969 additions and 31 deletions.
2 changes: 1 addition & 1 deletion monosketch-svelte/src/lib/libs/graphics-geo/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Point implements IPoint {
public readonly top: number,
) {
if (!(Number.isInteger(left) && Number.isInteger(top))) {
throw Error('location must be integer');
throw Error(`location must be integer ${left} ${top}`);
}
}

Expand Down
33 changes: 33 additions & 0 deletions monosketch-svelte/src/lib/libs/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,36 @@ export function getOrNull<T>(array: T[], index: number): T | null {
export function getOrDefault<T>(array: T[], index: number, defaultValue: T): T {
return index >= 0 && index < array.length ? array[index] : defaultValue;
}

export namespace ListExt {
/**
* Create a list of the specified size with the specified value.
* @param size
* @param value
*/
export function list<T>(size: number, value: () => T): T[] {
const result: T[] = [];
for (let i = 0; i < size; i++) {
result.push(value());
}
return result;
}
}

export namespace MapExt {
/**
* Get the value from the map with the specified key. If the key is not found, put the value
* created by the specified function into the map and return it.
* @param map
* @param key
* @param value
*/
export function getOrPut<K, V>(map: Map<K, V>, key: K, value: () => V): V {
let result = map.get(key);
if (result === undefined) {
result = value();
map.set(key, result);
}
return result;
}
}
24 changes: 24 additions & 0 deletions monosketch-svelte/src/lib/libs/string.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, test } from 'vitest';
import { StringExt } from '$libs/string';

describe('StringExt', () => {
test('trimMargin', () => {
const input = `
| 1 |
| 2 |
| 3 |
`;
const expected = ` 1 \n 2 \n 3 `;
expect(StringExt.trimMargin(input)).toStrictEqual(expected);
});

test('trimMargin with custom prefix', () => {
const input = `
# 1 |
# 2 |
# 3 |
`;
const expected = ` 1 \n 2 \n 3 `;
expect(StringExt.trimMargin(input, '#')).toStrictEqual(expected);
});
});
27 changes: 27 additions & 0 deletions monosketch-svelte/src/lib/libs/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export namespace StringExt {
/**
* Trims the margin of a string.
*
* The margin is defined by the first line that starts with a margin prefix.
* @param input
* @param marginPrefix The prefix of the margin. Default is `|`.
* @param trimEnd The end of the line. Default is true.
*/
export const trimMargin = (
input: string,
marginPrefix: string = '|',
trimEnd: boolean = true,
): string => {
const lines = input.split('\n');

const trimmedLines = lines.map((line) => {
const index = line.indexOf(marginPrefix);
if (index === -1) {
return null;
}
const endExcluded = trimEnd ? line.length - 1 : line.length;
return line.slice(index + marginPrefix.length, endExcluded);
});
return trimmedLines.filter((line) => line !== null).join('\n');
};
}
45 changes: 24 additions & 21 deletions monosketch-svelte/src/lib/mono/monobitmap/bitmap/monobitmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { binarySearch, getOrNull, mapIndexedNotNull, zip } from '$libs/sequence'
import { isHalfTransparentChar, isTransparentChar, TRANSPARENT_CHAR } from '$mono/common/character';
import { Rect } from '$libs/graphics-geo/rect';

namespace MonoBitmap {
export class MonoBitmap {
export namespace MonoBitmap {
/**
* A model class to hold the look of a shape after drawing.
* Create new object via [Builder].
*/
export class Bitmap {
readonly size: Size;

constructor(public matrix: Row[]) {
Expand Down Expand Up @@ -71,7 +75,7 @@ namespace MonoBitmap {
}
}

fillBitmap(row: number, column: number, bitmap: MonoBitmap): void {
fillBitmap(row: number, column: number, bitmap: Bitmap): void {
if (bitmap.isEmpty()) {
return;
}
Expand All @@ -93,28 +97,27 @@ namespace MonoBitmap {
const destVisual = this.visualMatrix[startRow + r];
const destDirection = this.directionMatrix[startRow + r];

const updateCell = (index: number, visualChar: Char, directionChar: Char) => {
for (let cell of src.asSequence(inStartCol, inStartCol + overlap.width)) {
const index = cell.index - inStartCol;
const destIndex = startCol + index;
// visualChar from source is always not transparent (0) due to the optimization of Row
if (isApplicable(destVisual[destIndex], visualChar)) {
destVisual[startCol + index] = visualChar;
if (isApplicable(destVisual[destIndex], cell.visual)) {
destVisual[startCol + index] = cell.visual;
}

// TODO: Double check this condition
if (isApplicable(destDirection[destIndex], directionChar)) {
destDirection[startCol + index] = directionChar;
if (isApplicable(destDirection[destIndex], cell.direction)) {
destDirection[startCol + index] = cell.direction;
}
};

src.forEachIndex(updateCell, inStartCol, inStartCol + overlap.width);
}
}
}

toBitmap(): MonoBitmap {
toBitmap(): Bitmap {
const rows = this.visualMatrix.map(
(chars, index) => new Row(chars, this.directionMatrix[index]),
);
return new MonoBitmap(rows);
return new Bitmap(rows);
}
}

Expand Down Expand Up @@ -154,19 +157,21 @@ namespace MonoBitmap {
);
}

forEachIndex(
callback: ForEachIndex,
fromIndex: number = 0,
toExclusiveIndex: number = this.size,
) {
*asSequence(
fromIndexOptional?: number,
toExclusiveIndexOptional?: number,
): Generator<Cell> {
const fromIndex = fromIndexOptional === undefined ? 0 : fromIndexOptional;
const toExclusiveIndex = toExclusiveIndexOptional === undefined ? this.size : toExclusiveIndexOptional;
const foundLow = binarySearch(this.sortedCells, (cell) => cell.index - fromIndex);
const low = foundLow >= 0 ? foundLow : -foundLow - 1;

for (let i = low; i < this.sortedCells.length; i++) {
const cell = this.sortedCells[i];
if (cell.index >= toExclusiveIndex) {
break;
}
callback(cell.index, cell.visual, cell.direction);
yield cell;
}
}

Expand All @@ -193,6 +198,4 @@ namespace MonoBitmap {
public direction: Char,
) {}
}

type ForEachIndex = (index: number, visual: Char, direction: Char) => void;
}
143 changes: 143 additions & 0 deletions monosketch-svelte/src/lib/mono/monobitmap/board/board.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { beforeEach, describe, expect, test } from 'vitest';
import { MonoBoard } from '$mono/monobitmap/board/board';
import { HighlightType, Pixel } from '$mono/monobitmap/board/pixel';
import { Point } from '$libs/graphics-geo/point';
import { Rect } from '$libs/graphics-geo/rect';
import { StringExt } from '$libs/string';
import trimMargin = StringExt.trimMargin;

describe('MonoBoard', () => {
let target: MonoBoard;

beforeEach(() => {
target = new MonoBoard();
target.clearAndSetWindow(Rect.byLTWH(-100, -100, 200, 200));
});

test('getSet', () => {
const points = [-48, -32, -18, -16, 0, 16, 18, 32, 48].map(
(value) => new Point(value, value),
);

points.forEach((point) => {
expect(target.get(point.left, point.top)).toBe(Pixel.TRANSPARENT);
});

const chars = '012345678';
chars.split('').forEach((char, index) => {
target.setPoint(points[index], char, HighlightType.NO);
});
chars.split('').forEach((char, index) => {
expect(target.getPoint(points[index]).visualChar).toBe(char);
});

expect(target.boardCount).toBe(7);
});

test('fillRect', () => {
target.fillRect(Rect.byLTWH(1, 1, 3, 3), 'A', HighlightType.NO);

expect(target.toString()).toStrictEqual(
trimMargin(`
| |
| AAA |
| AAA |
| AAA |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
`),
);

expect(target.boardCount).toBe(1);

target.fillRect(Rect.byLTWH(-3, -3, 3, 3), 'B', HighlightType.NO);
expect(target.toString()).toStrictEqual(
trimMargin(`
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| BBB |
| BBB |
| BBB |
| |
| AAA |
| AAA |
| AAA |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
`),
);

expect(target.boardCount).toBe(2);

target.fillRect(Rect.byLTWH(-1, 0, 3, 1), 'C', HighlightType.NO);
expect(target.toString()).toStrictEqual(
trimMargin(`
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| BBB |
| BBB |
| BBB |
| CCC |
| AAA |
| AAA |
| AAA |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
`),
);

expect(target.boardCount).toBe(3);
});
});
Loading

0 comments on commit 8533a7e

Please sign in to comment.