Skip to content

Commit 4f36546

Browse files
committed
Consolidate FixDef, Effects and Projections to Nodes !
Misc ==== - noise.sc: add controls - add octo.sc, add lines Server ===== - rename Effect.h to Nodes.h, combine projection, fixdef and effect in Node, add Panel16 and Multiply - Animations: add externals for octo, add sync hack and use scriptRuntime.execute - Physical layer: add setup and loop
1 parent 1e8e823 commit 4f36546

File tree

11 files changed

+334
-159
lines changed

11 files changed

+334
-159
lines changed

docs/moonbase/module/animations.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,37 +37,38 @@ They represent current state of Live Script processing and will be extended as m
3737

3838
* Multiple Nodes can be created (1)
3939
* Each node can have properties (compare controls in WLED / StarLight)
40-
* Each node can run precompile code or Live scripts (with or without loop)
40+
* Each node can run precompile code or Live scripts (with or without loop) 🚧
4141
* Each node has a type:
42-
* Fixture definition: tell where each pixture is in a 1D/2D/3D physical coordinate space (based on StarLight)
42+
* Fixture definition: tell where each pixture is in a 1D/2D/3D physical coordinate space (based on StarLight)
4343
* Fixture mapping: change the fixture to a 1D/2D/3D virtual coordinate space
4444
* A fixture mapping is for a specific fixture dimension and effect dimension
4545
* e.g. if the fixture is a globe, you can map that to 2D using mercator projection mapping
4646
* if the fixture is 200x200 you can map it to 50x50
4747
* if the fixture is 2D, a 1D effect can be shown as a circle or a bar (as WLED expand1D)
48-
* Effect: run an effect in a virtual coordinate space
49-
* or in the physical space if you want to run at highest performance, e.g. a random effect doesn't need to go through mappings ✅
50-
* Modifier: Mirror, rotate, etc, multiple projections allowed (projection in StarLight)
48+
* Effect:
49+
* run an effect in a virtual coordinate space ✅
50+
* in the physical space if you want to run at highest performance, e.g. a random effect doesn't need to go through mappings ✅
51+
* Modifier: Mirror, rotate, etc, multiple projections allowed (projection in StarLight) 🚧
5152
* Driver show: show the result on Leds (using FastLED, hpwit drivers), ArtNet, DDP, ...
5253
* Future situation: Nodes and noodles (2)
5354
* Replace the nodes table (1) by a graphical view (2)
5455
* Virtual Layer (MappingTable) (3)
55-
* Array of arrays. Outer array is virtual pixels, inner array is physical pixels.
56-
* Implemented efficiently using the StarLight PhysMap struct
57-
* e.g. [[],[0],[1,2],[3,4,5],[6,7,8,9]]
56+
* Array of arrays. Outer array is virtual pixels, inner array is physical pixels.
57+
* Implemented efficiently using the StarLight PhysMap struct
58+
* e.g. [[],[0],[1,2],[3,4,5],[6,7,8,9]]
5859
* first virtual pixel is not mapped to a physical pixel
5960
* second virtual pixel is mapped to physical pixel 0
6061
* third virtual pixel is mapped to physical pixels 1 and 2
6162
* and so on
6263
* Virtual pixels can be 1D, 2D or 3D. Physical pixels also, in any combination
63-
* Using x + y * sizeX + z * sizeX * sizeY
64-
* set/getPixelColor functions used in effects using the MappingTable
64+
* Using x + y * sizeX + z * sizeX * sizeY 🚧
65+
* set/getPixelColor functions used in effects using the MappingTable
6566
* Nodes manipulate the MappingTable and/or interfere in the effects loop
66-
* A Virtual Layer gets updated if fixture, mapping or dimensions change
67-
* An effect uses a virtual layer. One Virtual layer can have multiple effects.
67+
* A Virtual Layer gets updated if fixture, mapping or dimensions change 🚧
68+
* An effect uses a virtual layer. One Virtual layer can have multiple effects.
6869
* Physical layer
69-
* CRGB leds[NUM_LEDS] are physical pixels (as in FASTLED)
70-
* A Physical layer has one or more virtual layers and a virtual layer has one or more effects using it.
70+
* CRGB leds[NUM_LEDS] are physical pixels (as in FASTLED)
71+
* A Physical layer has one or more virtual layers and a virtual layer has one or more effects using it.
7172
* Presets/playlist: change (part of) the nodes model
7273

7374
✅: Done

misc/livescripts/E_lines.sc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
void setup()
2+
{
3+
}
4+
5+
void loop() {
6+
fadeToBlackBy(255);
7+
int x = millis() / 100;
8+
for (int y = 0; y < height; y++) {
9+
sCFP(y*width+x, CRGB(255,0,0), 255);
10+
}
11+
}

misc/livescripts/E_noise.sc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//E_noise.sc
1+
uint8_t intensityControl = 128;
2+
uint8_t speedControl = 128;
23

34
void setup()
45
{

misc/livescripts/E_octo.sc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
uint8_t intensityControl = 128;
2+
uint8_t speedControl = 128;
3+
4+
#define PI 3.1415926535
5+
int nb_branches;
6+
uint8_t C_X;
7+
uint8_t C_Y;
8+
uint8_t mapp;
9+
uint8_t rMapRadius[NUM_LEDS];
10+
uint8_t rMapAngle[NUM_LEDS];
11+
uint32_t t = 0; //=speed
12+
13+
void setup()
14+
{
15+
C_X = width / 2;
16+
C_Y = height / 2;
17+
mapp = 255 / width;
18+
for (int x = -C_X; x < C_X + (width % 2); x++) {
19+
for (int y = -C_Y; y < C_Y + (height % 2); y++) {
20+
21+
float h=128*(atan2(y, x)/PI);
22+
rMapAngle[(x + C_X) *height+y + C_Y]= (int)(h);
23+
h=hypot(x,y);
24+
rMapRadius[(x + C_X)*height +y + C_Y] = (int)(h); //thanks Sutaburosu
25+
}
26+
}
27+
}
28+
29+
30+
void loop() {
31+
32+
nb_branches = intensityControl / 25;
33+
34+
for (uint8_t x = 0; x < width; x++) {
35+
for (uint8_t y = 0; y < height; y++) {
36+
uint8_t angle = rMapAngle[x*height+y];
37+
uint8_t radius = rMapRadius[x*height+y];
38+
uint16_t intensity = sin8(sin8((angle * 4 - radius*mapp) / 4 + t) + radius*mapp - 2*t+ angle * nb_branches);
39+
40+
sPC(y*width+x, hsv(2*t - radius*mapp, 255, intensity));
41+
}
42+
}
43+
t = now * speedControl / 32 / 25; //speedControl 0..8, 40 changes per second
44+
}

misc/livescripts/rings241.sc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ void add(int leds, int radius) {
66
int x = width / 2.0 + ((sin8(255 * i / leds) - 127) / 127.0) * radius / 10.0;
77
int y = height / 2.0 + ((cos8(255 * i / leds) - 127) / 127.0) * radius / 10.0;
88
addPixel(x, y, 0);
9-
printf("%d %d %d\n", leds,x,y);
9+
printf("%d %d %d\n", leds,x,y);
1010
}
1111
}
1212

src/MoonLight/ModuleAnimations.h

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,34 @@
2121
#include "../MoonBase/Module.h"
2222

2323
#if FT_LIVESCRIPT
24+
#define USE_FASTLED //as ESPLiveScript.h calls hsv ! one of the reserved functions!!
2425
#include "ESPLiveScript.h" //note: contains declarations AND definitions, therefore can only be included once!
25-
#endif
26-
27-
#include "Effect.h" //effect.h will include VirtualLayer.h which will include PhysicalLayer.h
26+
static bool waitingOnLiveScript = true;
27+
static bool scriptsRunning = false;
28+
#endif
29+
30+
#include "Nodes.h" //Nodes.h will include VirtualLayer.h which will include PhysicalLayer.h
2831

2932
PhysicalLayer layerP; //global declaration of the physical layer
3033

3134
//to do: move to virtual layer ...
32-
void fadeToBlackBy_static(uint8_t fadeValue)
33-
{
34-
layerP.layerV[0]->fadeToBlackBy(fadeValue);
35-
}
35+
void _fadeToBlackBy(uint8_t fadeValue) { layerP.layerV[0]->fadeToBlackBy(fadeValue);}
36+
static void sPCLive(uint16_t pixel, CRGB color) {layerP.layerV[0]->setPixelColor(pixel, color);} //setPixelColor with color
37+
static void sCFPLive(uint16_t pixel, uint8_t index, uint8_t brightness) { layerP.layerV[0]->setPixelColor(pixel, ColorFromPalette(PartyColors_p, index, brightness));} //setPixelColor within palette
3638

3739
static void _addPin(uint8_t pinNr) {layerP.addPin(pinNr);}
3840
static void _addPixelsPre() {layerP.addPixelsPre();}
3941
static void _addPixel(uint16_t x, uint16_t y, uint16_t z) {layerP.addPixel({x, y, z});}
4042
static void _addPixelsPost() {layerP.addPixelsPost();}
4143

44+
static float _triangle(float j) {return 1.0 - fabs(fmod(2 * j, 2.0) - 1.0);}
45+
static float _time(float j) {
46+
float myVal = millis();;
47+
myVal = myVal / 65535 / j; // PixelBlaze uses 1000/65535 = .015259.
48+
myVal = fmod(myVal, 1.0); // ewowi: with 0.015 as input, you get fmod(millis/1000,1.0), which has a period of 1 second, sounds right
49+
return myVal;
50+
}
51+
4252
class ModuleAnimations : public Module
4353
{
4454
public:
@@ -55,24 +65,53 @@ class ModuleAnimations : public Module
5565
) : Module("animations", server, sveltekit, filesService) {
5666
ESP_LOGD(TAG, "constructor");
5767

58-
addExternalVariable("leds", "CRGB *", "", (void *)layerP.leds);
59-
addExternalFunction("fadeToBlackBy", "void", "uint8_t", (void *)fadeToBlackBy_static);
68+
uint8_t (*_inoise8)(uint16_t, uint16_t, uint16_t)=inoise8;
6069
uint16_t (*_random16)(uint16_t)=random16; //enforce specific random16 function
61-
addExternalFunction("random16", "uint16_t", "uint16_t", (void *)_random16);
62-
addExternalFunction("sin8", "uint8_t", "uint8_t", (void *)sin8);
63-
addExternalFunction("cos8", "uint8_t", "uint8_t", (void *)cos8);
64-
addExternalFunction("delay", "void", "uint8_t", (void *)delay);
70+
71+
//default
72+
addExternalFun("uint32_t", "millis", "", (void *)millis);
73+
float (*_sin)(float) = sin;
74+
addExternalFun("float", "sin", "float", (void *)_sin);
75+
float (*_cos)(float) = cos;
76+
addExternalFun("float", "cos", "float", (void *)_cos);
77+
float (*_atan2)(float, float) = atan2;
78+
addExternalFun("float", "atan2", "float,float",(void*)_atan2);
79+
float (*_hypot)(float, float) = hypot;
80+
addExternalFun("float", "hypot", "float,float",(void*)_hypot);
81+
addExternalFun("float", "time", "float", (void *)_time);
82+
addExternalFun("float", "triangle", "float", (void *)_triangle);
83+
6584
addExternalFunction("addPin", "void", "uint8_t", (void *)_addPin);
6685
addExternalFunction("addPixelsPre", "void", "", (void *)_addPixelsPre);
6786
addExternalFunction("addPixel", "void", "uint16_t,uint16_t,uint16_t", (void *)_addPixel);
6887
addExternalFunction("addPixelsPost", "void", "", (void *)_addPixelsPost);
69-
88+
addExternalFunction("cos8", "uint8_t", "uint8_t", (void *)cos8);
89+
addExternalFunction("delay", "void", "uint8_t", (void *)delay);
90+
addExternalFunction("fadeToBlackBy", "void", "uint8_t", (void *)_fadeToBlackBy);
91+
addExternalFunction("inoise8", "uint8_t", "uint16_t,uint16_t,uint16_t", (void *)_inoise8);
92+
addExternalVariable("leds", "CRGB *", "", (void *)layerP.leds);
93+
addExternalVariable("now", "uint32_t", "", (void *)millis);
94+
addExternalFunction("random16", "uint16_t", "uint16_t", (void *)_random16);
95+
addExternalFunction("sin8", "uint8_t", "uint8_t", (void *)sin8);
96+
addExternalFunction("sPC", "void", "uint16_t,CRGB", (void *)sPCLive);
97+
addExternalFunction("sCFP", "void", "uint16_t,uint8_t,uint8_t", (void *)sCFPLive);
98+
7099
for (asm_external el: external_links) {
71100
ESP_LOGD(TAG, "elink %s %s %d", el.shortname.c_str(), el.name.c_str(), el.type);
72101
}
73102
// ... send to client
74103
}
75104

105+
void addExternalVal(string result, string name, void * ptr) {
106+
if (findLink(name, externalType::value) == -1) //not allready added earlier
107+
addExternalVariable(name, result, "", ptr);
108+
}
109+
110+
void addExternalFun(string result, string name, string parameters, void * ptr) {
111+
if (findLink(name, externalType::function) == -1) //not allready added earlier
112+
addExternalFunction(name, result, parameters, ptr);
113+
}
114+
76115
void begin() {
77116
Module::begin();
78117

@@ -132,6 +171,8 @@ class ModuleAnimations : public Module
132171
values.add("Sinus");
133172
values.add("Lissajous");
134173
values.add("Lines");
174+
values.add("Panel16");
175+
values.add("Multiply");
135176
//find all the .sc files on FS
136177
File rootFolder = ESPFS.open("/");
137178
walkThroughFiles(rootFolder, [&](File folder, File file) {
@@ -226,14 +267,14 @@ class ModuleAnimations : public Module
226267
animationsChanged = false;
227268

228269
//rebuild layerP->layerV->effects
229-
for (Effect* effect : layerP.layerV[0]->effects) {
230-
ESP_LOGD(TAG, "delete effect %s", effect->name());
231-
delete effect;
270+
for (Node* node : layerP.layerV[0]->nodes) {
271+
ESP_LOGD(TAG, "delete effect %s", node->name());
272+
delete node;
232273
}
233-
layerP.layerV[0]->effects.clear(); //remove all effects
274+
layerP.layerV[0]->nodes.clear(); //remove all effects
234275
for (JsonObject node: _state.data["nodes"].as<JsonArray>()) {
235276
ESP_LOGD(TAG, "create effect %s", node["animation"].as<String>().c_str());
236-
layerP.addEffect(node["animation"]); // fill the layers and effects ...
277+
layerP.addNode(node["animation"]); // fill the layers and effects ...
237278
}
238279
}
239280

@@ -247,12 +288,29 @@ class ModuleAnimations : public Module
247288
layerP.leds[i] = CRGB(0, 0, 128);
248289
}
249290

291+
if (scriptsRunning && !waitingOnLiveScript) {// show has been called (in other loop)
292+
waitingOnLiveScript = true; //waiting on Live Script
293+
// ESP_LOGD(TAG, "waitingOnLiveScript %d %d", scriptsRunning, waitingOnLiveScript);
294+
while (waitingOnLiveScript) delay(1); // so live can continue
295+
}
296+
250297
// Serial.printf(" %s", animation.c_str());
251298
if (showLeds) driverShow();
252299
}
253300

254301
//update scripts / read only values in the UI
255302
void loop1s() {
303+
bool isRunning = false;
304+
for (Executable &exec: scriptRuntime._scExecutables) {
305+
if (exec.isRunning()) {
306+
isRunning = true;
307+
break;
308+
}
309+
}
310+
scriptsRunning = isRunning;
311+
if (!scriptsRunning) waitingOnLiveScript = true; //reset to default
312+
// ESP_LOGD(TAG, "waitingOnLiveScript %d %d", scriptsRunning, waitingOnLiveScript);
313+
256314
if (!_socket->getConnectedClients()) return;
257315

258316
JsonDocument newData; //to only send updatedData
@@ -291,18 +349,23 @@ class ModuleAnimations : public Module
291349

292350
void driverShow()
293351
{
294-
if (_state.data["driverOn"])
352+
if (_state.data["driverOn"] && FastLED.count()) {
295353
FastLED.show();
354+
}
296355
}
297356

298357
//ESPLiveScript functions
299358
//=======================
300359

301-
static void show()
360+
static void sync()
302361
{
303-
delay(1); //to feed the watchdog (also if loopState == 0)
362+
// Serial.printf("±");
304363

305-
// gAnimations->driverShow();
364+
delay(1); //to feed the watchdog (also if loopState == 0)
365+
// ESP_LOGD(TAG, "waitingOnLiveScript %d %d", scriptsRunning, waitingOnLiveScript);
366+
while (!waitingOnLiveScript) delay(1); //to feed the watchdog
367+
//do Live Script cycle
368+
waitingOnLiveScript = false; //Live Script produced a frame, main loop will deal with it
306369
}
307370

308371
void compileAndRun(const char * animation, const char * type, JsonVariant error) {
@@ -324,21 +387,24 @@ class ModuleAnimations : public Module
324387

325388
//send UI spinner
326389

327-
runningPrograms.setFunctionToSync(show);
390+
runningPrograms.setFunctionToSync(sync);
328391

329392
//run the recompile not in httpd but in main loopTask (otherwise we run out of stack space)
330393
// runInLoopTask.push_back([&, animation, type, error] {
331394
ESP_LOGD(TAG, "compileAndRun %s %s", animation, type);
332395
File file = ESPFS.open(animation);
333396
if (file) {
334-
std::string scScript = file.readString().c_str();
335-
// scScript += "void main(){setup();sync();}";
397+
std::string scScript;
398+
scScript += "#define NUM_LEDS " + std::to_string(layerP.nrOfLeds) + "\n"; //NUM_LEDS is used in arrays -> must be define e.g. uint8_t rMapRadius[NUM_LEDS];
399+
scScript += "#define width " + std::to_string(layerP.layerV[0]->size.x) + "\n";
400+
scScript += "#define height " + std::to_string(layerP.layerV[0]->size.y) + "\n";
401+
scScript += "#define depth " + std::to_string(layerP.layerV[0]->size.z) + "\n";
402+
scScript += file.readString().c_str();
336403
file.close();
337404

338405
// gAnimations = this;
339406
if (equal(type, "Effect")) {
340-
scScript += "#define NUM_LEDS " + std::to_string(layerP.nrOfLeds) + "\n"; //NUM_LEDS is used in arrays -> must be define e.g. uint8_t rMapRadius[NUM_LEDS];
341-
scScript += "void main(){setup();while(2>1){loop();delay(1);}}"; //sync();
407+
scScript += "void main(){setup();while(2>1){loop();sync();}}"; //;
342408
} else if (equal(type, "Fixture definition")) {
343409
scScript += "void main(){addPixelsPre();setup();addPixelsPost();}";
344410
}
@@ -348,15 +414,16 @@ class ModuleAnimations : public Module
348414

349415
// ESP_LOGD(TAG, "parsing %s", scScript.c_str());
350416

351-
Executable executable = parser.parseScript(&scScript);
417+
Executable executable = parser.parseScript(&scScript); // note that this class will be deleted after the function call !!!
352418
executable.name = animation;
353419
ESP_LOGD(TAG, "parsing %s done\n", animation);
354420
scriptRuntime.addExe(executable); //if already exists, delete it first
355421
ESP_LOGD(TAG, "addExe success %s\n", executable.exeExist?"true":"false");
356422

357423
if (executable.exeExist) {
358424
if (equal(type, "Fixture definition")) {
359-
executable.execute("main"); //background task (async - vs sync)
425+
// executable.execute("main"); //background task (async - vs sync)
426+
scriptRuntime.execute(executable.name, "main"); //background task (async - vs sync)
360427
// pass = 1;
361428
// liveM->executeTask(liveFixtureID, "c");
362429
// pass = 2;
@@ -366,7 +433,8 @@ class ModuleAnimations : public Module
366433
// setup : create controls
367434
// executable.execute("setup");
368435
// send controls to UI
369-
executable.executeAsTask("main"); //background task (async - vs sync)
436+
// executable.executeAsTask("main"); //background task (async - vs sync)
437+
scriptRuntime.executeAsTask(executable.name, "main"); //background task (async - vs sync)
370438
//assert failed: xEventGroupSync event_groups.c:228 (uxBitsToWaitFor != 0)
371439
}
372440
} else
@@ -386,10 +454,15 @@ class ModuleAnimations : public Module
386454
void kill(int index) {
387455
ESP_LOGD(TAG, "kill %d", index);
388456
if (index < scriptRuntime._scExecutables.size()) {
389-
scriptRuntime.kill(scriptRuntime._scExecutables[index].name);
390-
scriptRuntime.free(scriptRuntime._scExecutables[index].name);
457+
// scriptRuntime.kill(scriptRuntime._scExecutables[index].name);
458+
// scriptRuntime.free(scriptRuntime._scExecutables[index].name);
391459
scriptRuntime.deleteExe(scriptRuntime._scExecutables[index].name);
392460
}
461+
462+
if (waitingOnLiveScript) {
463+
waitingOnLiveScript = false;
464+
// ESP_LOGD(TAG, "waitingOnLiveScript %d %d", scriptsRunning, waitingOnLiveScript);
465+
}
393466
}
394467
}; // class ModuleAnimations
395468

0 commit comments

Comments
 (0)