Skip to content

Commit

Permalink
Merge pull request #2 from Vestaboard/feature/absolute-position
Browse files Browse the repository at this point in the history
absolute position
  • Loading branch information
jottenlips authored Feb 20, 2024
2 parents 7e3d89b + 12172b2 commit 621af56
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 11 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vestaboard/vbml",
"version": "1.0.13",
"version": "1.1.0",
"description": "The Vestaboard markup language",
"main": "lib/index.js",
"scripts": {
Expand Down
39 changes: 38 additions & 1 deletion src/__tests__/parseComponent.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parseComponent } from "../parseComponent";
import { parseAbsoluteComponent, parseComponent } from "../parseComponent";
import { Align, IVBMLComponent, Justify } from "../types";

describe("Parse Component", () => {
Expand Down Expand Up @@ -432,4 +432,41 @@ describe("Parse Component", () => {
[3, 4],
]);
});
it("Should parse absolute component", () => {
const input: IVBMLComponent = {
template: "Hello World!",
style: {
absolutePosition: {
x: 4,
y: 2,
},
width: 6,
height: 2,
},
};

const result = parseAbsoluteComponent(3, 12)(input);
expect(result).toEqual({
characters: [
[8, 5, 12, 12, 15, 0],
[23, 15, 18, 12, 4, 37],
],
x: 4,
y: 2,
});
});

it("Should parse a raw component", () => {
const input: IVBMLComponent = {
rawCharacters: [
[1, 2],
[3, 4],
],
};
const result = parseComponent(3, 12)(input);
expect(result).toEqual([
[1, 2],
[3, 4],
]);
});
});
208 changes: 207 additions & 1 deletion src/__tests__/vbml.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Align, vbml } from "..";
import { Align, Justify, vbml } from "..";

describe("VBML", () => {
it("Should parse a single component on a board", () => {
Expand Down Expand Up @@ -152,4 +152,210 @@ describe("VBML", () => {

expect(result).toEqual([[0], [1], [2], [3], [0]]);
});

it("Should layout absolute components by relative components", () => {
const result = vbml.parse({
style: {
height: 22,
width: 6,
},
components: [
{
template: "abc",
style: {
height: 6,
width: 22,
align: Align.top,
justify: Justify.left,
},
},
{
template: "def",
style: {
height: 1,
width: 3,
align: Align.top,
justify: Justify.left,
absolutePosition: {
x: 3,
y: 0,
},
},
},
],
});
expect(result[0]).toEqual([
1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
});

it("Should layout absolute components over relative components", () => {
const result = vbml.parse({
style: {
height: 22,
width: 6,
},
components: [
{
template: "abc",
style: {
height: 6,
width: 22,
align: Align.top,
justify: Justify.left,
},
},
{
template: "def",
style: {
height: 1,
width: 3,
align: Align.top,
justify: Justify.left,
absolutePosition: {
x: 0,
y: 0,
},
},
},
],
});
expect(result[0]).toEqual([
4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
});

it("Should layout absolute components over relative components", () => {
const result = vbml.parse({
style: {
height: 6,
width: 22,
},
components: [
{
template: "abc",
style: {
height: 6,
width: 22,
align: Align.top,
justify: Justify.left,
},
},
{
template: "def",
style: {
height: 1,
width: 3,
align: Align.top,
justify: Justify.left,
absolutePosition: {
x: 0,
y: 0,
},
},
},
],
});
expect(result[0]).toEqual([
4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
});

it("Should layout raw components", () => {
const result = vbml.parse({
style: {
height: 6,
width: 22,
},
components: [
{
rawCharacters: [[1, 2, 3]],
},
],
});
expect(result[0]).toEqual([
1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
});

it("Should layout absolute components with raw components for a mountain background clock", () => {
const result = vbml.parse({
props: {
time: "12:00 PM",
},
style: {
height: 6,
width: 22,
},
components: [
{
rawCharacters: [
[
68, 68, 68, 68, 68, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68,
68, 68, 68, 68, 68, 68,
],
[
68, 68, 68, 68, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68,
65, 65, 65, 65, 68, 68,
],
[
63, 63, 63, 69, 66, 69, 66, 69, 69, 63, 63, 63, 63, 63, 63, 65,
65, 65, 65, 65, 65, 63,
],
[
63, 63, 66, 66, 66, 69, 66, 66, 66, 66, 63, 63, 63, 63, 63, 65,
65, 65, 65, 65, 65, 63,
],
[
64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 64, 64, 64, 64,
65, 65, 65, 65, 64, 64,
],
[
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64,
],
],
},
{
template: "{{time}}",
style: {
height: 1,
width: 8,
absolutePosition: {
x: 11,
y: 3,
},
},
},
],
});
// visual representation of the result
// https://web.vestaboard.com/board/cda490a3-aaa5-4f2d-9018-a41cd365328a/compose/duplicate/[[68,68,68,68,68,69,69,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68],[68,68,68,68,69,69,69,69,68,68,68,68,68,68,68,68,65,65,65,65,68,68],[63,63,63,69,66,69,66,69,69,63,63,63,63,63,63,65,65,65,65,65,65,63],[63,63,66,66,66,69,66,66,66,66,63,27,28,50,36,36,0,16,13,65,65,63],[64,66,66,66,66,66,66,66,66,66,66,64,64,64,64,64,65,65,65,65,64,64],[66,66,66,66,66,66,66,66,66,66,66,66,64,64,64,64,64,64,64,64,64,64]]
expect(result).toEqual([
[
68, 68, 68, 68, 68, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
68, 68, 68, 68,
],
[
68, 68, 68, 68, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 65, 65,
65, 65, 68, 68,
],
[
63, 63, 63, 69, 66, 69, 66, 69, 69, 63, 63, 63, 63, 63, 63, 65, 65, 65,
65, 65, 65, 63,
],
[
63, 63, 66, 66, 66, 69, 66, 66, 66, 66, 63, 27, 28, 50, 36, 36, 0, 16,
13, 65, 65, 63,
],
[
64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 64, 64, 64, 64, 65, 65,
65, 65, 64, 64,
],
[
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64,
],
]);
});
});
14 changes: 9 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "./characterCodes";
import { createEmptyBoard } from "./createEmptyBoard";
import { layoutComponents } from "./layoutComponents";
import { parseComponent } from "./parseComponent";
import { parseAbsoluteComponent, parseComponent } from "./parseComponent";
import { IVBML } from "./types";
import { characterCodesToString } from "./characterCodesToString";
import { characterCodesToAscii } from "./characterCodesToAscii";
Expand All @@ -17,11 +17,15 @@ export const vbml = {
const width = input?.style?.width || BOARD_COLUMNS;
const emptyBoard = createEmptyBoard(height, width);

const components = input.components.map(
parseComponent(height, width, input.props)
);
const components = input.components
.filter((component) => !component.style?.absolutePosition)
.map(parseComponent(height, width, input.props));

return layoutComponents(emptyBoard, components);
const absoluteComponents = input.components
.filter((component) => !!component.style?.absolutePosition)
.map(parseAbsoluteComponent(height, width, input.props));

return layoutComponents(emptyBoard, components, absoluteComponents);
},
characterCodesToString,
characterCodesToAscii,
Expand Down
19 changes: 18 additions & 1 deletion src/layoutComponents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const layoutComponents = (
board: number[][],
components: number[][][]
components: number[][][],
absoluteComponents: { characters: number[][]; x: number; y: number }[]
) => {
let position = {
top: 0,
Expand Down Expand Up @@ -29,5 +30,21 @@ export const layoutComponents = (
};
});

absoluteComponents &&
absoluteComponents.forEach((component) => {
component.characters.forEach((row, rowIndex) => {
row.forEach((bit, bitIndex) => {
// make sure we are in bounds, truncate the rest for now
if (component.y + rowIndex >= board.length) {
return;
}
if (component.x + bitIndex >= board[0].length) {
return;
}
board[rowIndex + component.y][bitIndex + component.x] = bit;
});
});
});

return board;
};
18 changes: 17 additions & 1 deletion src/parseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import map from "lodash/fp/map";
export const parseComponent =
(defaultHeight: number, defaultWidth: number, props?: VBMLProps) =>
(component: IVBMLComponent) => {
if ("rawCharacters" in component) return component.rawCharacters;

const width = component?.style?.width || defaultWidth;
const height = component?.style?.height || defaultHeight;
const emptyComponent = createEmptyBoard(height, width);
Expand All @@ -24,5 +26,19 @@ export const parseComponent =
verticalAlign(height, component?.style?.align || Align.top),
horizontalAlign(width, component?.style?.justify || Justify.left),
renderComponent(emptyComponent)
)(component.template) as number[][];
)("template" in component ? component.template : "") as number[][];
};

export const parseAbsoluteComponent =
(defaultHeight: number, defaultWidth: number, props?: VBMLProps) =>
(component: IVBMLComponent) => {
return {
characters: parseComponent(
defaultHeight,
defaultWidth,
props
)(component) as number[][],
x: component.style?.absolutePosition?.x || 0,
y: component.style?.absolutePosition?.y || 0,
};
};
Loading

0 comments on commit 621af56

Please sign in to comment.