Maths help - Asymmetric sine curve formula

Wondered if anyone could advise

Plotting

y=sin(x)

gives a traditional sine curve with a positive lobe initially for values of x = 0 to 180 degrees, with a maximum of y=1 at 90 degrees. This is followed by a negative lobe for values x from 181 to 360 degrees, with a minimum of y=-1 at 270 degrees. Betwen these two lobes the plot crosses the y axis at x=180.

How would I go about “shifting” the centre of the plot to make the lobes asymmetrical? The maximum and minimum would remain the same, and the function should still “repeat” every 360 degrees, but the position that the plot crosses the X axis should be shifted from x=180 to (for example) x = 90.

Thus the target curve has a positive lobe for values of x from 0 to 90 degrees, but the a negative lobe for values of x from 91 to 360.

Currently I can get the first “compressed” part of the curve (ie a positive lobe that spans ninety degrees) by using

y=sin(2x).

Similarly I can get an “extended” curve where each lobe spans 270 degrees with

y=sin(2x/3)

But my attempts to either “flip” it from one equation to the other using if/then statements have failed to generate a singe equation that will provide the curve (this is the preferred option) have failed.

Can anyone help?

But my attempts to either “flip” it from one equation to the other using if/then statements have failed

What code do you have now? In what way does it “fail”?

a singe equation that will provide the curve (this is the preferred option)

Why is “a single equation” preferred?

float asymSin(float x) {
   if (x < SHIFTPOINT)
     return(sin(2*x));
   else
     return(sin(0.66667*x));
}

I’d be tempted to detect zero-crossings and step through a table of factors, for more generality…

If the X "shift point" is a zero crossing, then you just use two functions, one for each part of the plot. Otherwise you have will have difficulty matching the Y values to get a continuous line.

Matlab implementation:

clear variables;
PI=3.14159265;
x = 1:361;
y= 1:361;
for ii = 1:361
    j=ii-1;
if j<91 
    y(ii)=sin(PI*2*j/180);
else
    y(ii)=-sin(PI*(2*(j-90)/3)/180);
end
end

plot(x,y);

Result: (naturally, the slopes don't match)
ugly_sine.png

ugly_sine.png

This is handy…it fails in exactly the way westfw’s code does (see image), because if you plot the curve sequentially it’s not smooth…exactly as jremington says.

It’s also why a single equation would be preferred, because I thought that would be more likely to give a sequential (ie smooth) output.

Mixed Sine output.png

To get a single equation, fit a polynomial to a set of points taken from the two part curve.

It is impossible to fit that discontinuity in the slope exactly, but that is the result of simply implementing your suggestion for how to go about this operation.

I would use exponentiation of the input to the sin function like this:

y = sin(2 * M_PI * pow(x, -log(2) / log(n)))

where n is where the sine wave should cross the zero line. Both x and n range from 0 to 1. Using your example, n = 0.25. A plain sine wave has n = 0.5.

See the example output on Wolfram Alpha.

The ends of the wave have different slopes, so if you want them the same you'd have to use a more complicated formula inside the sin function call.

How “sin shaped” do you need it to be? A little calculus would let you pick a crossover between the two functions when the slopes are equal, rather than at the zero crossings, but I can’t quite visualize what it would actually look like... d sin(2x)/dx = d sin(.66667x)/dx 2 cos(2x) = .6667 cos(.6667x) ...

Or, see other types of curves that let you specify “control points”

Many thanks for all of your suggestions.

Some clarification may help, as usual I haven’t explained myself very well.

  1. What is it for?

I use the sine waves to modify the RGB channels of LEDs, starting from a random seed, so my current code is along the lines of;

for (i=0;i<=2;i++){
RGBSin[i]=random(0,360);
}

for (p=0;p<N_LEDS,p++){
strip.setPixelColor(p, 255*sin(RGBSin[0]*PI/180),255*sin(RGBSin[1]*PI/180),255*sin(RGBSin[2]*PI/180));
}
strip.show

for (i=0;i<=2;i++){
RGBSin[i]=RGBSin[i]+1;
}

*Disclaimer - the above is hand typed “pseudo code” as I’m away from my main machine. There are other parts to the actual code like limiting the RGBSin values between 0 and 360, and correcting the sine wave so the output is between 0 and 1, not -1 and 1)

I find it gives very nice random colour transitions. However, for one particular project I need there to be just two channels active - red and green - and for the LEDS to spend (approx) three times as much time in a red state compared to the green one - hence the idea for an asymmetric function.

  1. Why do you want a single equation?

Partly because I’d tried the “if/then” route as well as some other logical functions (MAX,MIN) and seen the “non-sequential” nature of the output - but also because it just seemed more elegant, and I wondered if it was theoretically possible.

  1. How “sin shaped” do I need it to be?

Obviously the smoother the better…the eye is stupidly sensitive and a sudden change in slope will equate to a noticeable step change in brightness. But, other than that…it doesn’t HAVE to be a sine wave, a polynomial with a “1 part positive three parts negative” plot would suffice

Obviously I’ll also need it’s counterpart, three parts positive to one part negative, which will apply to the red channel, but I think that should be easy to create if I get a “method” for the first one.

Yes, there are other ways to achieve the same effect…but I’ve yet to find one quite as pleasing to the eye as this one, even accepting the limitation that sine curves have equal lobes.

Hmm. Phase modulation? http://mathforum.org/library/drmath/view/65463.html

the eye is stupidly sensitive and a sudden change in slope will equate to a noticeable step change in brightness

have you actually tried it to see how it looks? I would have thought eyes had essentially logarithmic response to brightness, and would be relatively insensitive to changes of brightness change...

the eye is stupidly sensitive and a sudden change in slope will equate to a noticeable step change in brightness

The slope is not particularly relevant and "stupidly sensitive" does not describe the actual situation.

What matters is the log change of illumination intensity. A brightness step on 8-bit PWM scale from 1/255 to 2/255 is noticeable to the eye, but 250/255 to 255/255 is not.

On the 8 bit scale, to the human eye there are only about 8 noticeably different intensity levels (maybe 16 if you push it to half f-stops), as in this log lookup table:

255, 180, 128, 90, 64, 45, 32, 23, 16, 12, 8, 6, 4, 3, 2, 1,0

What is utterly amazing about the eye is the enormous [u]range[/u] of detection, around 14 decades of intensity.

If you literally want nothing more than to shift the position, without changing the shape:

y = sin(x + shift);

if shift == 90, then the positive peak will be at 180, the negative peak at 0 and 360, and the zero crossings at 90 and 270.

if shift == -90, then the positive peak will be at 180, the negative peak at 0 and 360, and the zero crossings at 90 and 270.

Regards, Ray L.

The sin() function expects radians as the units of the argument.