How do you use Bézier curves for envelopes, waves, yadda yadda?
-
- KVRAF
- Topic Starter
- 2279 posts since 20 Dec, 2002 from The Benighted States of Trumpistan
Short version: How do you code a curve you've drawn into C++?
Long version: I love cubic Bézier curves; they're subtle, beautiful, and much easier to shape than stringing together a bunch of math functions. If you keep everything within ±1000, as is the rule in PostScript fonts, you just draw your curve, move the decimal point one place to the left, and you've got three very usable decimal digits that fit perfectly into the ±1.000 range. Unfortunately, transforming these lovely curves into functions is a bit of a mystery... straight lines are easy (just some if-statements), but how about the actual curves themselves?
The Wikipedia article gives the equation
B(t) = (1-t)^3*P0 + 3(1-t)^2t*P1 + 3(1-t)^2*P2 + t^3*P3,
wherein P0 is the starting point, P1 and P2 are controls, and P3 is the end; and t is in the set of 0 and 1. How do you know when to use 0 or 1? Using 0 would simply turn the whole equation into 0, so that's probably not normal. Also, all four points have two values each (x and y), but the equation seems to use only one; do you run this twice, then, once for x and once for y?
Long version: I love cubic Bézier curves; they're subtle, beautiful, and much easier to shape than stringing together a bunch of math functions. If you keep everything within ±1000, as is the rule in PostScript fonts, you just draw your curve, move the decimal point one place to the left, and you've got three very usable decimal digits that fit perfectly into the ±1.000 range. Unfortunately, transforming these lovely curves into functions is a bit of a mystery... straight lines are easy (just some if-statements), but how about the actual curves themselves?
The Wikipedia article gives the equation
B(t) = (1-t)^3*P0 + 3(1-t)^2t*P1 + 3(1-t)^2*P2 + t^3*P3,
wherein P0 is the starting point, P1 and P2 are controls, and P3 is the end; and t is in the set of 0 and 1. How do you know when to use 0 or 1? Using 0 would simply turn the whole equation into 0, so that's probably not normal. Also, all four points have two values each (x and y), but the equation seems to use only one; do you run this twice, then, once for x and once for y?
Wait... loot _then_ burn? D'oh!
-
- KVRAF
- 8388 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
t is now close to are to P0 or P3
B(0) = P0
B(1) = P3
anything else is how far along the curve you want to be. If used in animation, if your motion followed the curve for 10 seconds, and you want 25fps, then that's 250 frames/values of t, one for each frame.
You could see t as a smoothness parameter almost.
So to get an algo, you expand the equation you quoted, such that you have
x = [(1-t)^3 * P0.x] + [3(1-t)^2t * P1.x] ..etc and
y = [(1-t)^3 * P0.y] + [3(1-t)^2t * P1.y] ..etc
that will give you co-ords for each value of t you give. The resolution of the spacing between successive values of t is your choice
If you want to iterate along the x axis plotting ys, then you have work to do: I'll let you think about it first
HTH
DSP
B(0) = P0
B(1) = P3
anything else is how far along the curve you want to be. If used in animation, if your motion followed the curve for 10 seconds, and you want 25fps, then that's 250 frames/values of t, one for each frame.
Code: Select all
t=0;
LOOP 0 -> 250
image.MOVETO(B(t));
t = t + (1/250);
WAIT (0.04 sec);
So to get an algo, you expand the equation you quoted, such that you have
x = [(1-t)^3 * P0.x] + [3(1-t)^2t * P1.x] ..etc and
y = [(1-t)^3 * P0.y] + [3(1-t)^2t * P1.y] ..etc
that will give you co-ords for each value of t you give. The resolution of the spacing between successive values of t is your choice
If you want to iterate along the x axis plotting ys, then you have work to do: I'll let you think about it first
HTH
DSP
-
- KVRAF
- Topic Starter
- 2279 posts since 20 Dec, 2002 from The Benighted States of Trumpistan
Yikes -- that much recursion at sampleRate just can't be a good thing. Nice job of filling in the blanks, btw.duncanparsons wrote:If you want to iterate along the x axis plotting ys, then you have work to do: I'll let you think about it first
Drat -- yet another idea for dynamically changing a transfer curve has led me nowhere. I really thought I had something workable, too: I was going to have the control points move in response to the last sample's output. Ah well, back to the drawing board...
Wait... loot _then_ burn? D'oh!
-
- KVRAF
- 8388 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
not that hard, actually. If you use Newton/Raphson, you can solve t for a given x in very few iterations. If done wisely, one could calc t for every nth x, get the y, then interpolate inbetween.
On a slightly related note, have you ever looked at the Douglas Peucker algorithm. I haven't quite worked out where I want to use in a VST yet, but the potential for sample reduction and FSU is awesome!
On a slightly related note, have you ever looked at the Douglas Peucker algorithm. I haven't quite worked out where I want to use in a VST yet, but the potential for sample reduction and FSU is awesome!
-
- KVRian
- 1153 posts since 10 Dec, 2003
You can just use a 1D bezier. Basicly each dimension is a function in terms of 't', where t is the distance along the curve, ranging from [0..1]
So instead of
x = f(t)
y = f(t)
You just do
x = t
y = f(t)
which simplifies to
y = f(x), where x is in the same range as t, [0..1]
So..
y(x) = (1-x)^3*Y0 + 3(1-x)^2x*Y1 + 3(1-x)x^2*Y2 + x^3*Y3
It basicaly works out the same as a 2 dimensional bezier with the control points for X set at [0, 1/3, 2/3, 1], which makes x a linear function which directly maps to t.
So instead of
x = f(t)
y = f(t)
You just do
x = t
y = f(t)
which simplifies to
y = f(x), where x is in the same range as t, [0..1]
So..
y(x) = (1-x)^3*Y0 + 3(1-x)^2x*Y1 + 3(1-x)x^2*Y2 + x^3*Y3
It basicaly works out the same as a 2 dimensional bezier with the control points for X set at [0, 1/3, 2/3, 1], which makes x a linear function which directly maps to t.
-
- KVRAF
- 8388 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
-
- KVRAF
- 8388 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
-
- KVRAF
- 1607 posts since 12 Apr, 2002
What I meant to say, exponential envelopes would sound more natural. What can you do with Bezier (from the things that you want to do for the envelopes, that is), thank you cannot do with exponential (1st or 2nd order)?duncanparsons wrote:? I wouldn't use them for envelopes, unless pan envelopes or somesuch. There's a wealth of things they could be used for tho, you just have to find them
Regards,
{Z}
-
- KVRian
- 626 posts since 29 Jul, 2003 from Paris - France
What do you mean by 2nd order exponential?Z1202 wrote:What I meant to say, exponential envelopes would sound more natural. What can you do with Bezier (from the things that you want to do for the envelopes, that is), thank you cannot do with exponential (1st or 2nd order)?
Regards,
{Z}
-
- KVRAF
- 1607 posts since 12 Apr, 2002
Step response of a 2nd order nonresonating filter. Somewhat exotic for an envelope, I guess, but possible.mdsp wrote:What do you mean by 2nd order exponential?Z1202 wrote:What I meant to say, exponential envelopes would sound more natural. What can you do with Bezier (from the things that you want to do for the envelopes, that is), thank you cannot do with exponential (1st or 2nd order)?
Regards,
{Z}
Regards,
{Z}
-
- KVRian
- 626 posts since 29 Jul, 2003 from Paris - France
ah I thought I had missed something...Z1202 wrote:Step response of a 2nd order nonresonating filter. Somewhat exotic for an envelope, I guess, but possible.
Here's what I found though googling for 2nd order exponentials.
http://demonstrations.wolfram.com/Conse ... tialDecay/
Nothing new, simple but effective. A good physically smooth and natural Attack/Decay envelope as an alternative to traditionnal synth enveloppes.
-
- KVRAF
- 1607 posts since 12 Apr, 2002
Looks more like impulse responses.mdsp wrote:ah I thought I had missed something...Z1202 wrote:Step response of a 2nd order nonresonating filter. Somewhat exotic for an envelope, I guess, but possible.
Here's what I found though googling for 2nd order exponentials.
http://demonstrations.wolfram.com/Conse ... tialDecay/
Nothing new, simple but effective. A good physically smooth and natural Attack/Decay envelope as an alternative to traditionnal synth enveloppes.
-
- KVRAF
- Topic Starter
- 2279 posts since 20 Dec, 2002 from The Benighted States of Trumpistan
Probably not for envelopes, no, but for other things. I'm looking for a way to create dynamic, tubelike saturation, which means transfer functions that change their shapes, and I'm working with what I know. I've seen differential equations from afar, but my math only went up to calculus, and that was back in the late '80s. I've tried writing compound functions, but I lack the math to eliminate discontinuities in every case. Cubic Béziers, however, which I use in designing fonts, are easy to visualize, easy to manipulate (with a mouse, at least), always continuous, and quite hip.Z1202 wrote:Why in the world would one use Bezier curves and not exponential segments for envelopes?
Basically, I need something with a slow rise from zero up to a certain point, then a rapid rise to a softish clip. The catch is that I want the rise and the hardness of the clipping to vary with what's come before. Béziers would work beautifully -- move the control points by a fraction of the previous sample's output.
For an example, here's what I was going to try as an experiment. In the positive phase, the curve goes from 0,0 to 700,700, with curves at 557,52 and 370,700 (a straight connection from the final point). Err, 0.700 and all that -- I'm thinking of drawing PostScript fonts. Multiply the previous sample's output by 0.1, and rotate the control points so that they're that many units (err, tenths) closer to the upper left-hand of the graph -- a faster, sharper curve.
*Shrug* Beyond my capabilities, alas. Maybe I'll play with manipulating sigmoids instead; their slopes are easy to vary.
Wait... loot _then_ burn? D'oh!
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4292 posts since 8 Mar, 2004 from Berlin, Germany
i semi-recently derived some stuff that might be useful to put this 'consecutive exponential decay' to work:mdsp wrote:ah I thought I had missed something...Z1202 wrote:Step response of a 2nd order nonresonating filter. Somewhat exotic for an envelope, I guess, but possible.
Here's what I found though googling for 2nd order exponentials.
http://demonstrations.wolfram.com/Conse ... tialDecay/
Nothing new, simple but effective. A good physically smooth and natural Attack/Decay envelope as an alternative to traditionnal synth enveloppes.
www.rs-met.com/documents/notes/AttackDecayEnvelope.pdf
basically, this could also be implemented as one 2nd order filter with two real poles - and the (whole) envelope would then be the impulse-response of this filter. but i think Z was talking about a 2nd order filter with a complex pole pair and then we would use its step-response for segments? rather exotic indeed. did anyone actually try this? does it sound good?