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

Rotating 2D Palette effect #3683

Merged
merged 4 commits into from
Jan 27, 2024
Merged
Changes from 1 commit
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
93 changes: 80 additions & 13 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1929,22 +1929,89 @@ static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=64,ix=


uint16_t mode_palette() {
uint16_t counter = 0;
if (SEGMENT.speed != 0)
{
counter = (strip.now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF;
counter = counter >> 8;
}

for (int i = 0; i < SEGLEN; i++)
{
uint8_t colorIndex = (i * 255 / SEGLEN) - counter;
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_MOVING_WRAP, 255));
#ifdef ESP8266
using mathType = int32_t;
using wideMathType = int64_t;
using angleType = uint16_t;
constexpr mathType sInt16Scale = 0x7FFF;
constexpr mathType maxAngle = 0xFFFF;
constexpr mathType staticRotationScale = 256;
constexpr mathType animatedRotationScale = 1;
constexpr int16_t (*sinFunction)(uint16_t) = &sin16;
constexpr int16_t (*cosFunction)(uint16_t) = &cos16;
#else
using mathType = float;
using wideMathType = float;
using angleType = float;
constexpr mathType sInt16Scale = 1.0f;
constexpr mathType maxAngle = M_TWOPI / 256.0;
constexpr mathType staticRotationScale = 1.0f;
constexpr mathType animatedRotationScale = M_TWOPI / double(0xFFFF);
constexpr float (*sinFunction)(float) = &sin_t;
constexpr float (*cosFunction)(float) = &cos_t;
#endif
const bool isMatrix = strip.isMatrix;
const int cols = SEGMENT.virtualWidth();
const int rows = isMatrix ? SEGMENT.virtualHeight() : strip.getSegmentsNum();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not get it. How is this supposed to work?

getSegmentsNum() will return the size of underlying vector which will have the size of the largest encountered segment count. I.e. I create can 10 segments then delete all but one (and adjust its dimensions) and the number returned by getSegmentsNum() will return 10, not 1. Segments are not removed from vector unless low memory condition occurs.
If you need usable segments then use getActiveSegmentsNum().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not like on the fly segment changes work particularly great anyway. You can only be sure of what the will actually look like if you store the config and reboot the device. So I'm not concerned with the vector being too long. I don't care about missing segments either, those segments will just not be filled by my effect.

Using getActiveSegmentsNum() however would be incorrect, as I care for existing segments, not those that happen to be on. I also want to compare the segment ID to it, so the segment count must be greater than the segment ID. The segment count is my assumed matrix height for 2D emulation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getActiveSegments() counts valid segments, not the segments that are on. There is no function to count on segments. Valid segments are segments that have length()>0. To put it the other way: active segments are visible in UI, inactive segments are not.


const int inputShift = SEGMENT.speed;
const int inputSize = SEGMENT.intensity;
const int inputRotation = SEGMENT.custom1;
const bool inputAnimateShift = SEGMENT.check1;
const bool inputAnimateRotation = SEGMENT.check2;
const bool inputAssumeSquare = SEGMENT.check3;

const int paletteOffset = (!inputAnimateShift) ? (inputShift) : (((strip.now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8);

mathType sinTheta;
mathType cosTheta;
if (rows <= 1) {
sinTheta = 0;
cosTheta = sInt16Scale;
} else if (cols <= 1) {
sinTheta = sInt16Scale;
cosTheta = 0;
} else {
const angleType theta = (!inputAnimateRotation) ? (inputRotation * maxAngle / staticRotationScale) : (((strip.now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale);
sinTheta = sinFunction(theta);
cosTheta = cosFunction(theta);
}

const mathType maxX = std::max(1, cols-1);
const mathType maxY = std::max(1, rows-1);
const mathType maxXIn = inputAssumeSquare ? maxX : mathType(1);
const mathType maxYIn = inputAssumeSquare ? maxY : mathType(1);
const mathType maxXOut = !inputAssumeSquare ? maxX : mathType(1);
const mathType maxYOut = !inputAssumeSquare ? maxY : mathType(1);
const mathType centerX = sInt16Scale * maxXOut / mathType(2);
const mathType centerY = sInt16Scale * maxYOut / mathType(2);
const mathType scale = std::abs(cosTheta) + (std::abs(sinTheta) * maxYOut / maxXOut);
const int yFrom = isMatrix ? 0 : strip.getCurrSegmentId();
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
const int yTo = isMatrix ? maxY : yFrom;
for (int y = yFrom; y <= yTo; ++y) {
const mathType ytSinTheta = mathType((wideMathType(sinTheta) * wideMathType(y * sInt16Scale - centerY * maxYIn))/wideMathType(maxYIn * scale));
for (int x = 0; x < cols; ++x) {
const mathType xtCosTheta = mathType((wideMathType(cosTheta) * wideMathType(x * sInt16Scale - centerX * maxXIn))/wideMathType(maxXIn * scale));
const mathType sourceX = xtCosTheta + ytSinTheta + centerX;
int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * 255) / (sInt16Scale * maxXOut);
if (inputSize <= 128) {
colorIndex = (colorIndex * inputSize) / 128;
} else {
// Linear function that maps colorIndex 128=>1, 256=>9
colorIndex = ((inputSize - 112) * colorIndex) / 16;
}
colorIndex += paletteOffset;
const uint32_t color = SEGMENT.color_wheel((uint8_t)colorIndex);
if (isMatrix) {
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
SEGMENT.setPixelColorXY(x, y, color);
} else {
SEGMENT.setPixelColor(x, color);
}
}
}

return FRAMETIME;
}
static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3=0,o2=0";
static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Physical Square;;!;12;o1=1,o2=1";


// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active
Expand Down
Loading