Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lights)!: remove individual fixture requirements and constraints #38

Merged
merged 5 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions src/modules/lights/color-definitions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ColorChannel } from './entities/colors';
import { ColorChannel, IColorsRgb } from './entities/colors-rgb';

export enum WheelColor {
WHITE = 'white',
Expand Down Expand Up @@ -35,12 +35,8 @@ export const wheelColors = Object.values(WheelColor);

export const rgbColors = Object.values(RgbColor);

// TODO: implement wheel colors
export type RgbColorDefinition = { [k in ColorChannel]: number };
export type WheelColorDefinition = RgbColorDefinition;

export type RgbColorSpecification = {
definition: RgbColorDefinition;
definition: Required<IColorsRgb>;
alternative: WheelColor;
complementary: RgbColor[];
hex: string;
Expand Down
4 changes: 2 additions & 2 deletions src/modules/lights/effects/color/beat-fade-out.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ export default class BeatFadeOut extends LightsEffect<BeatFadeOutProps> {

const color = this.getCurrentColor(p, i);
if (color == null) {
p.fixture.setMasterDimmer(0);
p.fixture.resetColor();
} else {
p.fixture.setMasterDimmer(Math.round(255 * beatProgression));
p.fixture.setBrightness(beatProgression);
p.fixture.setColor(color);
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/modules/lights/effects/color/fire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ export default class Fire extends LightsEffect<FireProps> {
destroy(): void {}

tick(): LightsGroup {
this.lightsGroup.pars.forEach((p) => {
p.fixture.setCurrentValues({
masterDimChannel: 128,
[...this.lightsGroup.pars, ...this.lightsGroup.movingHeadRgbs].forEach((p) => {
p.fixture.setBrightness(0.5);
p.fixture.setCustomColor({
redChannel: 255,
greenChannel: 32 + Math.round(Math.random() * 64),
blueChannel: 0,
amberChannel: 128,
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/modules/lights/effects/color/single-flood.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class SingleFlood extends LightsEffect<SingleFloodProps> {

this.lightsGroup.pars.forEach((p) => {
p.fixture.setColor(this.props.color ?? RgbColor.ORANGE);
p.fixture.setMasterDimmer(255 * progression);
p.fixture.setBrightness(progression);
});

this.lightsGroup.movingHeadWheels.forEach((m) => m.fixture.blackout());
Expand Down
4 changes: 2 additions & 2 deletions src/modules/lights/effects/color/sparkle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ export default class Sparkle extends LightsEffect<SparkleProps> {
const colorIndex = this.colorIndices[index];
const color = colors[colorIndex % colors.length];
p.fixture.setColor(color);
p.fixture.setMasterDimmer(Math.round(255 * progression));
p.fixture.setBrightness(progression);
});
this.lightsGroup.movingHeadRgbs.forEach((p, i) => {
const index = i;
const progression = this.getDimProgression(this.beats[nrPars + index]);
const colorIndex = this.colorIndices[nrPars + index];
const color = colors[colorIndex % colors.length];
p.fixture.setColor(color);
p.fixture.setMasterDimmer(Math.round(255 * progression));
p.fixture.setBrightness(progression);
});

return this.lightsGroup;
Expand Down
8 changes: 3 additions & 5 deletions src/modules/lights/effects/color/static-color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class StaticColor extends LightsEffect<StaticColorProps> {
this.lightsGroup.fixtures.forEach((f) => {
f.fixture.setColor(this.props.color);
if (!this.props.beatToggle) {
f.fixture.setMasterDimmer(Math.round((this.props.relativeBrightness ?? 1) * 255));
f.fixture.setBrightness(this.props.relativeBrightness ?? 1);
}
});
this.lightsGroup.movingHeadWheels.forEach((f) => {
Expand Down Expand Up @@ -99,11 +99,9 @@ export default class StaticColor extends LightsEffect<StaticColorProps> {
.forEach((f, i) => {
// If beatToggle is disabled, or if it is enabled and the fixture should be turned on
if (!this.props.beatToggle || i % 2 === this.ping) {
f.fixture.setMasterDimmer(
Math.round(progression * (this.props.relativeBrightness ?? 1) * 255),
);
f.fixture.setBrightness(progression * (this.props.relativeBrightness ?? 1));
} else {
f.fixture.setMasterDimmer(0);
f.fixture.setBrightness(0);
}
});

Expand Down
2 changes: 1 addition & 1 deletion src/modules/lights/effects/color/wave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class Wave extends LightsEffect<WaveProps> {
const apply = (p: LightsGroupPars | LightsGroupMovingHeadRgbs) => {
const progression = this.getProgression(currentTick, p);
const brightness = this.getBrightness(progression);
p.fixture.setMasterDimmer(Math.max(0, brightness * 255));
p.fixture.setBrightness(Math.max(0, brightness));
p.fixture.setColor(this.props.colors[0]);
};

Expand Down
10 changes: 2 additions & 8 deletions src/modules/lights/effects/movement/random-position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,12 @@ export default class RandomPosition extends LightsEffect<RandomPositionProps> {
this.lightsGroup.movingHeadRgbs.forEach((m, i) => {
if (this.counters[i] > 0) return;

m.fixture.setCurrentValues({
panChannel: Math.round(Math.random() * (255 / 3)),
tiltChannel: Math.round(Math.random() * 255),
});
m.fixture.setPositionRel(Math.random() / 3, Math.random());
});
this.lightsGroup.movingHeadWheels.forEach((m, i) => {
if (this.counters[nrMHRgbs + i] > 0) return;

m.fixture.setCurrentValues({
panChannel: Math.round(Math.random() * (255 / 3)),
tiltChannel: Math.round(Math.random() * 255),
});
m.fixture.setPositionRel(Math.random() / 3, Math.random());
});

this.counters = this.counters.map(
Expand Down
146 changes: 146 additions & 0 deletions src/modules/lights/entities/colors-rgb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { Column } from 'typeorm';
import { RgbColor, rgbColorDefinitions } from '../color-definitions';
import LightsFixtureShutterOptions, { ShutterOption } from './lights-fixture-shutter-options';
import Colors from './colors';
import { IColorsWheel } from './colors-wheel';

export type ColorChannel = keyof ColorsRgb;

export interface IColorsRgb {
redChannel: number;
greenChannel: number;
blueChannel: number;
coldWhiteChannel?: number | null;
warmWhiteChannel?: number | null;
amberChannel?: number | null;
uvChannel?: number | null;
}

export default class ColorsRgb extends Colors implements IColorsRgb {
@Column({ type: 'tinyint', unsigned: true, nullable: true })
public masterDimChannel?: number;

@Column({ type: 'tinyint', unsigned: true, nullable: true })
public shutterChannel?: number;

@Column({ type: 'tinyint', unsigned: true })
public redChannel: number;

@Column({ type: 'tinyint', unsigned: true })
public greenChannel: number;

@Column({ type: 'tinyint', unsigned: true })
public blueChannel: number;

@Column({ type: 'tinyint', nullable: true, unsigned: true })
public coldWhiteChannel?: number | null;

@Column({ type: 'tinyint', nullable: true, unsigned: true })
public warmWhiteChannel?: number | null;

@Column({ type: 'tinyint', nullable: true, unsigned: true })
public amberChannel?: number | null;

@Column({ type: 'tinyint', nullable: true, unsigned: true })
public uvChannel?: number | null;

private strobePing = false;

private currentValues: Required<IColorsRgb> = {
redChannel: 0,
greenChannel: 0,
blueChannel: 0,
coldWhiteChannel: 0,
warmWhiteChannel: 0,
amberChannel: 0,
uvChannel: 0,
};

public setColor(color: RgbColor): void {
this.currentValues = rgbColorDefinitions[color].definition;
}

public setCustomColor(color: IColorsRgb): void {
const givenColors = Object.keys(color) as (keyof IColorsRgb)[];
givenColors.forEach((key: keyof IColorsRgb) => {
this.currentValues[key] = color[key]!;
});
}

public reset(): void {
this.currentValues = {
redChannel: 0,
greenChannel: 0,
blueChannel: 0,
coldWhiteChannel: 0,
warmWhiteChannel: 0,
amberChannel: 0,
uvChannel: 0,
};
}

private getColor(color: keyof Required<IColorsRgb>): number {
let value = this.currentValues[color]!;
if (this.masterDimChannel) return value;
value = Math.round(value * this.currentBrightness);
return value;
}

public setStrobeInDmx(values: number[], shutterOptions: LightsFixtureShutterOptions[]): number[] {
if (this.masterDimChannel) values[this.masterDimChannel - 1] = 255;
if (this.shutterChannel)
values[this.shutterChannel - 1] =
shutterOptions.find((o) => o.shutterOption === ShutterOption.STROBE)?.channelValue ?? 0;

if (this.shutterChannel || this.strobePing) {
// If we have a shutter channel or we should manually strobe
values[this.redChannel - 1] = 255;
values[this.blueChannel - 1] = 255;
values[this.greenChannel - 1] = 255;
if (this.warmWhiteChannel) values[this.warmWhiteChannel - 1] = 255;
if (this.coldWhiteChannel) values[this.coldWhiteChannel - 1] = 255;
if (this.amberChannel) values[this.amberChannel - 1] = 255;
} else if (!this.shutterChannel) {
// If we do not have a shutter channel and the ping is off,
// turn off all colors
values[this.redChannel - 1] = 0;
values[this.blueChannel - 1] = 0;
values[this.greenChannel - 1] = 0;
if (this.warmWhiteChannel) values[this.warmWhiteChannel - 1] = 0;
if (this.coldWhiteChannel) values[this.coldWhiteChannel - 1] = 0;
if (this.amberChannel) values[this.amberChannel - 1] = 0;
}

// If we have no shutter channel, manually flip the strobe bit
if (!this.shutterChannel) {
this.strobePing = !this.strobePing;
}

return values;
}

public setColorsInDmx(values: number[], shutterOptions: LightsFixtureShutterOptions[]): number[] {
if (this.masterDimChannel)
values[this.masterDimChannel - 1] = Math.round(this.currentBrightness * 255);
if (this.shutterChannel)
values[this.shutterChannel - 1] =
shutterOptions.find((o) => o.shutterOption === ShutterOption.OPEN)?.channelValue ?? 0;
values[this.redChannel - 1] = this.getColor('redChannel');
values[this.greenChannel - 1] = this.getColor('greenChannel');
values[this.blueChannel - 1] = this.getColor('blueChannel');
if (this.coldWhiteChannel != null) {
values[this.coldWhiteChannel - 1] = this.getColor('coldWhiteChannel');
}
if (this.warmWhiteChannel != null) {
values[this.warmWhiteChannel - 1] = this.getColor('warmWhiteChannel');
}
if (this.amberChannel != null) {
values[this.amberChannel - 1] = this.getColor('amberChannel');
}
if (this.uvChannel != null) {
values[this.uvChannel - 1] = this.getColor('uvChannel');
}

return values;
}
}
123 changes: 123 additions & 0 deletions src/modules/lights/entities/colors-wheel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Column, OneToMany } from 'typeorm';
import LightsMovingHeadWheelShutterOptions from './lights-moving-head-wheel-shutter-options';
import LightsWheelColorChannelValue from './lights-wheel-color-channel-value';
import LightsWheelGoboChannelValue from './lights-wheel-gobo-channel-value';
import LightsWheelRotateChannelValue from './lights-wheel-rotate-channel-value';
import { RgbColor, rgbColorDefinitions, WheelColor } from '../color-definitions';
import Colors from './colors';
import LightsFixtureShutterOptions, { ShutterOption } from './lights-fixture-shutter-options';

export interface IColorsWheel {
colorChannel: number;
goboChannel: number;
goboRotateChannel?: number | null;
}

export default class ColorsWheel extends Colors implements IColorsWheel {
@Column({ type: 'tinyint', unsigned: true })
public masterDimChannel: number;

@Column({ type: 'tinyint', unsigned: true, nullable: true })
public shutterChannel?: number;

@Column({ type: 'tinyint', unsigned: true })
public colorChannel: number;

@Column({ type: 'tinyint', unsigned: true })
public goboChannel: number;

@Column({ type: 'tinyint', nullable: true, unsigned: true })
public goboRotateChannel: number | null;

@OneToMany(() => LightsWheelColorChannelValue, (c) => c.movingHead, { eager: true })
public colorChannelValues: LightsWheelColorChannelValue[];

@OneToMany(() => LightsWheelGoboChannelValue, (c) => c.movingHead, { eager: true })
public goboChannelValues: LightsWheelGoboChannelValue[];

@OneToMany(() => LightsWheelRotateChannelValue, (c) => c.movingHead, { eager: true })
public goboRotateChannelValues: LightsWheelRotateChannelValue[];

private strobePing = false;

private currentValues: IColorsWheel = {
colorChannel: 0,
goboChannel: 0,
goboRotateChannel: 0,
};

public setColor(color: RgbColor): void {
const wheelColor = rgbColorDefinitions[color].alternative;
const channelValueObj = this.colorChannelValues.find((v) => v.name === wheelColor);
this.currentValues = {
...this.currentValues,
colorChannel: channelValueObj?.value ?? 0,
};
}

public setGobo(gobo?: string) {
const channelValueObj = this.goboChannelValues.find((v) => v.name === gobo);
this.currentValues = {
...this.currentValues,
goboChannel: channelValueObj?.value ?? 0,
};
}

public setGoboRotate(rotate?: string) {
const channelValueObj = this.goboRotateChannelValues.find((v) => v.name === rotate);
this.currentValues = {
...this.currentValues,
goboRotateChannel: channelValueObj?.value ?? 0,
};
}

public reset(): void {
this.currentValues = {
colorChannel: 0,
goboChannel: 0,
goboRotateChannel: 0,
};
}

private get channelValues() {
return this.currentValues;
}

public setStrobeInDmx(values: number[], shutterOptions: LightsFixtureShutterOptions[]): number[] {
if (this.shutterChannel)
values[this.shutterChannel - 1] =
shutterOptions.find((o) => o.shutterOption === ShutterOption.STROBE)?.channelValue ?? 0;
values[this.colorChannel - 1] =
this.colorChannelValues.find((o) => o.name === WheelColor.WHITE)?.value ?? 0;

if (this.shutterChannel || this.strobePing) {
// If we have a shutter channel or we should manually strobe,
// turn on the light
values[this.masterDimChannel - 1] = 255;
} else if (!this.shutterChannel) {
// If we do not have a shutter channel and the ping is off,
// turn off the light
values[this.masterDimChannel - 1] = 0;
}

// If we have no shutter channel, manually flip the strobe bit
if (!this.shutterChannel) {
this.strobePing = !this.strobePing;
}
return values;
}

public setColorsInDmx(values: number[], shutterOptions: LightsFixtureShutterOptions[]): number[] {
values[this.masterDimChannel - 1] = Math.round(this.currentBrightness * 255);
if (this.shutterChannel)
values[this.shutterChannel - 1] =
shutterOptions.find((o) => o.shutterOption === ShutterOption.OPEN)?.channelValue ?? 0;
values[this.colorChannel - 1] = this.channelValues.colorChannel;
values[this.goboChannel - 1] = this.channelValues.goboChannel;
if (this.goboRotateChannel != null) {
values[this.goboRotateChannel - 1] = this.channelValues.goboRotateChannel || 0;
}

return values;
}
}
Loading
Loading