Skip to content

Commit

Permalink
add PanTiltRoll projection + fixes
Browse files Browse the repository at this point in the history
LedLeds
- add PanTiltRoll projection
- XYZ: support for PanTiltRoll projection
- drawCharacter bug fix

LedModEffects
- add PanTiltRoll projection
- loop: fadeToBlack for rotations

SysModModel:
- replace contextRowNr by setValueRowNr and getValueRowNr
- use in varPre/PostDetails, LedEffects
  • Loading branch information
ewowi committed Mar 26, 2024
1 parent 49cbd13 commit 9df3ab4
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 60 deletions.
4 changes: 0 additions & 4 deletions src/App/LedEffects.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class Effect {
virtual void controls(JsonObject parentVar) {}

void addPalette(JsonObject parentVar, unsigned8 value) {
//currentVar["value"][contextRowNr] will be set to value parameter
JsonObject currentVar = ui->initSelect(parentVar, "pal", value, false, [](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_UIFun: {
ui->setLabel(var, "Palette");
Expand Down Expand Up @@ -1621,12 +1620,9 @@ class Effects {

// effect->loop(leds); //do a loop to set sharedData right
// leds.sharedData.loop();
leds.doMap = true; // avoid effects loop to set contextRowNr
delay(100); // give looptask the time to stop the effect tbd: this is a bit of a hack
mdl->varPreDetails(var, rowNr);
effect->controls(var);
mdl->varPostDetails(var, rowNr);
leds.doMap = false;

effect->setup(leds); //if changed then run setup once (like call==0 in WLED)

Expand Down
22 changes: 21 additions & 1 deletion src/App/LedLeds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ void fastled_fill_rainbow(struct CRGB * targetArray, int numToFill, unsigned8 in
fill_rainbow(targetArray, numToFill, initialhue, deltahue);
}

unsigned16 Leds::XYZ(unsigned16 x, unsigned16 y, unsigned16 z) {
if (projectionNr == p_Rotate) {
Coord3D result = spinXY(x, y, size.x, size.y, proRollSpeed);
return result.x + result.y * size.x + result.z * size.x * size.y;
}
else if (projectionNr == p_PanTiltRoll || projectionNr == p_Preset1) {
Coord3D result = Coord3D{x, y, z};
if (proPanSpeed) result = trigoPanTiltRoll.pan(result, size/2, millis() * 5 / (255 - proPanSpeed));
if (proTiltSpeed) result = trigoPanTiltRoll.tilt(result, size/2, millis() * 5 / (255 - proTiltSpeed));
if (proRollSpeed) result = trigoPanTiltRoll.roll(result, size/2, millis() * 5 / (255 - proRollSpeed));
if (fixture->size.z == 1) result.z = 0; // 3d effects will be flattened on 2D fixtures
if (result >= 0 && result < size)
return result.x + result.y * size.x + result.z * size.x * size.y;
else
return UINT16_MAX;
}
else
return x + y * size.x + z * size.x * size.y;
}

// maps the virtual led to the physical led(s) and assign a color to it
void Leds::setPixelColor(unsigned16 indexV, CRGB color, unsigned8 blendAmount) {
if (indexV < mappingTable.size()) {
Expand All @@ -36,7 +56,7 @@ void Leds::setPixelColor(unsigned16 indexV, CRGB color, unsigned8 blendAmount) {
}
else if (indexV < NUM_LEDS_Max) //no projection
fixture->ledsP[projectionNr==p_Random?random(fixture->nrOfLeds):indexV] = color;
else
else if (indexV != UINT16_MAX) //assuming UINT16_MAX is set explicitly (e.g. in XYZ)
USER_PRINTF(" dev sPC V:%d >= %d", indexV, NUM_LEDS_Max);
}

Expand Down
50 changes: 24 additions & 26 deletions src/App/LedLeds.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum Projections
p_Default,
p_Multiply,
p_Rotate,
p_PanTiltRoll,
p_DistanceFromPoint,
p_Preset1,
p_None,
Expand All @@ -49,21 +50,22 @@ enum Projections
//128: 128, 1 0 -32645
//192: 1, 127 -32645 0

static unsigned trigoCached = 1;
static unsigned trigoUnCached = 1;

struct Trigo {
uint16_t period = 360; //default
Trigo(uint16_t period = 360) {
this->period = period;
}
uint16_t period = 360; //default period 360
Trigo(uint16_t period = 360) {this->period = period;}
float sinValue[3]; uint16_t sinAngle[3] = {UINT16_MAX,UINT16_MAX,UINT16_MAX}; //caching of sinValue=sin(sinAngle) for pan, tilt and roll
float cosValue[3]; uint16_t cosAngle[3] = {UINT16_MAX,UINT16_MAX,UINT16_MAX}; //caching of cosValue=cos(cosAngle) for pan, tilt and roll
virtual float sinBase(uint16_t angle) {return sinf(M_TWOPI * angle / period);}
virtual float cosBase(uint16_t angle) {return cosf(M_TWOPI * angle / period);}
int16_t sin(int16_t factor, uint16_t angle, uint8_t index = 0) {
if (sinAngle[index] != angle) {sinAngle[index] = angle; sinValue[index] = sinBase(angle);} else USER_PRINTF("s");
if (sinAngle[index] != angle) {sinAngle[index] = angle; sinValue[index] = sinBase(angle);trigoUnCached++;} else trigoCached++;
return factor * sinValue[index];
};
int16_t cos(int16_t factor, uint16_t angle, uint8_t index = 0) {
if (cosAngle[index] != angle) {cosAngle[index] = angle; cosValue[index] = cosBase(angle);} else USER_PRINTF("c");
if (cosAngle[index] != angle) {cosAngle[index] = angle; cosValue[index] = cosBase(angle);trigoUnCached++;} else trigoCached++;
return factor * cosValue[index];
};
// https://msl.cs.uiuc.edu/planning/node102.html
Expand Down Expand Up @@ -93,22 +95,22 @@ struct Trigo {
}
Coord3D rotate(Coord3D in, Coord3D middle, uint16_t panAngle, uint16_t tiltAngle, uint16_t rollAngle, uint16_t period = 360) {
this->period = period;
for (int i=0; i<3;i++) sinAngle[i] = UINT16_MAX;
return roll(tilt(pan(in, middle, panAngle), middle, tiltAngle), middle, rollAngle);
}
};

struct Trigo16: Trigo { //FastLed sin16 and cos16
using Trigo::Trigo;
float sinBase(uint16_t angle) {return sin16(65536.0f * angle / period) / 32645.0f;}
float cosBase(uint16_t angle) {return cos16(65536.0f * angle / period) / 32645.0f;}
};
struct Trigo8: Trigo { //FastLed sin8 and cos8
using Trigo::Trigo;
float sinBase(uint16_t angle) {return (sin8(256.0f * angle / period) - 128) / 127.0f;}
float cosBase(uint16_t angle) {return (cos8(256.0f * angle / period) - 128) / 127.0f;}
};
struct Trigo16: Trigo { //FastLed sin16 and cos16
using Trigo::Trigo;
float sinBase(uint16_t angle) {return sin16(65536.0f * angle / period) / 32645.0f;}
float cosBase(uint16_t angle) {return cos16(65536.0f * angle / period) / 32645.0f;}
};

static Trigo trigoPanTiltRoll(255); // Trigo8 is hardly any faster (27 vs 28 fps) (spanXY=28)

class Fixture; //forward

Expand Down Expand Up @@ -178,10 +180,10 @@ static Coord3D spinXY(uint_fast16_t x, uint_fast16_t y, uint_fast16_t width, uin
cosrot = cosf(now);
last_millis = millis()/12;
// scale to fit - comment out the next lines to disable
float maxProj = max(abs(width/2 * sinrot), abs(height/2 * cosrot));
int maxdim = max(width/2, height/2);
float newScaling = maxProj / float(maxdim);
projScale = max(min(newScaling, projScaleMax), projScaleMin);
// float maxProj = max(abs(width/2 * sinrot), abs(height/2 * cosrot));
// int maxdim = max(width/2, height/2);
// float newScaling = maxProj / float(maxdim);
// projScale = max(min(newScaling, projScaleMax), projScaleMin);
}
// center
int x1 = int(x) - width/2;
Expand Down Expand Up @@ -222,7 +224,9 @@ class Leds {
unsigned8 effectDimension = -1;

Coord3D startPos = {0,0,0}, endPos = {UINT16_MAX,UINT16_MAX,UINT16_MAX}; //default
unsigned8 proRSpeed = 128;
unsigned8 proPanSpeed = 128;
unsigned8 proTiltSpeed = 128;
unsigned8 proRollSpeed = 128;

SharedData sharedData;

Expand All @@ -244,14 +248,7 @@ class Leds {
return XYZ(coord.x, coord.y, coord.z);
}

unsigned16 XYZ(unsigned16 x, unsigned16 y, unsigned16 z) {
if (projectionNr == p_Rotate || projectionNr == p_Preset1) {
Coord3D result = spinXY(x, y, size.x, size.y, proRSpeed);
return result.x + result.y * size.x + result.z * size.x * size.y;
}
else
return x + y * size.x + z * size.x * size.y;
}
unsigned16 XYZ(unsigned16 x, unsigned16 y, unsigned16 z);

Leds(Fixture &fixture) {
USER_PRINTF("Leds[%d] constructor %d\n", UINT8_MAX, sizeof(PhysMap));
Expand Down Expand Up @@ -410,8 +407,9 @@ class Leds {
Coord3D chrPixel;
for (chrPixel.y = 0; chrPixel.y<fontSize.y; chrPixel.y++) { // character height
Coord3D pixel;
pixel.z = 0;
pixel.y = y + chrPixel.y;
if (pixel.y >=0 && pixel.y < size.y) {
if (pixel.y >= 0 && pixel.y < size.y) {
byte bits = 0;
switch (font%5) {
case 0: bits = pgm_read_byte_near(&console_font_4x6[(chr * fontSize.y) + chrPixel.y]); break;
Expand Down
63 changes: 44 additions & 19 deletions src/App/LedModEffects.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class LedModEffects:public SysModule {
options.add("Default");
options.add("Multiply");
options.add("Rotate");
options.add("PanTiltRoll");
options.add("Distance ⌛");
options.add("Preset 1");
options.add("None");
Expand Down Expand Up @@ -169,25 +170,45 @@ class LedModEffects:public SysModule {
case f_ChangeFun:
ui->initCheckBox(var, "mirror", false, false, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_ChangeFun:
fixture.projections[rowNr]->doMap = true;
fixture.doMap = true;
if (rowNr < fixture.projections.size()) {
fixture.projections[rowNr]->doMap = true;
fixture.doMap = true;
}
return true;
default: return false;
}});
fixture.projections[rowNr]->doMap = true;
fixture.doMap = true;
if (rowNr < fixture.projections.size()) {
fixture.projections[rowNr]->doMap = true;
fixture.doMap = true;
}
return true;
default: return false;
}});
}
if (proValue == p_Rotate || proValue == p_Preset1) {
ui->initSlider(var, "proRSpeed", 128, 1, 255, false, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
if (proValue == p_PanTiltRoll || proValue == p_Preset1) {
ui->initSlider(var, "proPan", 128, 0, 254, false, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_ChangeFun:
if (rowNr < fixture.projections.size())
fixture.projections[rowNr]->proPanSpeed = mdl->getValue(var, rowNr);
return true;
default: return false;
}});
ui->initSlider(var, "proTilt", 128, 0, 254, false, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_ChangeFun:
if (rowNr < fixture.projections.size())
fixture.projections[rowNr]->proTiltSpeed = mdl->getValue(var, rowNr);
return true;
default: return false;
}});
}
if (proValue == p_Rotate || proValue == p_Preset1 || proValue == p_PanTiltRoll) {
ui->initSlider(var, "proRoll", 128, 0, 254, false, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_UIFun:
ui->setLabel(var, "Rotation speed");
ui->setLabel(var, "Roll speed");
return true;
case f_ChangeFun:
if (rowNr < fixture.projections.size())
fixture.projections[rowNr]->proRSpeed = mdl->getValue(var, rowNr);
fixture.projections[rowNr]->proRollSpeed = mdl->getValue(var, rowNr);
return true;
default: return false;
}});
Expand Down Expand Up @@ -333,17 +354,15 @@ class LedModEffects:public SysModule {

//for each programmed effect
// run the next frame of the effect
// vector iteration on classes is faster!!! (22 vs 30 fps !!!!)
// for (std::vector<Leds *>::iterator leds=fixture.projections.begin(); leds!=fixture.projections.end(); ++leds) {
if (mdl->contextRowNr == UINT8_MAX) {
stackUnsigned8 rowNr = 0;
for (Leds *leds: fixture.projections) {
if (!leds->doMap) { // don't run effect while remapping
// USER_PRINTF(" %d %d,%d,%d - %d,%d,%d (%d,%d,%d)", leds->fx, leds->startPos.x, leds->startPos.y, leds->startPos.z, leds->endPos.x, leds->endPos.y, leds->endPos.z, leds->size.x, leds->size.y, leds->size.z );
mdl->contextRowNr = rowNr++;
effects.loop(*leds);
mdl->contextRowNr = UINT8_MAX;
}
stackUnsigned8 rowNr = 0;
for (Leds *leds: fixture.projections) {
if (!leds->doMap) { // don't run effect while remapping
// USER_PRINTF(" %d %d,%d,%d - %d,%d,%d (%d,%d,%d)", leds->fx, leds->startPos.x, leds->startPos.y, leds->startPos.z, leds->endPos.x, leds->endPos.y, leds->endPos.z, leds->size.x, leds->size.y, leds->size.z );
mdl->getValueRowNr = rowNr++;
effects.loop(*leds);
mdl->getValueRowNr = UINT8_MAX;
if (leds->projectionNr == p_PanTiltRoll || leds->projectionNr == p_Rotate || leds->projectionNr == p_Preset1)
leds->fadeToBlackBy(50);
}
}

Expand Down Expand Up @@ -640,6 +659,12 @@ class LedModEffects:public SysModule {
frameCounter = 0;
}

void loop10s() {
USER_PRINTF("caching %u %u\n", trigoCached, trigoUnCached); //not working for some reason!!
// trigoCached = 0;
// trigoUnCached = 0;
}

private:
unsigned long frameMillis = 0;
unsigned long frameCounter = 0;
Expand Down
3 changes: 2 additions & 1 deletion src/Sys/SysModModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

JsonDocument * SysModModel::model = nullptr;
JsonObject SysModModel::modelParentVar;
unsigned8 SysModModel::contextRowNr = UINT8_MAX;
unsigned8 SysModModel::setValueRowNr = UINT8_MAX;
unsigned8 SysModModel::getValueRowNr = UINT8_MAX;
int SysModModel::varCounter = 1; //start with 1 so it can be negative, see var["o"]

SysModModel::SysModModel() :SysModule("Model") {
Expand Down
15 changes: 11 additions & 4 deletions src/Sys/SysModModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ struct Coord3D {
bool operator<=(Coord3D rhs) {
return x <= rhs.x && y <= rhs.y && z <= rhs.z;
}
bool operator<(Coord3D rhs) {
return x < rhs.x && y < rhs.y && z < rhs.z;
}
bool operator>=(uint16_t rhs) {
return x >= rhs && y >= rhs && z >= rhs;
}

//assignments
Coord3D operator=(Coord3D rhs) {
Expand Down Expand Up @@ -178,7 +184,8 @@ class SysModModel:public SysModule {

bool doWriteModel = false;

static unsigned8 contextRowNr;
static unsigned8 setValueRowNr;
static unsigned8 getValueRowNr;

SysModModel();
void setup();
Expand Down Expand Up @@ -298,7 +305,7 @@ class SysModModel:public SysModule {
JsonVariant getValue(JsonObject var, unsigned8 rowNr = UINT8_MAX) {
if (var["value"].is<JsonArray>()) {
JsonArray valueArray = var["value"].as<JsonArray>();
if (rowNr == UINT8_MAX) rowNr = contextRowNr;
if (rowNr == UINT8_MAX) rowNr = getValueRowNr;
if (rowNr != UINT8_MAX && rowNr < valueArray.size())
return valueArray[rowNr];
else if (valueArray.size())
Expand Down Expand Up @@ -367,13 +374,13 @@ class SysModModel:public SysModule {
varOrder(var, -varOrder(var)); // set all negative
}
}
contextRowNr = rowNr;
setValueRowNr = rowNr;
print->printJson("varPreDetails post", var);
}

void varPostDetails(JsonObject var, unsigned8 rowNr) {

contextRowNr = UINT8_MAX;
setValueRowNr = UINT8_MAX;
if (rowNr != UINT8_MAX) {

print->printJson("varPostDetails pre", var);
Expand Down
10 changes: 5 additions & 5 deletions src/Sys/SysModUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ class SysModUI:public SysModule {
// print->printJson("initVarAndUpdate varFun value is null", var);
} else if (var["value"].is<JsonArray>()) {
JsonArray valueArray = var["value"].as<JsonArray>();
if (mdl->contextRowNr != UINT8_MAX) { // if var in table
if (mdl->contextRowNr >= valueArray.size())
if (mdl->setValueRowNr != UINT8_MAX) { // if var in table
if (mdl->setValueRowNr >= valueArray.size())
valueNeedsUpdate = true;
else if (valueArray[mdl->contextRowNr].isNull())
else if (valueArray[mdl->setValueRowNr].isNull())
valueNeedsUpdate = true;
}
}
Expand All @@ -189,10 +189,10 @@ class SysModUI:public SysModule {
if (valueNeedsUpdate) {
bool valueFunExists = false;
if (varFun) {
valueFunExists = varFun(var, mdl->contextRowNr, f_ValueFun);
valueFunExists = varFun(var, mdl->setValueRowNr, f_ValueFun);
}
if (!valueFunExists) { //setValue provided (if not null)
mdl->setValue(var, value, mdl->contextRowNr); //does changefun if needed, if var in table, update the table row
mdl->setValue(var, value, mdl->setValueRowNr); //does changefun if needed, if var in table, update the table row
}
}
else { //do changeFun on existing value
Expand Down

0 comments on commit 9df3ab4

Please sign in to comment.