Skip to content

Commit de77edf

Browse files
committed
Add TroyMovingHeadEffect part 1-2
1 parent f118ea3 commit de77edf

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed

src/MoonLight/Effects.h

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,223 @@ class SphereMoveEffect: public Node {
748748
}
749749
}; // SphereMove3DEffect
750750

751+
class TroyMovingHeadEffect: public Node {
752+
public:
753+
754+
static const char * name() {return "Troy's Moving Heads 15 CH 🔥♫💫🐺";}
755+
756+
uint8_t bpm;
757+
uint8_t pan;
758+
uint8_t tilt;
759+
uint8_t zoom;
760+
uint8_t range;
761+
uint8_t colorwheel;
762+
uint8_t light_number;
763+
uint8_t colorwheelbrightness; // 0-255, 0 = off, 255 = full brightness
764+
time_t cooldown = millis();
765+
uint16_t start_channel = 0; //start channel for this effect, used to set the DMX map
766+
767+
bool autoMove;
768+
bool audioReactive;
769+
bool invert;
770+
int closestColorIndex = -1;
771+
772+
std::vector<CRGB> colorwheelpalette = {
773+
CRGB(255, 255, 255), // White
774+
CRGB(255, 0, 0), // Red
775+
CRGB(0, 255, 0), // Green
776+
CRGB(0, 0, 255), // Blue
777+
CRGB(255, 255, 0), // Yellow
778+
CRGB(0, 255, 255), // Cyan
779+
CRGB(255, 165, 0), // Orange
780+
CRGB(128, 0, 128) // Purple
781+
};
782+
783+
void setup() override {
784+
addControl(&start_channel, "start_channel", "range", 0, 0, 512); // DMX start channel for this effect
785+
addControl(&light_number, "light_number", "range", 0, 0, 512);
786+
addControl(&bpm, "bpm", "range", 30);
787+
addControl(&pan, "pan", "range", 175);
788+
addControl(&tilt, "tilt", "range", 90);
789+
addControl(&zoom, "zoom", "range", 20);
790+
addControl(&colorwheel, "colorwheel", "range", 0, 0, 7); // 0-7 for 8 colors in the colorwheel
791+
addControl(&colorwheelbrightness, "colorwheelbrightness", "range", 255); // 0-7 for 8 colors in the colorwheel
792+
addControl(&autoMove, "autoMove", "checkbox", false);
793+
addControl(&range, "range", "range", 20);
794+
addControl(&audioReactive, "audioReactive", "checkbox", false);
795+
addControl(&invert, "invert", "checkbox", false);
796+
layerV->layerP->lights.header.channelsPerLight = 15; //set channels per light to 15 (RGB + Pan + Tilt + Zoom + Brightness)
797+
layerV->layerP->lights.header.offsetRGB = start_channel+10; //set offset for RGB lights in DMX map
798+
layerV->layerP->lights.header.offsetPan = start_channel+0;
799+
layerV->layerP->lights.header.offsetTilt = start_channel+1;
800+
layerV->layerP->lights.header.offsetZoom = start_channel+7;
801+
layerV->layerP->lights.header.offsetBrightness = start_channel+8; //set offset for brightness
802+
layerV->layerP->lights.header.offsetColorWheel = start_channel+5; //set offset for color wheel in DMX map
803+
layerV->layerP->lights.header.offsetColorWheelBrightness = start_channel+3; //set offset for color wheel brightness in DMX map
804+
}
805+
806+
// Function to compute Euclidean distance between two colors
807+
double colorDistance(const CRGB& c1, const CRGB& c2) {
808+
return std::sqrt(std::pow(c1.r - c2.r, 2) + std::pow(c1.g - c2.g, 2) + std::pow(c1.b - c2.b, 2));
809+
}
810+
811+
// Function to find the index of the closest color
812+
int findClosestColorWheelIndex(const CRGB& inputColor, const std::vector<CRGB>& palette) {
813+
int closestIndex = 0;
814+
double minDistance = colorDistance(inputColor, palette[0]);
815+
816+
for (size_t i = 1; i < palette.size(); ++i) {
817+
double distance = colorDistance(inputColor, palette[i]);
818+
if (distance < minDistance) {
819+
minDistance = distance;
820+
closestIndex = static_cast<int>(i);
821+
}
822+
}
823+
824+
return closestIndex;
825+
}
826+
827+
void loop() override {
828+
829+
uint8_t light[15]; //layerV->layerP->lights.header.channelsPerLight is not accepted by setLight (yet)
830+
memset(light, 0, sizeof(light)); //set light to 0
831+
832+
if (audioReactive) {
833+
// layerV->layerP->lights.header.offsetRGB = 10;
834+
layerV->layerP->lights.header.setRGB(light, CRGB(audio.bands[15],audio.bands[7],audio.bands[0]));
835+
if (audio.bands[2] > 200 && cooldown + 3000 < millis()) { //cooldown for 3 seconds
836+
cooldown = millis();
837+
colorwheel = random8(8)*5; //random colorwheel index and convert to 0-35 range
838+
}
839+
layerV->layerP->lights.header.setColorWheel(light, colorwheel);
840+
layerV->layerP->lights.header.setColorWheelBrightness(light, (audio.bands[0]>200)?audio.bands[0]:0); // Use the first band for brightness
841+
layerV->layerP->lights.header.setZoom(light, (audio.bands[0]>200)?0:128);
842+
uint8_t mypan = beatsin8(bpm, pan-range, pan+range, 0, audio.bands[0]/2);
843+
uint8_t mytilt = beatsin8(bpm, tilt-range, tilt+range, 0, audio.bands[0]/2);
844+
if (invert) {
845+
mypan = 255 - mypan; // invert pan
846+
mytilt = 255 - mytilt; // invert tilt
847+
}
848+
layerV->layerP->lights.header.setPan(light, mypan);
849+
layerV->layerP->lights.header.setTilt(light, mytilt);
850+
layerV->layerP->lights.header.setBrightness(light, (audio.bands[0]>200)?0:layerV->layerP->lights.header.brightness);
851+
} else {
852+
// layerV->layerP->lights.header.offsetRGB = 10;
853+
layerV->layerP->lights.header.setRGB(light, CHSV( beatsin8(10), 255, 255));
854+
layerV->layerP->lights.header.setColorWheel(light,colorwheel);
855+
layerV->layerP->lights.header.setColorWheelBrightness(light, colorwheelbrightness); // layerV->layerP->lights.header.brightness);
856+
layerV->layerP->lights.header.setPan(light, autoMove?beatsin8(bpm, pan-range, pan + range, 0, (invert)?128:0): pan); //if automove, pan the light over a range
857+
layerV->layerP->lights.header.setTilt(light, autoMove?beatsin8(bpm, tilt - range, tilt + range, 0, (invert)?128:0): tilt);
858+
layerV->layerP->lights.header.setBrightness(light, layerV->layerP->lights.header.brightness);
859+
}
860+
layerV->setLight({light_number,0,0}, light);
861+
}
862+
};
863+
864+
class TroyMovingHead32Effect: public Node {
865+
public:
866+
867+
static const char * name() {return "Troy's Moving Heads 32 CH 🔥♫💫🐺";}
868+
869+
uint8_t bpm;
870+
uint8_t pan;
871+
uint8_t tilt;
872+
uint8_t zoom;
873+
uint8_t range;
874+
uint8_t cutin;
875+
uint8_t light_number;
876+
time_t cooldown = millis();
877+
uint16_t start_channel = 0; //start channel for this effect, used to set the DMX map
878+
879+
bool autoMove;
880+
bool audioReactive;
881+
bool invert;
882+
883+
void setup() override {
884+
addControl(&start_channel, "start_channel", "range", 0, 0, 512); // DMX start channel for this effect
885+
addControl(&light_number, "light_number", "range", 0, 0, 512); // DMX start channel for this effect
886+
addControl(&bpm, "bpm", "range", 30);
887+
addControl(&pan, "pan", "range", 175);
888+
addControl(&tilt, "tilt", "range", 90);
889+
addControl(&zoom, "zoom", "range", 20);
890+
addControl(&cutin, "cutin", "range", 200);
891+
addControl(&autoMove, "autoMove", "checkbox", false);
892+
addControl(&range, "range", "range", 20);
893+
addControl(&audioReactive, "audioReactive", "checkbox", false);
894+
addControl(&invert, "invert", "checkbox", false);
895+
layerV->layerP->lights.header.channelsPerLight = 32;
896+
layerV->layerP->lights.header.offsetRGB = start_channel+9;
897+
layerV->layerP->lights.header.offsetPan = start_channel+0;
898+
layerV->layerP->lights.header.offsetTilt = start_channel+2;
899+
layerV->layerP->lights.header.offsetZoom = start_channel+5;
900+
layerV->layerP->lights.header.offsetBrightness = start_channel+6;
901+
}
902+
903+
// Function to compute Euclidean distance between two colors
904+
double colorDistance(const CRGB& c1, const CRGB& c2) {
905+
return std::sqrt(std::pow(c1.r - c2.r, 2) + std::pow(c1.g - c2.g, 2) + std::pow(c1.b - c2.b, 2));
906+
}
907+
908+
// Function to find the index of the closest color
909+
int findClosestColorWheelIndex(const CRGB& inputColor, const std::vector<CRGB>& palette) {
910+
int closestIndex = 0;
911+
double minDistance = colorDistance(inputColor, palette[0]);
912+
for (size_t i = 1; i < palette.size(); ++i) {
913+
double distance = colorDistance(inputColor, palette[i]);
914+
if (distance < minDistance) {
915+
minDistance = distance;
916+
closestIndex = static_cast<int>(i);
917+
}
918+
}
919+
return closestIndex;
920+
}
921+
922+
void loop() override {
923+
924+
uint8_t light[32];
925+
memset(light, 0, sizeof(light)); //set light to 0
926+
927+
if (audioReactive) {
928+
layerV->layerP->lights.header.offsetRGB = start_channel+9;
929+
layerV->layerP->lights.header.setRGB(light, CRGB(audio.bands[15]>cutin?audio.bands[15]:0,audio.bands[7]>cutin?audio.bands[7]:0,audio.bands[0]));
930+
layerV->layerP->lights.header.offsetRGB += 4;
931+
layerV->layerP->lights.header.setRGB(light, CRGB(audio.bands[15],audio.bands[7]>cutin?audio.bands[7]:0,audio.bands[0]>cutin?audio.bands[0]:0));
932+
layerV->layerP->lights.header.offsetRGB += 4;
933+
layerV->layerP->lights.header.setRGB(light, CRGB(audio.bands[15]>cutin?audio.bands[15]:0,audio.bands[7],audio.bands[0]>cutin?audio.bands[0]:0));
934+
layerV->layerP->lights.header.offsetRGB += 7;
935+
layerV->layerP->lights.header.setRGB(light, CRGB(audio.bands[15]>cutin?map(audio.bands[15],cutin-1,255,0,255):0,audio.bands[7]>cutin?map(audio.bands[7],cutin-1,255,0,255):0,audio.bands[0]>cutin?map(audio.bands[0],cutin-1,255,0,255):0));
936+
if (audio.bands[0] > cutin) {
937+
layerV->layerP->lights.header.setZoom(light, 255);
938+
cooldown = millis();
939+
} else if (cooldown + 5000 < millis()) {
940+
layerV->layerP->lights.header.setZoom(light, 0);
941+
cooldown = millis();
942+
}
943+
// layerV->layerP->lights.header.setZoom(light, (audio.bands[0]>cutin)?255:0);
944+
uint8_t mypan = beatsin8(bpm, pan-range, pan+range, 0, audio.bands[0]/2);
945+
uint8_t mytilt = beatsin8(bpm, tilt-range, tilt+range, 0, audio.bands[0]/2);
946+
if (invert) {
947+
mypan = 255 - mypan; // invert pan
948+
mytilt = 255 - mytilt; // invert tilt
949+
}
950+
if (audio.bands[0]+audio.bands[7]+audio.bands[15] > 1) {
951+
layerV->layerP->lights.header.setPan(light, mypan);
952+
layerV->layerP->lights.header.setTilt(light, mytilt);
953+
layerV->layerP->lights.header.setBrightness(light, 255);
954+
} else {
955+
layerV->layerP->lights.header.setBrightness(light, 0);
956+
}
957+
} else {
958+
// layerV->layerP->lights.header.offsetRGB = 10;
959+
layerV->layerP->lights.header.setRGB(light, CHSV( beatsin8(10), 255, 255));
960+
layerV->layerP->lights.header.setPan(light, autoMove?beatsin8(bpm, pan-range, pan + range, 0, (invert)?128:0): pan); //if automove, pan the light over a range
961+
layerV->layerP->lights.header.setTilt(light, autoMove?beatsin8(bpm, tilt - range, tilt + range, 0, (invert)?128:0): tilt);
962+
layerV->layerP->lights.header.setBrightness(light, layerV->layerP->lights.header.brightness);
963+
}
964+
layerV->setLight({light_number,0,0}, light);
965+
}
966+
};
967+
751968
class WaveEffect: public Node {
752969
public:
753970

src/MoonLight/ModuleEditor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class ModuleEditor : public Module
123123
values.add(RGBWParEffect::name());
124124
values.add(SinusEffect::name());
125125
values.add(SphereMoveEffect::name());
126+
values.add(TroyMovingHeadEffect::name());
127+
values.add(TroyMovingHead32Effect::name());
126128
values.add(WaveEffect::name());
127129

128130
values.add(HumanSizedCubeLayout::name());

src/MoonLight/PhysicalLayer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ PhysicalLayer::PhysicalLayer() {
165165
else if (equal(name, RandomEffect::name())) node = new RandomEffect();
166166
else if (equal(name, RipplesEffect::name())) node = new RipplesEffect();
167167
else if (equal(name, RGBWParEffect::name())) node = new RGBWParEffect();
168-
else if (equal(name, WaveEffect::name())) node = new WaveEffect();
169168
else if (equal(name, SinusEffect::name())) node = new SinusEffect();
170169
else if (equal(name, SphereMoveEffect::name())) node = new SphereMoveEffect();
170+
else if (equal(name, TroyMovingHeadEffect::name())) node = new TroyMovingHeadEffect();
171+
else if (equal(name, TroyMovingHead32Effect::name())) node = new TroyMovingHead32Effect();
172+
else if (equal(name, WaveEffect::name())) node = new WaveEffect();
171173

172174
else if (equal(name, HumanSizedCubeLayout::name())) node = new HumanSizedCubeLayout();
173175
else if (equal(name, PanelLayout::name())) node = new PanelLayout();

0 commit comments

Comments
 (0)