Skip to content

Commit 61029b6

Browse files
authored
Merge pull request #3 from LeoDJ/flowBasedConfig
First Milestone: PixelBridge with flow-based config is usable on the MateLight
2 parents 87c388c + f68b50a commit 61029b6

File tree

81 files changed

+17231
-1161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+17231
-1161
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@ It's targeted to do the image data conversion for the [modLED project](https://g
66

77

88
## How to run
9-
Run with `npm start` (don't forgetto run `npm i` the first time).
9+
Run with `npm start` (don't forget to run `npm i` and `npm run build` the first time).
10+
11+
## Development
12+
Develop with `npm run dev`. Then access http://localhost:8081 for the development frontend. (Port 8080 will still host the outdated release frontend)
13+
14+
**Disclaimer**: This is my first big Typescript project. So there are bound to be horrific architectural decisions and many other beginner's mistakes. Please excuse that.
15+
The premise is to get to the first milestone with as little premature optimization as possible, otherwise I would never arrive there.

package-lock.json

Lines changed: 14666 additions & 954 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,58 @@
44
"description": "",
55
"main": "main.ts",
66
"dependencies": {
7-
"@types/sharp": "^0.26.1",
7+
"core-js": "^3.6.5",
8+
"cors": "^2.8.5",
89
"express": "^4.17.1",
9-
"sharp": "^0.26.3"
10+
"rete": "^1.4.5",
11+
"rete-area-plugin": "^0.2.1",
12+
"rete-code-plugin": "^0.2.0",
13+
"rete-comment-plugin": "^0.7.0-rc.1",
14+
"rete-connection-plugin": "^0.9.0",
15+
"rete-context-menu-plugin": "^0.6.0",
16+
"rete-history-plugin": "^0.2.2",
17+
"rete-keyboard-plugin": "^0.1.2",
18+
"rete-minimap-plugin": "^0.3.1",
19+
"rete-task-plugin": "^0.3.0",
20+
"rete-vue-render-plugin": "^0.5.0",
21+
"serialport": "^9.2.8",
22+
"sharp": "^0.23.4",
23+
"vue": "^2.6.11",
24+
"vue-class-component": "^7.2.3",
25+
"vue-property-decorator": "^9.1.2"
1026
},
1127
"devDependencies": {
28+
"@types/cors": "^2.8.12",
1229
"@types/express": "^4.17.9",
1330
"@types/node": "^14.14.10",
31+
"@types/serialport": "^8.0.2",
32+
"@types/sharp": "^0.26.1",
33+
"@typescript-eslint/eslint-plugin": "^4.18.0",
34+
"@typescript-eslint/parser": "^4.18.0",
35+
"@vue/cli-plugin-babel": "~4.5.0",
36+
"@vue/cli-plugin-eslint": "~4.5.0",
37+
"@vue/cli-plugin-typescript": "~4.5.0",
38+
"@vue/cli-service": "~4.5.0",
39+
"@vue/eslint-config-typescript": "^7.0.0",
40+
"concurrently": "^6.5.1",
1441
"copyfiles": "^2.4.1",
15-
"typescript": "^4.1.2"
42+
"eslint": "^6.7.2",
43+
"eslint-plugin-vue": "^6.2.2",
44+
"nodemon": "^2.0.15",
45+
"typescript": "^4.1.2",
46+
"vue-template-compiler": "^2.6.11"
1647
},
1748
"scripts": {
49+
"start": "node dist/main.js",
50+
"dev": "concurrently --kill-others \"npm run devBackend\" \"npm run devWebsite\"",
1851
"test": "echo \"Error: no test specified\" && exit 1",
19-
"copyFiles": "copyfiles -u 3 src/webinterface/static/* dist/webinterface/static/",
20-
"build": "tsc && npm run copyFiles",
21-
"start": "npm run build && node dist/main.js",
22-
"devWebsite": "nodemon -e html,css,js,png,jpg --watch src --exec npm run copyFiles",
23-
"dev": "nodemon --watch src -e ts --exec npm start"
52+
"copyFiles": "copyfiles -u 3 src/webinterface/frontend/dist/** dist/webinterface/frontend",
53+
"build": "tsc && npm run buildWebsite",
54+
"buildBackend": "tsc",
55+
"buildWebsite": "vue-cli-service build",
56+
"startBackend": "npm run buildBackend && node dist/main.js --inspect",
57+
"devBackend": "nodemon --watch src --ignore src/webinterface/frontend -e ts --exec npm run startBackend",
58+
"devWebsite": "vue-cli-service serve"
2459
},
2560
"author": "",
2661
"license": "MIT"

src/common/MappingGenerator.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
3+
export type MapType = 'line' | 'snake';
4+
export type MapOrientation = 'horz' | 'vert';
5+
export type MapFlip = 'none' | 'even' | 'odd';
6+
export type MapStart = 'tl' | 'tr' | 'bl' | 'br';
7+
8+
export interface MappingParams {
9+
mapType: MapType;
10+
mapOrientation: MapOrientation;
11+
mapFlip?: MapFlip;
12+
mapStart: MapStart;
13+
}
14+
15+
export interface MappingEntry {
16+
position: number;
17+
flipped: boolean;
18+
}
19+
20+
export class MappingGenerator {
21+
22+
constructor(protected mappingParams: MappingParams) { }
23+
24+
// TODO: implement rotation of frames
25+
async generateMapping(frameWidth: number, frameHeight: number): Promise<MappingEntry[]> {
26+
const snake = this.mappingParams.mapType == 'snake';
27+
const vertical = this.mappingParams.mapOrientation == 'vert';
28+
const startingLeft = this.mappingParams.mapStart == 'tl' || this.mappingParams.mapStart == 'bl';
29+
const startingTop = this.mappingParams.mapStart == 'tl' || this.mappingParams.mapStart == 'tr';
30+
31+
// ‧͙⁺˚* Magic, do not touch! *˚⁺‧͙
32+
33+
// for vertical orientation, the meaning of X and Y is swapped
34+
const width = vertical ? frameHeight : frameWidth;
35+
const height = vertical ? frameWidth : frameHeight;
36+
37+
let mapping: MappingEntry[] = [];
38+
for (let i = 0; i < width * height; i++) {
39+
let x: number, y: number;
40+
41+
// for vertical orientation, the meaning of X and Y is swapped
42+
// booleans related if the
43+
let flipX = vertical ? !startingTop : !startingLeft;
44+
let flipY = vertical ? !startingLeft : !startingTop;
45+
46+
if (!flipY)
47+
y = Math.floor(i / width);
48+
else
49+
y = height - Math.floor(i / width) - 1;
50+
51+
// implement snake by flipping x depending on which y we're on
52+
if (snake) {
53+
const flipOnEvenLine = flipY && (height % 2 == 0); // normally flip on odd lines, except when starting from bottom and height is even (flipY is said start flag, horz/vert corrected)
54+
if (y % 2 == (flipOnEvenLine ? 0 : 1)) {
55+
flipX = !flipX;
56+
}
57+
}
58+
59+
if (!flipX)
60+
x = Math.floor(i % width);
61+
else
62+
x = width - (i % width) - 1;
63+
64+
let newPos: number;
65+
// for vertical orientation, the meaning of X and Y is swapped
66+
if (!vertical)
67+
newPos = y * width + x;
68+
else
69+
newPos = x * height + y;
70+
71+
let flipModule = false;
72+
if (this.mappingParams.mapFlip && this.mappingParams.mapFlip != 'none') {
73+
flipModule = (y % 2 == 0) != (this.mappingParams.mapFlip == 'even');
74+
}
75+
76+
mapping[newPos] = { position: i, flipped: flipModule };
77+
}
78+
return mapping;
79+
}
80+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { NodeData } from "rete/types/core/data";
2+
3+
export interface InstanceState {
4+
instance: any;
5+
params: any;
6+
}
7+
8+
export class BackendInstanceManager {
9+
instances: { [id: number]: InstanceState } = {};
10+
11+
constructor() {}
12+
13+
async createOrReconfigureInstance(node: NodeData, newParams: any, createNewInstance: () => any) {
14+
// check if instance already exists for given node ID
15+
if (node.id in this.instances) {
16+
const instanceState = this.instances[node.id];
17+
// check if parameters have changed
18+
if (JSON.stringify(newParams) !== JSON.stringify(instanceState.params)) {
19+
// recreate instance with new parameters
20+
if (instanceState.instance.close) {
21+
await instanceState.instance.close();
22+
}
23+
delete instanceState.instance;
24+
instanceState.params = newParams;
25+
instanceState.instance = createNewInstance();
26+
}
27+
}
28+
else {
29+
const newInstanceState: InstanceState = {
30+
params: newParams,
31+
instance: createNewInstance()
32+
}
33+
this.instances[node.id] = newInstanceState;
34+
}
35+
}
36+
37+
getInstance (node: NodeData) {
38+
return this.instances[node.id];
39+
}
40+
41+
async handleRemovedNodes(removedNodeIds: string[]) {
42+
removedNodeIds.forEach(async (nodeId) => {
43+
if (nodeId in this.instances) {
44+
// call closing function if it exists
45+
if (this.instances[nodeId].instance?.close) {
46+
await this.instances[nodeId].instance.close();
47+
}
48+
delete this.instances[nodeId];
49+
}
50+
});
51+
}
52+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
interface Frame {
1+
export interface Frame {
22
width: number;
33
height: number;
44
buffer: Buffer; // color order: RGB

src/common/frameArr.interface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Frame } from "./frame.interface";
2+
3+
export interface FrameArr {
4+
width: number; // width in amount of frames
5+
height: number; // height in amount of frames
6+
frames: Frame[];
7+
}

src/common/resolution.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface Resolution {
2+
x: number;
3+
y: number;
4+
}

src/common/reteTask.interface.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as Rete from "rete";
2+
import { WorkerInputs } from "rete/types/core/data";
3+
4+
export interface ReteTask {
5+
inputs: WorkerInputs;
6+
component: Rete.Component;
7+
worker: unknown;
8+
next: Array<{
9+
key: string;
10+
task: ReteTask;
11+
}>;
12+
outputData: any;
13+
closed: string[];
14+
15+
getInputs(type: string): string[];
16+
reset(): void;
17+
run(data: any, needReset?: boolean, garbage?: any[], propagate?: boolean): any; // returns outputData from called worker
18+
clone(root: boolean, oldTask: ReteTask, newTask: ReteTask): ReteTask;
19+
20+
}

src/common/util.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+

0 commit comments

Comments
 (0)