-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from LeoDJ/flowBasedConfig
First Milestone: PixelBridge with flow-based config is usable on the MateLight
- Loading branch information
Showing
81 changed files
with
17,231 additions
and
1,161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
|
||
|
||
export type MapType = 'line' | 'snake'; | ||
export type MapOrientation = 'horz' | 'vert'; | ||
export type MapFlip = 'none' | 'even' | 'odd'; | ||
export type MapStart = 'tl' | 'tr' | 'bl' | 'br'; | ||
|
||
export interface MappingParams { | ||
mapType: MapType; | ||
mapOrientation: MapOrientation; | ||
mapFlip?: MapFlip; | ||
mapStart: MapStart; | ||
} | ||
|
||
export interface MappingEntry { | ||
position: number; | ||
flipped: boolean; | ||
} | ||
|
||
export class MappingGenerator { | ||
|
||
constructor(protected mappingParams: MappingParams) { } | ||
|
||
// TODO: implement rotation of frames | ||
async generateMapping(frameWidth: number, frameHeight: number): Promise<MappingEntry[]> { | ||
const snake = this.mappingParams.mapType == 'snake'; | ||
const vertical = this.mappingParams.mapOrientation == 'vert'; | ||
const startingLeft = this.mappingParams.mapStart == 'tl' || this.mappingParams.mapStart == 'bl'; | ||
const startingTop = this.mappingParams.mapStart == 'tl' || this.mappingParams.mapStart == 'tr'; | ||
|
||
// ‧͙⁺˚* Magic, do not touch! *˚⁺‧͙ | ||
|
||
// for vertical orientation, the meaning of X and Y is swapped | ||
const width = vertical ? frameHeight : frameWidth; | ||
const height = vertical ? frameWidth : frameHeight; | ||
|
||
let mapping: MappingEntry[] = []; | ||
for (let i = 0; i < width * height; i++) { | ||
let x: number, y: number; | ||
|
||
// for vertical orientation, the meaning of X and Y is swapped | ||
// booleans related if the | ||
let flipX = vertical ? !startingTop : !startingLeft; | ||
let flipY = vertical ? !startingLeft : !startingTop; | ||
|
||
if (!flipY) | ||
y = Math.floor(i / width); | ||
else | ||
y = height - Math.floor(i / width) - 1; | ||
|
||
// implement snake by flipping x depending on which y we're on | ||
if (snake) { | ||
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) | ||
if (y % 2 == (flipOnEvenLine ? 0 : 1)) { | ||
flipX = !flipX; | ||
} | ||
} | ||
|
||
if (!flipX) | ||
x = Math.floor(i % width); | ||
else | ||
x = width - (i % width) - 1; | ||
|
||
let newPos: number; | ||
// for vertical orientation, the meaning of X and Y is swapped | ||
if (!vertical) | ||
newPos = y * width + x; | ||
else | ||
newPos = x * height + y; | ||
|
||
let flipModule = false; | ||
if (this.mappingParams.mapFlip && this.mappingParams.mapFlip != 'none') { | ||
flipModule = (y % 2 == 0) != (this.mappingParams.mapFlip == 'even'); | ||
} | ||
|
||
mapping[newPos] = { position: i, flipped: flipModule }; | ||
} | ||
return mapping; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { NodeData } from "rete/types/core/data"; | ||
|
||
export interface InstanceState { | ||
instance: any; | ||
params: any; | ||
} | ||
|
||
export class BackendInstanceManager { | ||
instances: { [id: number]: InstanceState } = {}; | ||
|
||
constructor() {} | ||
|
||
async createOrReconfigureInstance(node: NodeData, newParams: any, createNewInstance: () => any) { | ||
// check if instance already exists for given node ID | ||
if (node.id in this.instances) { | ||
const instanceState = this.instances[node.id]; | ||
// check if parameters have changed | ||
if (JSON.stringify(newParams) !== JSON.stringify(instanceState.params)) { | ||
// recreate instance with new parameters | ||
if (instanceState.instance.close) { | ||
await instanceState.instance.close(); | ||
} | ||
delete instanceState.instance; | ||
instanceState.params = newParams; | ||
instanceState.instance = createNewInstance(); | ||
} | ||
} | ||
else { | ||
const newInstanceState: InstanceState = { | ||
params: newParams, | ||
instance: createNewInstance() | ||
} | ||
this.instances[node.id] = newInstanceState; | ||
} | ||
} | ||
|
||
getInstance (node: NodeData) { | ||
return this.instances[node.id]; | ||
} | ||
|
||
async handleRemovedNodes(removedNodeIds: string[]) { | ||
removedNodeIds.forEach(async (nodeId) => { | ||
if (nodeId in this.instances) { | ||
// call closing function if it exists | ||
if (this.instances[nodeId].instance?.close) { | ||
await this.instances[nodeId].instance.close(); | ||
} | ||
delete this.instances[nodeId]; | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
interface Frame { | ||
export interface Frame { | ||
width: number; | ||
height: number; | ||
buffer: Buffer; // color order: RGB | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Frame } from "./frame.interface"; | ||
|
||
export interface FrameArr { | ||
width: number; // width in amount of frames | ||
height: number; // height in amount of frames | ||
frames: Frame[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface Resolution { | ||
x: number; | ||
y: number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as Rete from "rete"; | ||
import { WorkerInputs } from "rete/types/core/data"; | ||
|
||
export interface ReteTask { | ||
inputs: WorkerInputs; | ||
component: Rete.Component; | ||
worker: unknown; | ||
next: Array<{ | ||
key: string; | ||
task: ReteTask; | ||
}>; | ||
outputData: any; | ||
closed: string[]; | ||
|
||
getInputs(type: string): string[]; | ||
reset(): void; | ||
run(data: any, needReset?: boolean, garbage?: any[], propagate?: boolean): any; // returns outputData from called worker | ||
clone(root: boolean, oldTask: ReteTask, newTask: ReteTask): ReteTask; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as Rete from "rete"; | ||
|
||
import { NodeData, WorkerInputs } from "rete/types/core/data"; | ||
import { ArtnetSource } from "../../sources/ArtnetSource"; | ||
import { BackendInstanceManager, InstanceState } from "../backendInstanceManager"; | ||
import { Frame } from "../frame.interface"; | ||
import { Resolution } from "../resolution.interface"; | ||
import { ReteTask } from "../reteTask.interface"; | ||
|
||
interface ArtnetInputParams { | ||
port: number; | ||
startUniverse: number; | ||
resolution: Resolution; | ||
} | ||
|
||
interface ArtnetInputState extends InstanceState { | ||
instance: ArtnetSource; | ||
params: ArtnetInputParams; | ||
} | ||
|
||
export class ArtnetInputComponentWorker extends Rete.Component { | ||
constructor(protected instMgr: BackendInstanceManager) { | ||
super("ArtNet Input"); | ||
} | ||
|
||
tasks: {[id: number]: ReteTask} = {}; | ||
|
||
[x: string]: any; // make Typescript happy (allow arbitrary member variables, as there is no definition file for Rete Tasks) | ||
task = { | ||
outputs: {frame: 'option'}, | ||
init: (task: ReteTask, node: NodeData) => { // gets called on engine.process | ||
this.tasks[node.id] = task; | ||
task.run(null); // init node instance with parameters from inputs (has to be done via the worker) | ||
} | ||
} | ||
|
||
async builder(node: Rete.Node) { | ||
// see node builder definition in webinterface/frontend/src/node-editor/components | ||
} | ||
|
||
// TODO: remove created artnet source when node gets deleted | ||
initBackend = async (node: NodeData, inputs: WorkerInputs) => { | ||
const nodeParams: ArtnetInputParams = { | ||
port: (inputs['port']?.length ? inputs['port'][0] : node.data.port) as number, | ||
startUniverse: (inputs['universe']?.length ? inputs['universe'][0] : node.data.startUniverse) as number, | ||
resolution: (inputs['outRes']?.length ? inputs['outRes'][0] : node.data.resolution) as Resolution | ||
}; | ||
|
||
|
||
// check for undefined parameters | ||
if (nodeParams.port == undefined || nodeParams.startUniverse == undefined || nodeParams.resolution?.x == undefined || nodeParams.resolution?.y == undefined) { | ||
return; | ||
} | ||
|
||
const task = this.tasks[node.id]; | ||
|
||
this.instMgr.createOrReconfigureInstance(node, nodeParams, () => | ||
new ArtnetSource( | ||
nodeParams.resolution.x, | ||
nodeParams.resolution.y, | ||
(f: Frame) => { this.tasks[node.id].run({frame: f}); }, | ||
nodeParams.port, | ||
nodeParams.startUniverse) | ||
); | ||
|
||
// createOrReconfigureInstance(node, this.artnetSources, nodeParams, () => | ||
// new ArtnetSource( | ||
// nodeParams.resolution.x, | ||
// nodeParams.resolution.y, | ||
// (f: Frame) => { this.tasks[node.id].run({frame: f}); }, | ||
// nodeParams.port, | ||
// nodeParams.startUniverse) | ||
// ); | ||
} | ||
|
||
async worker(node: NodeData, inputs: WorkerInputs, data: any) { | ||
if (data === null) { | ||
this.closed = ['frame']; // stop propagating event | ||
this.component.initBackend(node, inputs); // worker is run outside of current class context, so we need to acess initBackend via .component | ||
} | ||
else { | ||
this.closed = []; // enable propagating event again | ||
data.fromId = node.id; | ||
// don't have to do more, frame data is already contained in data (called from ArtnetSource frame callback) | ||
} | ||
} | ||
} |
Oops, something went wrong.