Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » EasyToUse Dynamic Color Gradiant Generator

This thread is locked; no one can reply to it. rss feed Print
EasyToUse Dynamic Color Gradiant Generator
Dennis
Member #1,090
July 2003
avatar

Martin: I wanted to edit my post, but now that you already answered, i'll just post what i wanted to add to my previous one;))
(i'll look at your picture and read yours afterwards)

Time to clean up the mess, i created:
From your point of view(which i can see now) there are several things i said that are wrong for that point of view.
More specifically:
-"for each pixel along the width there's exactly one function value" (true, but there can be more than one pixel required to correctly render a closed curve(which this programm does not do)
-"it has got nothing to do with the way it is implemented in my code" (wrong, it HAS got sth. to do with the way it is implemented)
-Confusional: I mixed up x function values with x-pixel position sometimes.
-Confusional: You were also mixing these up by mixing points with pixels, which i later adopted in my first sketch, leading to even more confusion.
That lead to: When you were talking about points on curve, you really meant all pixel positions that would render to be set on the curve, while i was mixing it up with just every [x,y] function value point pair, ignoring any "vertically next to each other"-pixels, not seeing that those pixels are the important detail here.
-"num_points_on_curve==width, always", (wrong, true if function values was used instead of points)

Now i understand that my (width*height)*width distance calculations would still be wrong, because all your green pixels would be ignored in it(regardless if the were to be rendered or not).
So my algorithm is a little inaccurate(but i think it'll only be visible, if using a unit of very few pixels(<5 or so) but still right in its' general idea,
while your algorithm could be accurate but might be wrong in its' general idea, because the premise could be wrong as i pointed out above and also you did not answer my other question.

Please verify the following:
To make my algorithm 100% accurate all i'd need to do is calculate straight lines from every [x,y]pixel i already have to the following [x+1,y] pixel and then store all points along that line as additional "pixels"_on_curve.
In distance calculations also check the distances to these additional points.(That will be very slow indeed and as said above probably not affect the result much on units>5 pixels or so.)

Those assumptions about how the units would (positively) affect inaccuracy are just rough guesses for a simple x*x. Of course it depends a lot on the f'(x)(the higher that, the less the accuracy of the unoptimized algorithm) values in every point.

to make sure the fun aspect of the whole thing is not forgotten :)
Heres two -x^3 laid onto each other(first attracts, second absorbs) embedded in a simpler non polynomial gradient:
http://homepages.compuserve.de/DennisTapier/allegroforum/fungrad.png

init_ca_list3(&test3);
add_to_ca_list3(&test3,CA_POINT,CA_ATTRACT,1.0,1.0,0,0, 200,200,0, 500,NULL);
add_to_ca_list3(&test3,CA_POINT,CA_ATTRACT,1.0,1.0,639,479,0,200,0,500,NULL);
add_to_ca_list3(&test3,CA_POINT,CA_ATTRACT,1.0,1.0,320,240,0,0,200,500,NULL);
add_to_ca_list3(&test3,CA_GLOBAL,CA_ATTRACT,1.0,1.0,40,0,255,255,255,0,NULL); 
add_to_ca_list3(&test3,CA_POLYNOMIAL,CA_ATTRACT,1.0,1.0,0,0,255,200,0,200,
                make_polynomial(3,320,320,240,-1.0,0,0,0));
add_to_ca_list3(&test3,CA_POLYNOMIAL,CA_ABSORB,1.0,1.0,0,0,0,255,128,100,
                make_polynomial(3,320,320,240,-1.0,0,0,0)); 
render_col_grad_to_bitmap3(backb,&test3); /* backb must be a valid 32bpp bitmap 640x480 in dimensions */
save_tga("./fun.tga",backb,NULL);
kill_ca_list3(&test3);

[EDIT]

Martin said:

(unmathematically : in a very close place around a point the function value is nearly equal to the tangent. Since the tangent is not at right angle to the distance, you get closer to A by moving right over it, so by moving right a bit on the curve you also get closer to point A) the closest point then lies between those two - B. I understand that's not a mathematical proof, but I think one could base the proof upon this idea.

That is clear but there is a flaw in that argument. Your point "a" is very close to the peak P, while my point "a" is far away, you would have to put your point way off above your image, if you were to assume for it the same position as my point "a".(You can't just zoom in on the peak and move "a". You have to have "a" stay where it was and thus it won't be visible on the area you zoomed in.) Also i'm sure that your point "a" would NOT lay inside my area "A". It would more likely be to the right of it, so the closest point to that would indeed not be at the peak but i did never say so. It only applies for points in Area "A".

[[EDIT 2]
Trezker, that looks nice.:)

Martin Cerny
Member #3,931
October 2003

DB said:

That is clear but there is a flaw in that argument.

Not true - the image is not important, however it's scaled or points moved, the premise for the argument is that tangent is not at right angle to the distance. I though about it a bit and I think I could make serious mathematical proof of the fact that if the tangent is not at right angle to the distance, then there is a point on the curve, which is closer. Hence the only situation when there is no point closer is when the tangent is at right angle to distance.

Baba
---------------
Smile, it's gonna be worse.

da_flo
Member #1,907
February 2002
avatar

Quote:

I though about it a bit and I think I could make serious mathematical proof of the fact that if the tangent is not at right angle to the distance, then there is a point on the curve, which is closer. Hence the only situation when there is no point closer is when the tangent is at right angle to distance.

Proving that The closest distance of a point to a curve is always at right angle to the tangent at the closest point of the curve is quite easy. I made it in a few minutes this morning, just let me some time to write it in english and upload it. ;)

Dennis
Member #1,090
July 2003
avatar

Martin said:

Not true - the image is not important, however it's scaled or points moved, the premise for the argument is that tangent is not at right angle to the distance. I though about it a bit and I think I could make serious mathematical proof of the fact that if the tangent is not at right angle to the distance, then there is a point on the curve, which is closer.

No need to prove it. I reconsidered my drawing to be totally wrong. I can see that myself now. Your argument with the circle around "a" that will intersect with the curve another time has opened my eyes now.
You're right.:)

[edit]
Still i have no idea which algorithm will be better.:(

[edit2]
Still your point is not lying in my area A but that's not your fault. It's mine, 'cause if properly calculated one would find that my area A is indeed empty;D. And the way i wildly constructed that supposed area A in my picture is complete and utter nonsense, as for every point in that area i can find another point on the curve that is closer even if i don't calculate it and just use your described method with the circle. My Math-FU is weak. You win.:)

Martin Cerny
Member #3,931
October 2003

Quote:

Still i have no idea which algorithm will be better.

The simplest way would be to try. When you finish the object version, I can easily extend your object for polynomial attractor and override it's distance method and constuctor and then we will be able to do some FPS checks.

EDIT: Ain't it funny we spent a very big part of this thread arguing about such an unimportant minor thing? And nearly noone interrupted us.;D

Baba
---------------
Smile, it's gonna be worse.

Dennis
Member #1,090
July 2003
avatar

I edited while you posted.

I don't know when the object version will be done, i wanted to start today but i spend the entire day arguing with you.;)

Martin said:

Ain't it funny we spent a very big part of this thread arguing about such an unimportant minor thing? And nearly noone interrupted us.;D

Funny yes, unimportant no. You did a good job defending your algorithm and proved me wrong several times, which is good, because otherwise i would have never even bothered to think of your method as a considerable alternative.:)

da_flo
Member #1,907
February 2002
avatar

Quote:

No need to prove it.

Well, I went through all the hassle writing it and scanning it while you answered, so I'm posting it anyway. :P

Sorry for the hand-written proof, that was the easier way for me do to that. I hope it's clear and readable enough. I'm not used to writing maths in english. ;D

Well, you probably don't care, but here it is. By the way, I followed the thread by didn't read all the explanations, so if you want a third point of view, tell me and I'll try to go deep into it. ;)

Dennis
Member #1,090
July 2003
avatar

da_flo:
I'd still like a third party opinion but not on the general math, as Martin already showed me that i was wrong with my last drawing(and with some other arguments) but it would be great if you could dig into it and answer the questions regarding the algorithms that are still left unanswered.:)

Martin Cerny
Member #3,931
October 2003

da_flo: you seem to better in math than I am, so I'll try to explain my ideas with calulating the distance directly - maybe you could find a way out:

notation: f'(x) is a derivative of function f(x) //hope derivative is the right term
[a,b] is point with x-coordinate a and y-coordinate b
if A is a point, than A1 is it's x coordinate and A2 it's y coordinate

I tried two ways to calculate the distance - first I tried to qualify the function of distance from point A to a point on the curve according to it's x coordinate d(x) and then find a minimum through derivation. That leads quickly to problems, because for polynomial of n-th degree d(x) is of 2n-th degree and so it's derivation (which you have to solve) is of (2n-1)-th degree.
The other way was similar to my algorithm - assuming that the distance lies on a normal to tangent you find all those points on the curve and then calculate their distances and compare them.
We evaluate the vector of a tangent in a point
v = (1,f'(x))
and the 'distance' vector u = [x,f(x)] - A = (x - A1, f(x) - A2)
and then compare the dot product (hope that's the right term) u.v to zero.
u.v = x - A1 + f'(x)*f(x) - A2*(f'x)
but since f'(x)*f(x) is of (2n-1)th degree we are in the same trouble :(
I tried one other method but it led to exactly the same equation -
for f(x)= a*x^2 + b*x +c the result was:
2*a^2*x^3 + 3*a*b*x^2 + (2*a*c - 2*a*A2 + 1)*x + b*c - A1 - b*A2
which I wasn't able to solve without using the formula for 3rd degree equations which is a bit difficult to count in hand.

Other idea was to start with x=A1 and calculate the derivation f'(x) and move 'left' (substract a little number from x) if (f'(x) > 0) && (f(x) > A1) or (f'(x) < 0) && (f(x) < A1) and move 'right' otherwise. And continue doing this until the distance |A,[x,f(x)]| will stop getting lower. Thus you could get quite good result within good time.

Hope everything was clear enough. I can't figure out anything better now.

Baba
---------------
Smile, it's gonna be worse.

Richard Phipps
Member #1,632
November 2001
avatar

Just checked the program out, looks great! This could come in very useful for some menu backgrounds. :D

Dennis
Member #1,090
July 2003
avatar

Thanks:), that's one of the things i also had in mind for the generator. I hope you did not miss that the latest version3 has a small inconsistency in it for the polynomials.
Inside "make_plut" you must replace the calculation with this one to have it compute faster and also to have y growing downwards:

/* using "Horner Scheme" to compute function value */
   cy=PN->co[PN->he]*cx;
   for(j=(int)PN->he-1; j>0; j--)
     cy = (PN->co[j] + cy)*cx;
   cy = cy + PN->co[0];

[edit]
related posts:
Miran's latest version with GUI,[url http://www.allegro.cc/forums/view_thread.php?_id=518074#target]
version3 with all *fixes*(the attachment of that post)[/url]

da_flo
Member #1,907
February 2002
avatar

Okay, I tried to look at it, Martin.

I didn't spend much time, and didn't find any efficient way to do it.
In fact, solving the polynomial equation of degree (2n-1) seems almost the best solution. For each point, you then have to solve an equation, but with some methods it can be done with very few iterations, depending on the accuray wanted. For example, the Newton-Raphson method is quite simple and gives very good and quick results in most cases. But in some particuliar cases (for example because of the function's minima), it can have a chaotic behaviour. See the newton fractals shown at the bottom of the wikipedia page. I didn't read the article, but they should explain the practical problems of the method.

So I really don't know how to do. I'm not really used to numerical computing. I'm learning rather theorical maths, not engineering maths. ;) (and besides I still have a lot to learn... ::))

Dennis' suggestion of computing the distance to SCREEN_W points of the curve and taking the minimum is quite heavy, and is still an approximation (and using as many points as the screen width is quite arbitrary anyway. It's only quite accurate because SCREEN_W is big enough). Besides, if we want to be rigorous, we have to consider a part of the polynomial which is outside the screen, otherwise we could have some strange effects near the screen borders for some polynomials.

As a conclusion, there seems to be no fast way to do it. If we want to keep the graphical fun (as Dennis said), we should forget the mathematical aspect a bit.

I'll try to play with Dennis' code and add some accurate distance computation, regardless of speed. I'll post screenshots of the 2 versions, to see if the difference is really worth it. ;)

-----------------------------------------------------------------------------------------------

I was also thinking of a new kind of attractors/absorbers : You could use circles. :)

There the distance will be easy to compute. Let's assume you have a circle of radius R.

If d is the distance from the point to the center of the circle, then the distance to the circle is abs(d-R). :)

I'll try to test it if I can, but if you feel like adding it yourself, Dennis, don't hesitate.

miran
Member #2,407
June 2002

Quote:

You could use circles.

Good idea. That will make nice rings :)

--
sig used to be here

Dennis
Member #1,090
July 2003
avatar

da_flo said:

I'll try to test it if I can, but if you feel like adding it yourself, Dennis, don't hesitate.

I will not add any features/attractors until i have ported the entire old version3 to CPP using proper class hierarchy, as this will make adding new attractor types easier in the future. Circles are a good idea.(i already mentioned it on page2 of this thread;))

Now i got to read the rest of all post offline, because my online bill is exploding(already 67€ only for this month...yeah dial up sucks:-/).

da_flo
Member #1,907
February 2002
avatar

Quote:

(i already mentioned it on page2 of this thread;))

Oooops. ;D

Also, we might have to types of circles : circles (like the ones I've describes), giving rings, and "plain circles" where the distance is d-R if d > R, and 0 otherwise. That could give nice gradients, like sun effects or other things...

Off-topic :

That's my 666th post. I'm doomed. :o

;D

Dennis
Member #1,090
July 2003
avatar

da_flo said:

and "plain circles" where the distance is d-R if d > R, and 0 otherwise.

That's also a nice idea. In the current version sun effects can be achieved by having two or more point attractors at the same position with different ranges and selecting clever color combinations so they get mixed like you want them to in the center.;)

This was my 685th post, i did not pay attention to my 666th, but i don't feel doomed, however i'll probably stay away from allegro.cc for a day or two now, so that i'll hopefully find the time to do the CPP port.:)

da_flo
Member #1,907
February 2002
avatar

I started looking at the gradient generator more closely. Well, in fact I never played much with the GUI, and didn't really get the working of the algo. (I know, shame on me :-[)

I saw the role the distance to the ca played, but didn't see the ca had a range. That leads to really neat results, but I don't know why but I was rather expecting rather some kind of infinite range, with color interpolation towards infinity, with an interpolation fonction like f(d) = (d/(d+1))^n, which interpolates from 0 for d=0 to 1 for d=infinity.

Well, perhaps is it I that has a twisted mind. ;D That's just the way I would have created a gradient generator.

Then I tried the linearity and multiplier parameters. Since the modifiers are applied to the distance before checking for the range, it's messing a with the range. The linearity factor doesn't cause many problems, but the multiplier is more worrying.
If multiplier>1, then it artificially shrinks the range of the ca. If multiplier<1, then beyond the range everything is of the same color as on the range (I hope I'm clear here :-/). It acts as if one added a global attractor with that color.
I don't know if these are bugs are rather "features" ;), but it just seems wrong to me. I wouldn't expect it to behave like that, since the range is now irrelevant, and with multiplier<1 it even screws up the hole bitmap. :-/

I think you should apply the modifiers to d/next->range rather than d, after the range check, and drop the multiplier (keep only the linearity parameter).
d/next->range < 1, so applying the linearity parameter will give a number between 0 and 1, usable for the interpolation.
(I think this change is useful, because even the linearity messes with the range to some extent in the current version)

I hope I was clear enough. So was this behaviour intended or not ? I'm not asking you to change it, it's just a (useful, IMO) suggestion. ;)

Dennis
Member #1,090
July 2003
avatar

[edit] added more fixes from two posts below, redownload attachment[/edit]

It is both feature and bug.:)
It is a "bug" as in "it leads to visually odd and useless behaviour".:(
It is a "feature" as in "it does exactly what is described in the header file" and i used it to create some bars with sharp edges to justify it for myself.;)
(But i found that sharp bordered bars could be easily created by cutting a portion out from a smooth one so...)

A "visually" fixed version3 with much more useful behaviour is attached.;D
http://homepages.compuserve.de/DennisTapier/allegroforum/COLGRAD3F.PNG
(As you can see, p(==multiplier in Miran's version) is not useless, as it does something different than exp.)

Snippet from the new readme:
[edit]

Version 3fix2 (Aug. 10th 2005) (version 1 and 2 stripped(==declared obsolete))
   Whereas in the unfixed version p and exp did exactly what was described,
   their behaviour lead to "visually odd" results.
   In this fixed version p is used to modify the intensity of the color.
   And exp is used to modify the "falloff" behaviour of the intensity in
   respect to the distance from the attractor.
   (Check the three examples in "main" to see the difference.)
   Changes in detail:
   -The old "effect_modify" was deleted
   -And "render_col_grad_to_bitmap3" was changed
   *-*-*
   -in PN_DESC the coefficient type was changed to double grrrr to the ellipse(...)
   -make sure you place a "." before the variables pass as coefficients to "make_polynomial",
    even if you want to pass just 0 or else you'll get wrong behaviour, because of stupid
    type promotion in the ellipse grrr (bet your donkey that the next version
    will not use the ellipse but rather a pointer to a dynamic length array!)
   *-*-*
   (Still the original behaviour of version1 can be achieved by setting p=1.0,exp=1.0)

   Version 3: (Aug. 6th 2005) (introducing polynomial attractors CA_POLYNOMIAL)
   Check newly defined PN_DESC starting at line 156 and the accompanied support

da_flo
Member #1,907
February 2002
avatar

Okay. I played with your code yesterday night and did some things for fun.

First, I must say that I had some trouble to get your code to work.
First, I'm working with gcc, so in make_polynomial, in work->co<i>=va_arg(marker,float);, the float args get promoted to double, which caused a segmentation fault. I change the co array to double, and it worked.
Then, I spent a lot of time getting the polynomials to actually work. This was a nasty and stupid bug, and as it was late and the values used confused me even more during debbuging, it took me a lot of time. Here it is :

for(i=(int)he+1; i>=0; i--)
       work->co<i>=va_arg(marker,double);

It has to be he, not he+1, otherwise it cannot work ! >:( It should even sigsev with he+1, but it didn't ! (that would have helped me >:(). No offense intended, but I really hated you when I finally figured it out. ;)

I wanted to make some comments on your code too. At first, the polynomial code seemed quited messy, and I was confused because you use ints everywhere, for the coordinates as well as for the lookup table, and your unit. Of course I understand it's for a matter of efficiency, and that you won't loose much accuracy that way. But hey I often make some kinds of image generating programs, and I'm often not concerned by speed, and use floats everywhere... ;) For example, I would rather have used floats and used a 'scale' parameter to convert everything to screen coordinates when drawing. Well, I'm not trying to criticize your work, I'm only describing the way I usually work. ;)

Then, I implemented an algorithm to actually compute the distance to the polynomial curve. I used your first idea in a slightly optimized and more rigorous way.
For each point on screen, I must get the minimal distance to the polynomial. Then there's often no need to check all the points on screen. If you already have the distance d to one of the points of the curve, for example

d=abs(P(x)-y)

, then the distance is less than d, and we only have to check the points in a circle of radius d. Then I check the points of the curve from x-d to x+d, and take the minimum.

This method has some drawbacks. First, we have to check some points which are outside the screen. To do that, I create an extended plut table, with a plut_offset parameter giving the number of points outside the screen on the left and on the right. The real problem is that, for example for P(x) = -x^2, the curves decreases very quickly, therefor for some points,

d=abs(P(x)-y)

is very big, which leads to a huge lookup table and a huge number of calculations.

But there's a way to speed it up, now that you have fixed the range/modifier problem. In fact, if the distance is greater than the range, it's useless. Thus if the first d is greater than range, we can check the points from x-range to x+range only.
That's a bit hackish, and could lead to bugs if the range system was changed in the rendering, but it will really speed it up. That way for each point we have less than range*2 calculations, compared to the fixed SCREEN_W number of calculations in your initial method.

So, I rendered your example using my own distance calculation, and attached the result, just so that we can compare the two methods, for fun. I'm not saying that we should use that algorithm. It depends on your priorities, whether you're concerned with speed, like if you want your algorithm to be used almost in real time in games (and in that case after all, people are free not to use polynomials if they want full speed ::)), or if you want to do prerendered gradients where the speed doesn't matter as long as you have the picture (That's usually my way of thinking. I'm a computer barbarian and I love it ;D).
I made it only for fun. The choices are all up to you.

If you're interested (even just for fun), I can post my code later, with a new version using the range to optimize it a bit.
I might improve it a bit too. Because using integer coordinates from 0 to SCREEN_W-1 for x is only arbitrary and related to the pixels and to your integer parameters. So I could make my lookup table with a custom precision, since it's no longer used to get P(x) for x on the screen. Then we could tune the precision, whether we want speed or accuracy. :)

Dennis
Member #1,090
July 2003
avatar

da_flo said:

It has to be he, not he+1, otherwise it cannot work ! >:( It should even sigsev with he+1, but it didn't ! (that would have helped me >:(). No offense intended, but I really hated you when I finally figured it out.;)

Uhhh of course slaps hand at his head. My stupid mistake.:o
I wonder why MSVC did not crash and i wonder even more why it still rendered the correct polynomials, because after properly changing it to "he", also the function value computation with horner scheme(also the naive method) had to be adjusted.[edit2](*wrong*, see below)[/edit2]:-/

You can't blame me for the float to double thing though, that's your stupid compilers fault. If it promotes one float to double it should promote all float through the entire programme, imho.>:(
However, i'll keep that in mind for the CPP version and will use doubles all the way.

Here's a hotifx-replacement for "make_plut" and "make_polynomial"(left float as float but will be changed in the CPP port):

[EDIT]FORGET about the hotfix, it made things even worse as polynomials totally ignored all "co"s except the first one...GIMME A BREAK!:P [/EDIT]
[EDIT2]
Ok, this time it is really fixed, please redownload my previous attachment. I found out that also in MSVC the float gets promoted to double(I really hated MS when i found about that.>:() and so i changed the coefficient type to double. The function value calculation did in fact not need a change. The odd behaviour, i experienced in the edit above, resulted from the fact that if a simple number like 0,1,5 or so without a "." in it was passed to the ellipse, it also got promoted but NOT TO DOUBLE grrr, resulting in very wrong behaviour of the polynomials. But now it works correct if you pay attention to what i mention in the readme.:) THE CPP VERSION WILL DEFINITELY USE A POINTER TO A VARIABLE LENGTH ARRAY AND NOT THAT !#*$5'ED UP ELLIPSE(...):)
[/EDIT2]

But that REALLY was the last fix to the old version.;)
About the rest of your post i can't say anything now, i just quickly overflew it, but i understand the improvements. However i must recommend that you give me a break from the old version now, or else i'll never finish the port.;)(I already started this morning but then i made the other range p,exp fix first because it bugged me so much.)

da_flo
Member #1,907
February 2002
avatar

Okay, first, comments on your message :

Quote:

You can't blame me for the float to double thing though, that's your stupid compilers fault

Go say that to all the people using various flavours of gcc. ;)
And I was not blaming you. I was just trying to get it to work on my compiler and to point out compatibility and portability issues, since you probably want as many people as possible to use your algorithm.

Quote:

[EDIT]FORGET about the hotfix, it made things even worse as polynomials totally ignored all "co"s except the first one...GIMME A BREAK!:P [/EDIT]
[EDIT2]
Ok, this time it is really fixed, please redownload my previous attachment.

I don't know what your fix is, since I didn't read your new code. But my fixed version seems to work. ??? You can take a look at the one I'll attach "later" in this post if you want.

Quote:

THE CPP VERSION WILL DEFINITELY USE A POINTER TO A VARIABLE LENGTH ARRAY AND NOT THAT !#*$5'ED UP ELLIPSE(...)

I nearly always code in C++ but almost never used the STL (shame on me...), but most C++ guys would tell you to use STL vectors to do that. ;)

Quote:

However i must recommend that you give me a break from the old version now, or else i'll never finish the port.;)

Sorry if I prevent you from working on the C++ version. :-/ That was not my goal.
Well, the bugs I pointed out were asking for a fix, I must admit. ;D
But for the distance to a polynomial, I'm not requestiong you to add it to the current version. Concentrate on your port, I did it because I thought it was fun, and so that you and other people can play with it to see the differences with your previous algorithm.

Here I come to the distance to the polynomial curve. ;D (wow. That was seamless... ;D)

So I rewrote my code, now using the ca range to make it easier and faster. And the code really is simple now ! :o
I must have been really tired yesterday night. Past 4am, my code-fu is somehow weaker ;D. I did stupid things that were useless and slowed everything down. Go figure. ::)

So, to do it, I needed to make the polynomial aware of the range of the ca. Because of your structures, I had to had a 'range' member to 'PN_DESC' and make an ugly hack :

/* In wcolgradgen.cpp */
void set_pn_range(ppolynomial PN,int range)
{
  if(PN)
  {
    PN->range=range;
    PN->changed=1;
  }
}

/* in void render_col_grad_to_bitmap3(BITMAP *bmp, col_att_list3* pcal) */
if(next->PN)
      {
        if( (next->PN->w) != bmp->w) /* if target bitmap has different width than lookup table... */
          set_pn_w(next->PN,bmp->w); /* makes sure that the lookup table will
                                        be calculated for evey x-pixel position */
        if( (next->PN->range) != next->range )
          set_pn_range(next->PN, next->range);
      }

Then I modified the make_plut function :

1/* create new table : size w + 2*range.*/
2 /* 'range' is the offset of x=0 in the table */
3 /* There are 'range' values on the left of the screen, and 'range' on the right */
4 PN->plut=NULL;
5 PN->plut=(int *)malloc((PN->w+2*PN->range) * sizeof(int));
6 if(!PN->plut) return -1; /* error not enough memory */
7 
8 /* calculate the values for the width of the bitmap, and the part on the left and on the right needed*/
9 for(i=-PN->range; i<PN->w+PN->range; i++)
10 {
11 cx = (-(PN->x - i)) / (float)PN->unit;
12 
13 /* using "Horner Scheme" to compute function value */
14 cy=PN->co[PN->he]*cx;
15 for(j=(int)PN->he-1; j>0; j--)
16 cy = (PN->co[j] + cy)*cx;
17 cy = cy + PN->co[0];
18 
19 PN->plut[i + PN->range]=PN->y+(int)(cy*PN->unit); /* finally add the value to the table */
20 }

And finally of course I modified distance_to_ca3 : :)

1if (ca->t==CA_POLYNOMIAL)
2 {
3 /* uses lookup table for calculation if available else returns 0
4 to fall back to weird but at least uncrashing behaviour */
5 if(ca->PN)
6 if(ca->PN->plut)
7 {
8 /* calculates the distances to all the points on the curve in the given range */
9 /* and takes the minumum */
10 int i, r;
11 double dmin, d, dx, dy;
12 dmin = abs((ca->PN->plut[x + ca->PN->range])-y);
13 if (ca->PN->range < dmin) r = ca->PN->range;
14 else r = (int)(dmin);
15 for (i = -r; i<= r; ++i)
16 {
17 dx = i;
18 dy = ca->PN->plut[x+i + ca->PN->range] - y;
19 d = sqrt(dx*dx + dy*dy);
20 if (d<dmin) dmin = d;
21 }
22 return dmin;
23 }
24 else return 0;
25 }

My whole modified and fixed code is attached to the post.
I made it render your example with polynomials, and it gives the same picture than the one attached in my previous post (which was made with the stupid yet accurate version I made before ::)).

Feel free to play with it, so that we can see if it's worth adding it to the new version. Personnaly I like it, but I don't know about the speed concerns. :-/

And I almost forgot : I didn't add the 'custom lookup table precision' thing, because it would have been quite messy, with ugly coordinates conversion, and because I just didn't feel like it. Later perhaps. :P

Dennis
Member #1,090
July 2003
avatar

da_flo said:

Sorry if I prevent you from working on the C++ version. That was not my goal.
Well, the bugs I pointed out were asking for a fix, I must admit.

Oh, no need for excuses. Bugs are there to get fixed, and especially the weird behaviour of p and exp had already bugged(!) me for some longer time(and judging from Miran's earlier comments about it, i think he was not too happy with that behaviour either).
And the typo(resulting from laze copy/pasting) with he+1 would probably have gone unnoticed if you had not found it.:) I think it is better these bugs are fixed already in the old version than if they were ported to the new one.

da_flo said:

Feel free to play with it, so that we can see if it's worth adding it to the new version. Personnaly I like it, but I don't know about the speed concerns.:-/

We can test it, as soon as the new version is done. There you'll not need to hack, because the polynomial derived class will of course have access to the range attribute. All you'd need to do is inherit it and override the distance method and probably the lookuptablecreation method. ...maybe i'll even test your function later this evening, but i really need a pause from the stuff now...;)
(The picture rendered with your version looks nice.)

[edit]

da_flo said:

Go say that to all the people using various flavours of gcc.

Yes, i already said in my edit that it is also the same way with MSVC, so MSVC is also a stupid compiler and as i also said a better(as in "less error prone" solution will be used in the port.

da_flo said:

I don't know what your fix is.

Multiple things. First the p and exp behaviour, second the he+1 bug and last but not least speed improvements by saving a few unnecessary calculations in the render function.:)

da_flo
Member #1,907
February 2002
avatar

Hey, I was playing with the polynomials "fun" example at the top of the 4th page, using my distance calculation and changing parameters, and I noticed some strange behaviour of the polynomial attractors, even using my supposed-to-be-accurate method.

Then I tried it with very simple examples, with only one polynomial attractor and a global attractor for the background. And what I found was really disturbing. Look at the picture I attached. When the range is big enough, you can see darker lines where the radius of curvature of the polynomial curve has a minimum value.
There are 3 pictures in the file. The top picture is made with P(x) = x^3 - x (a friend of mine to whom I showed that picture and who knew nothing about this gradient generator told me "What's that ? A small intestine viewed with X-rays ?" ;D), the middle one is P(x) = -x^2, the bottom picture is the middle one viewed from further back. (Note : the polynomials are actually -x^3+x and x^2, but to hell with those silly screen coordinates. ;))

When I saw that, I was really disturbed, and couldn't say if it was a bug in my code (which would have surprised me, since it seemed to work well with the first examples, and I couldn't see how such a "bug" could occur :P), or mathematical properties, or an optical illusion... or several of these things at the same time ! ;D

Now I'm quite sure it's not a bug, and I can somehow feel what the mathematical reasons are, but thats still bugging me, because it just doesn't feel natural. And also, the contrast might have been accentuated, by some kind of optical illusion, and my laptop screen shifts the colors and the constrast of the gradients, especially when I look my screen from further back.

What do you guys think of it ? Can you see what I mean ? Does it disturb you ? Does it feel natural ? Do you think that maths and polynomials are great ? ;D

Richard Phipps
Member #1,632
November 2001
avatar

You know if the random element was combined with some kind of 'genetic breeding' it could be even easier to get good results, by selecting your favourite pictures to combine and mutate from a pool. :)

Dennis
Member #1,090
July 2003
avatar

da_flo said:

What do you guys think of it ? Can you see what I mean ? Does it disturb you ? Does it feel natural ?

If you observe your pictures closely(don't know why i immediately spotted it), you will find that those "dark lines" are appearing along curves of other pixels that are not on the polynomial but for which there are always at least TWO points on the polynomial with EQUAL distance to the points on the "dark line".
(I hope you see it. It's easier to see at the x^2 but its also the same with the curved "dark lines" on the -x^3-x.)
Don't know what is causing it though, but i suppose your algorithm could be reporting a wrong distance(too far, hence the darker color) for those points.
It could also be caused by the inaccuracy, Remember: we're still completely neglecting those "vertically next to each other" pixels on the curve in distance computations.

Disturbing?
NO. Actually it looks pretty cool nonetheless, like bending flexible and soft rubber bars.
(Whatever you do, keep a version of that distance algorithm to preserve that effect.)
(I'll also keep my version where only the distance to the function value at the x position is used, because i like the effect resulting from that a lot.)
Note too self: Must..stay..away..from..allegro.cc..to..get..port..done..argh



Go to: