## Pond ripple effect

13 posts / 0 new
Author
Message

I hope I can explain this correctly...

A colleague has built a lighting installation. It is basically 24 Rings concentric rings of Led-tape. (5050-type RGB LEDs)

Each whole ring lights with one colour.

Each whole ring is connected to a DMX controller, with 3 Channels (one each for R, G, B)

The DMX controllers take DMX512 and output  PWM for the RGB leds.

So any ring, can be any colour. Each ring's colours is 3 channels of DMX, so (24 * 3) = 72 channels of DMX for the whole thing.

This is all controlled by a ATMEGA3208 which outputs the DMX to the controller.

So far so good. This all works great. I wrote the code to output properly framed DMX512 from the ATMEGA.

Refresh rate is about 10 times per second.

Enhancement

Now they wish to choose a starting colour (sent to the ATMEGA over the second UART) and a switch.

When a new start colour is sent, all rings go to that colour immediately. (No problem, already working).

Whenever the switch is pushed the rings of light should change from <starting colour> to white over a few seconds.

But they should not just fade from <starting colour> to white. The colours should ripple from the edge to the centre like ripples on a pond.

Now I'm fine with the electronics and DMX etc, but I'm not a graphics genius, or a gifted mathematician!

So, any suggestions on a good way to achieve this "ripple" effect on the colours?

Should I use a SIN function, or maybe a lookup table?

Should I calculate a basic fade from <starting colour> to white and then iterate over it several times, with some form of transform function to generate the ripple?

How would you achieve it? Any suggestions?

SpiderKenny
@spiderelectron
www.spider-e.com

Instead of a ripply, how about a gradient?  So at the beginning, the edge is dim white & it pushes it's way to the center, with the incoming edge getting whiter & whiter.   Eventually the whole thing is full white.

You could also maybe slowly ramp up from dim white to full white.  As you do this, quickly start setting random rings to the current white setting...it will fill in with whiter & whiter values until all rings are filled with the full white value (with a final forcing to fill in any stragglers).  This might give a sparkling effect.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

https://www.quora.com/What-is-the-equation-describing-a-ripple-in-water

which comes from  y=Aeâˆ’btcos(wt+Ï•)

EDIT:  https://www.youtube.com/watch?v=JrzgE7p-xnU (which is cool to watch!) mentions:  z=(cos( 0.5sqrt(x^2+y^2)-6n)/(0.5(x^2+y^2)+1+2n), n={0...10}

Actually who remembers the Acorn Atom home computer from the mid 1980's? One of the most amazing things (at the time) was a relatively simple BASIC program in the manual that drew a 3D ripple.

    1 REM Three-Dimensional Plotting
50 L=30;M=40;N=8
110 Z=0;CLEAR4
120 A=#8000;B=#9800
130 FORJ=A TO B STEP4;!J=-1;N.
150 S=L*L+M*M;GOS.s;R=Q
160 S=S+N*N;GOS.s;S=L*L+M*M
170 T=L*L+M*M+N*N
200 F.U=-20TO20
210 V=-20;GOS.c;GOS.b
220 F.V=-19TO20;GOS.c;GOS.a;N.;N.
230 END
400sQ=S/2
410 DOQ=(Q+S/Q)/2
415 U.(Q-1)*(Q-1)<S AND(Q+1)*(Q+1)>S
420 R.
500 REM DRAWTO(U,V,W)
510aZ=3
520k>O=T-U*L-V*M-W*N
530 C=T*(V*L-U*M)*4/(R*O)+128
540 D=96+3*Q*(W*S-N*(U*L+V*M))/(R*O)
560 PLOT(Z+4),C,D;Z=0;R.
600cW=300/(10+U*U+V*V)-10;R.

But I think the key thing is the description:

The program below plots a perspective view of the curve
1/(1+x^2+y^2) for a range of values of x and y.

However this is just a static image of the wave at one point. It clearly needs something like the cos() input of the functions above to be "cyclic".

Last Edited: Mon. Jul 1, 2019 - 09:27 AM

Thanks @clawson

This looks promising: so thanks for the link.

z=(cos( 0.5sqrt(x^2+y^2)-6n)/(0.5(x^2+y^2)+1+2n), n={0...10}

I might experiment with that, and see if the 3208 can handle calculating that for the  72 channels at a reasonable pace.

If not, I might reduce it to a look up table or something.

Thanks.

SpiderKenny
@spiderelectron
www.spider-e.com

Also, keeping in mid that each 'ring' is always the same colour all the way round,

I don't need X and Y in the equation, just one of them. That should simplify things.

Basically like drawing a line across the image in the YT link you sent, from the circumference to the centre, and using the maths for that one line.

SpiderKenny
@spiderelectron
www.spider-e.com

SpiderKenny wrote:
see if the 3208 can handle calculating that
I wouldn't suggest calculating things like that at run time. Unless you are flash-starved pre-claculate a results table and use an int index into it.

PS I spent about a minute googling above formulae - I'm sure that spending a bit more time sifting results might get something even more "usable".

Last Edited: Mon. Jul 1, 2019 - 09:54 AM

My recollection was that the wave would be a

Bessel function, but I just consulted Wikipedia

and they're really complicated!  Clawson's

formula should look good.

--Mike

Thanks avr-mike

Claswson's formula calculates a z-height in 3D space.

This "height" (Which I'll probably scale 0 to 1) will be the amount of offset to apply to each LED in the current fade from <start colour> to white.

Then, I'll shift the index by one, and calculate the next fade level, apply the offsets again and so on - thus moving the ripple towards the centre, at the same time as fading towards white.

SpiderKenny
@spiderelectron
www.spider-e.com

Based on your problem, I did this in Excel, hope it helps. It's just the product of an exponential decay and a (shifted and rescaled) cosine function, you can put it in a lookup table and use it to mix the old and new RGB values, it should produce the ripple effect. Parameters are adjustable in the spreadsheet.

## Attachment(s):

El Tangas wrote:

Based on your problem, I did this in Excel, hope it helps. It's just the product of an exponential decay and a (shifted and rescaled) cosine function, you can put it in a lookup table and use it to mix the old and new RGB values, it should produce the ripple effect. Parameters are adjustable in the spreadsheet.

Fab! Thanks so much. That is very much appreciated.

SpiderKenny
@spiderelectron
www.spider-e.com

I've worked with the WS2812s a lot.  You definitely don't need any exotic math functions, just simple straight-line ramping.  I won't try and draw a picture, but this should make it clear:

Start at 255

Ramp down to 0

Ramp up to N1*255 (N1 < 1)

Ramp down to 0

Ramp up to N2*255 (N2 < N1)

Ramp down to 0

Ramp up to N3*255 (N3 < N2)

Continue as many cycles as desired

The main point I'm making is that straight-line ramping is fine for any fading effects.  If you really want more of a roller-coaster effect, break the ramps into multiple ramps with different slopes.  The key to ramping is that it is like a line-drawing algorithm, where you have to account for accumulated error.  Examples below

If you want to ramp from 100 to 116 in 4 steps, that's easy: 100-104-108-112-116

But if you want to ramp from 100 to 115 in 4 steps, you'll have to "slip a cog" occasionally to end up at your desired end point (again, just like a line-drawing algorithm): 100-104-108-111-115 (the "cog-slip" here is 108-111).

I've got code I use for this, I'll see if I can dig it up.

Here is my version of ramping (hoping it's bug-free, I extracted it from a larger code section)

typedef uint8_t u8;
typedef int8_t  s8;

typedef struct
{
u8 num_ticks;            // #ticks in this ramp
u8 tick_cnt;             // current tick in num_ticks
u8 delta;                // total amount to ramp
u8 base_step;            // delta/num_ticks
u8 rem;                  // remainder
s8 acc;                  // ramping accumulator, init to 0
s8 sign;                 // direction to ramp, +1 or -1
u8 brightness;           // current ramp value
} RAMP;

.....
// perform one ramp step

RAMP * pr = &R[led];     // get the ramp we're computing

u8 step = pr->base_step; // how much we'll step this time
pr->acc -= pr->rem;
if (pr->acc < 0)         // we "slipped a cog", so adjust
{
step++;              // step +1 this time
pr->acc += pr->num_ticks;  // reset acc
}
step *= pr->sign;        // adjust step direction
pr->brightness += step;  // ramp current brightness by step

.....
// next, convert brightness into RGB values for current color mapping
.....

// now advance to next tick, and calc new ramp if done with this ramp

if (--pr->tick_cnt == 0)  // reached end of ramp
{
.....
// calculate new ramp delta, num_ticks, sign
.....
// now load new values into RAMP struct

div_t step_info = div(pr->delta, pr->num_ticks);
pr->base_step = step_info.quot;
pr->rem = step_info.rem;
pr->acc = 0;
pr->tick_cnt = pr->num_ticks;
}


Last Edited: Mon. Jul 1, 2019 - 04:08 PM

Being bored, I'll run through the 100-to-115-in-4-steps example using the posted code.

div(15, 4) gives QUOT = 3, REM = 3

acc is initially 0

STEP 1: step = 3; acc - REM = -3; increment step to 4; acc = -3+4 = 1

STEP 2: step = 3; acc - REM = -2; increment step to 4; acc = -2+4 = 2

STEP 3: step = 3; acc - REM = -1; increment step to 4; acc = -1+4 = 3

STEP 4: step = 3; acc - REM = 0; step remains at 3; acc = 0  // here the 4-step cycle starts all over again

Note that this yields 100-104-108-112-115.  The "cog slip happened in the last step.  Where it happens doesn't matter as long as it happens, and happens in the same relative location each pass through the 4-step cycle.