color gradient from beizer curve

Started by
20 comments, last by smurfkiller 17 years, 7 months ago
I want to make a color gradient from a beizer curve. The problem is that a beizer curve is parametric where for example x = f(t), y = f(t). I need to compute a 'y' (clor value) based on 'x'. But because the curve is parametric do not have any other option than to compute the 'y' value by sampling the 'x' axis. Is there no other solution? This is verry similar to 3dsMax material editor where you can control color gradients and other functions by beizer curves.
Crush your enemies, see them driven before you, and hear the lamentations of their women!
Advertisement
I'm not sure a bezier curve is precisely the type of curve you need. But perhaps something very similar. Consider a polynomial function f(x) = a·x3 + b·x2 + c·x + d. You can evaluate this function anywhere between 0 and 1, and get a single value back (y). Two problems that we must solve, though, are that we have four unknowns (a, b, c, and d), and depending on what those values are, the result could be just about anything. But if we choose the unknowns carefully, we can restrict the range of the function when x is between 0 and 1. How do we choose values for a, b, c and d? There are a variety of ways, but here are two very useful ones:

We need four knowns of some form in order to determine the four unknowns. First, let's assume that f(0) = g0 and f(1) = g1. Basically, we are saying that when x is 0, we know that we'll get g0, the value that we chose ahead of time that will be at the very beginning of the color gradient. And the same for g1, except that it is the value of the color gradient at the very end. So this gives us two knowns.

The easy way to calculate the second two knowns would be to do essentially the same thing as above, but for values when x is 1/3 and 2/3. We could say then that f(1/3) = g1/3 and f(2/3) = g2/3. So now that we have four knowns, how do we get the four unknowns a, b, c, and d?

First, using the g constants that we have chosen, rewrite the polynomial above, with the x value filled in:

f(0) = g0 = a·(0)3 + b·(0)2 + c·(0) + d
f(1/3) = g1/3 = a·(1/3)3 + b·(1/3)2 + c·(1/3) + d
f(2/3) = g2/3 = a·(2/3)3 + b·(2/3)2 + c·(2/3) + d
f(1) = g1 = a·(1)3 + b·(1)2 + c·(1) + d

These simplify into

g0 = d
g1/3 = a·(1/27) + b·(1/9) + c·(1/3) + d
g2/3 = a·(8/27) + b·(4/9) + c·(2/3) + d
g1 = a + b + c + d

When you solve that system of equations (you can do it the basic algebraic way, or use linear algebra, whichever you're more comfortable with), you get

a = 9/2·g1 - 27/2·g2/3 + 27/2·g1/3 - 9/2·g0
b = -9/2·g1 + 18·g2/3 - 45/2·g1/3 + 9·g0
c = g1 - 9/2·g2/3 + 0·g1/3 - 11/2·g0
d = g0

So whatever you choose for g0, g1/3, g2/3, and g1, you can use those four values to determine a, b, c, and d, plug all that into f(x) = a·x3 + b·x2 + c·x + d, and then figure out what f(x) equals. The resulting gradient will be a curve that starts at g0, goes through both g1/3 and g2/3 when x is 1/3 and 2/3 respectively, and ends at g1.

The other way to determine the other two knowns is a little more creative, and requires a bit of calculus knowledge. It is more similar to bezier curves, however. Instead of saying that the curve equals g1/3 and g2/3 when x is 1/3 and 2/3 respectively, let's say that the slope of the curves equals s0 and s1 when x is 0 and 1 respectively. This will mean that we know the value and slope of the curve only at x = 0 and 1, but we don't know anything about the curve in between. We still have the first and last equations from above, because they came from our first two knowns:

f(0) = g0 = a·(0)3 + b·(0)2 + c·(0) + d
f(1) = g1 = a·(1)3 + b·(1)2 + c·(1) + d

which as above simplify to:

g0 = d
g1 = a + b + c + d

Our second two equations will come from the derivative of the polynomial, however, because it is the derivative that specifies slope.

f'(0) = s0 = 3·a·(0)2 + 2·b·(0) + c
f'(1) = s1 = 3·a·(1)2 + 2·b·(1) + c

which simplify to:

s0 = c
s1 = 3·a + 2·b + c

Solving these four equations, this time we get:

a = 2·g0 - 2·g1 + s0 + s1
b = -3·g0 + 3·g1 - 2·s0 - s1
c = s0
d = g0

So again, once you have determined g0, g1, s0, and s1, you can figure out a, b, c and d, and thus the overall equation.

So depending on if you merely want to supply four values for the curve to equal when x is 1, 1/3, 2/3, and 1, then you can use the first system. Or if you'd rather supply the values and slopes when x is 0 and 1, then you can use the second system. In both cases, the resulting value will be the color gradient, and if you chose values that make sense for the values and slopes, then the color gradient will always be in a range that makes sense as well.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Thanks alot for the reply, the first aproach is verry similar to solving the bezier functions:

x(t) = at3 + bt2 + ct + x0
x1 = x0 + c / 3
x2 = x1 + (c + b) / 3
x3 = x0 + c + b + a

where:

c = 3 (x1 - x0)
b = 3 (x2 - x1) - c
a = x3 - x0 - c - b

I will try the second aproach with the slopes.



Crush your enemies, see them driven before you, and hear the lamentations of their women!
I found out that this aproach is not exactly what im after. The problem is that the bezier curve is given in parametric space if you look at the example below you see that the gradient for a bezier curve is given as y = f(x):
description of your image
This basicaly means that i need to express y = f(x), where y = f(t) and x = f(t). Maybe someone have an idea how they do that i 3dsmax. I think they are sampling the 'x axis' for some values of parameter 't' but that wont work for my purpose.
Crush your enemies, see them driven before you, and hear the lamentations of their women!
Quote:Original post by smurfkiller
Maybe someone have an idea how they do that i 3dsmax. I think they are sampling the 'x axis' for some values of parameter 't' but that wont work for my purpose.
I'm just jumping in here without having read the whole thread, but if you need y(x) I think you should be able to get it as follows.

1. Derive t(x) from x(t) by solving x(t) for t
2. Plug t(x) into y(t) to yield y(t(x)), which gives you the y(x) you're looking for

I think the degree of t(x) will be the same as that of the curve, so you'll need a cubic solver of some sort.

I didn't test this or work through it in detail, so it may or may not be correct.
I think that is very complicated since the solution leads to three roots where two of them can be complex.
Crush your enemies, see them driven before you, and hear the lamentations of their women!
Quote:Original post by smurfkiller
I think that is very complicated since the solution leads to three roots where two of them can be complex.
Well, yeah, but that's exactly what you get if you solve a 2D cubic curve for one coordinate in terms of another. Isn't that what you want to do? Or maybe I'm missing something...
No its not posible, its like trying to express a parametric curve in none parametric form. Unless its something im missing?
For example circle: x = r*cos(t), y = r*sin(t) if you try to express y = f(x) you get a function where there are two values for a given x value, so you have to split it in y = ±f(x). The question is how do i plug in the solution of tree roots into one equation?
Crush your enemies, see them driven before you, and hear the lamentations of their women!
Quote:Original post by smurfkiller
No its not posible, its like trying to express a parametric curve in none parametric form. Unless its something im missing?
For example circle: x = r*cos(t), y = r*sin(t) if you try to express y = f(x) you get a function where there are two values for a given x value, so you have to split it in y = ±f(x). The question is how do i plug in the solution of tree roots into one equation?
Well, I'm not absolutely certain that my solution is correct, but I'm fairly certain. I'll try to clarify a bit.

In general, if you solve a cubic curve for y in terms of x or vice versa, you get a cubic equation, the solution of which has either zero, one (possibly tangent), two (one tangent), or three real roots. If you draw some different cubic curves it should become apparent why this is so. This is somewhat similar to your circle example where one x corresponds to (possibly) two values of y.

Your particular example has some constraints such that, theoretically, there should only ever be one real root. Nevertheless, to find the solution I think you'll still need to solve a cubic equation. Maybe there's a way around it, but if so it's not occurring to me.
Actually, any cubic equation with real coefficients is guaranteed to have a real root. To visualise this (suppose y = y(x)), observe that as x tends to an infinity, so must y (though not necessarily with the same sign). Hence by the Intermidiate Value Theorem, every such cubic must cross the x-axis. The other roots may or may not be real.

Determining y(s) = y(x^(-1)(s)) seems like a good idea, and it works fine so long as x is invertible, but the Inverse Function Theorem (let's see if I can name any more theorems [rolleyes]) demands that x(s) be strictly increasing/decreasing, or equivalently, has two complex roots.
Since there are no constraints on Bezier splines (unlike B-Splines) restricting the nature of the roots (within the given interval), this method isn't guaranteed to succeed. So in general, it fails.

The reason we can't simply use the real root that we know exists is that we may end up with nasty discontinuities and bifurcations, which will be unstable depending on what method you use to extract the polynomial's roots. This will manifest itself as potential sudden colour changes as you cross a node.

This situation could be avoided if it were possible to determine the root that exists at all times and keep track of it in each section of the spline, but with floating-point error this is unreliable.

Certainly, better method exist to determine the colour of the spline than attempting to eliminate the Bezier parameter. If you tell us what the colour of the spline is supposed to represent, we may be able to suggest a better alternative.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.

This topic is closed to new replies.

Advertisement