Archive: Polynomial solvers


10th April 2004 01:42 UTC

Polynomial solvers
WARNING: Unoptimised messy code ahead!

I finally have *working* cubic and quartic equation solvers. These are faster than Whittaker iteration in many cases where they can be applied. However they extra susceptible to DM glitches when used due to tiny rounding errors. From experimenting most of these errors seem to come from 'a=0' quartics, i.e. 0x^4+bx^3+cx^2+dx+e which is a cubic. This problem crops up mostly with the quartic solver, it comes up with the cubic but very little.

The rest if the problems are all quartic specific. Sometimes the reducing cubic will have a zero leading coefficient as above. Other problems result from filtering out complex roots from the quartic solver, using if(x0i,.. etc doesn't seem to be enough, best to use the discriminant type values used in the solver to determine how many roots are what.

For the cubic I used the actual discriminant to tell if there are one or three real roots. This value is stored in disc in my code. Also note that x0r is always a real root of the cubic and that x0i always = 0. For the quartic there are 3 possiblities, and there is probably a better way to check them then how I have done it. This is the piece of code that deals with the complexity of the quartic roots to make it a bit clearer.

if(below(qz2,0),
exec2(
exec2(
assign(x0i,.5*qz),
assign(x1i,.5*qz)
)
,
exec2(
assign(x2i,-.5*qz),
assign(x3i,-.5*qz)
)
)
,
exec2(
exec2(
assign(x0r,.5*qz),
assign(x1r,.5*qz)
)
,
exec2(
assign(x2r,-.5*qz),
assign(x3r,-.5*qz)
)
)
);

if(below(sq1,0),
exec2(
assign(x0i,x0i+.5*qv1),
assign(x1i,x1i-.5*qv1)
)
,
exec2(
assign(x0r,x0r+.5*qv1),
assign(x1r,x1r-.5*qv1)
)
);

if(below(sq2,0),
exec2(
assign(x2i,x2i+.5*qv2),
assign(x3i,x3i-.5*qv2)
)
,
exec2(
assign(x2r,x2r+.5*qv2),
assign(x3r,x3r-.5*qv2)
)
);

qz2<0 = no real roots
qz2>0 and:

sq1<0,sq2<0 = no real roots
sq1>0,sq2<0 = two real roots: x0r, x1r
sq1<0,sq2>0 = two real roots: x2r, x3r
sq1>0,sq2>0 = four real roots: x0r, x1r, x2r, x3r

That should be enough to clean up any uglies resulting from these solvers. The most stable method of use is to solve as quartic if first coefficient not zero, if it is then solve as cubic, if the first coefficient of cubic = 0 then solve as quadratic etc... then make sure to filter out complex values using the discriminants instead of standard logic.

The attached zip contains an implementation of each solver.

Roots stored as x0r+x0i*sqrt(-1),x1r+x1i*sqrt(-1),x2r+x2i*sqrt(-1),x3r+x3i*sqrt(-1)

Feel free to use anywhere, credit not necessary.

EDIT: you may wish to optimise/modify variable names etc... do this, i wont likely be releasing another version of this code since it is stable.


10th April 2004 04:44 UTC

Sorry, my writing codes in SS & DM is not good.
I wish I could help to you master, but, my knowledge in writing codes is not enough yet.
Maybe in future I will learn writing codes from AVS masters like you.


10th April 2004 08:01 UTC

use qa0,qb0,qc0,qd0,qe0 as:

qa0x^4+qb0x^3+qc0x^2+qd0x+qe0=0

then the solver will ouput the solutions

x0r,x0i x1r,x1i x2r,x2i x3r,x3i

where xnr is real and xni is imaginary

if qa0=0 then a cubic will be solved, etc and x4r etc will not be present and will be replaced by some suitably large value for raytracing purposes.

exec3(exec3(assign(x0r,1000000),assign(x0i,0),assign(x1r,1000000)), exec3(assign(x1i,0),assign(x2r,1000000),assign(x2i,0)), exec3(assign(x3r,1000000),assign(x3i,0),if(equal(qa0,0), if(equal(qb0,0),if(equal(qc0,0),assign(x0r,-qe0/qd0),exec3( exec3(assign(disc,sqr(qd0)-4*qc0*qe0),exec3(assign(dr,0), assign(di,0),assign(if(below(disc,0),di,dr),sqrt(disc))), assign(invsa,1/(2*qc0))),exec3(assign(x0r,(-qd0+dr)*invsa), assign(x0i,di*invsa),assign(x1r,(-qd0-dr)*invsa)), assign(x1i,-di*invsa))),exec2(exec3(exec3(assign(a2,qc0/qb0), assign(a1,qd0/qb0),assign(a0,qe0/qb0)),exec3( assign(q,(3*a1-sqr(a2))*.11111111111), assign(r,((9*a1-2*sqr(a2))*a2-27*a0)*.0185185185), assign(disc,sqr(q)*q+sqr(r))), exec3(assign(x0r,0),assign(x0i,0),assign(x1r,0))), exec2(exec3(assign(x1i,0),assign(x2r,0),assign(x2i,0)), if(below(disc,0), exec2(exec2(assign(theta,acos(r/sqrt(-sqr(q)*q))), assign(x0r,2*sqrt(-q)*cos(theta*.333333)-a2*.333333333)), exec2(assign(x1r,2*sqrt(-q)*cos((theta+2*$PI)*.333333333) -a2*.333333),assign(x2r,2*sqrt(-q)*cos((theta+4*$PI)*.333333333) -a2*.333333))), exec2(exec2(assign(s,sign(r+sqrt(disc))*pow(abs(r+sqrt(disc)), .333333333)),assign(t,sign(r-sqrt(disc))*pow(abs(r-sqrt(disc)), .333333333))), exec3(assign(x0r,-a2*.333333333+s+t),assign(x1r,-a2*.333333333 -(s+t)*.5),exec3(assign(x2r,-a2*.333333333-(s+t)*.5),assign(x1i,abs(.8660254*(s-t))), assign(x2i,-x1i)))))))),exec2(exec3(exec3(exec3(assign(inv,1/qa0),assign(qa,qb0*inv), assign(qb,qc0*inv)),exec3(assign(qc,qd0*inv),assign(qd,qe0*inv), assign(a2,-qb)),exec3(assign(a1,qa*qc-4*qd),assign(a0,qd*(4*qb-sqr(qa))-sqr(qc)), assign(q,(3*a1-sqr(a2))*.11111111111))), exec3(exec3(assign(r,((9*a1-2*sqr(a2))*a2-27*a0)*.0185185185), assign(disc,sqr(q)*q+sqr(r)),assign(x0r,0)),exec3(assign(x0i,0), assign(x1r,0),assign(x1i,0)), exec3(assign(x2r,0),assign(x2i,0),if(below(disc,0),exec2(exec2( assign(theta,acos(r/sqrt(-sqr(q)*q))),assign(x0r,2*sqrt(-q)*cos(theta*.333333)-a2*.333333333)), exec2(assign(x1r,2*sqrt(-q)*cos((theta+2*$PI)*.333333333)-a2*.333333),assign(x2r, 2*sqrt(-q)*cos((theta+4*$PI)*.333333333)-a2*.333333))), exec2(exec2(assign(s,sign(r+sqrt(disc))*pow(abs(r+sqrt(disc)),.333333333)),assign(t, sign(r-sqrt(disc))*pow(abs(r-sqrt(disc)),.333333333))), exec3(assign(x0r,-a2*.333333333+s+t),assign(x1r,-a2*.333333333 -(s+t)*.5),exec3(assign(x2r,-a2*.333333333-(s+t)*.5),assign(x1i, abs(.8660254*(s-t))), assign(x2i,-x1i))))))), exec3(exec3(assign(qz1, if(below(disc,0),max(x0r,max(x1r,x2r)),x0r)),assign(qz2,qz1+sqr(qa)*.25-qb), assign(qz,sqrt(abs(qz2)))), exec3(if(equal(qz,0),exec2(assign(sq1,.75*sqr(qa)-2*qb+2*sqrt(sqr(qz1)-4*qd)), assign(sq2,.75*sqr(qa)-2*qb-2*sqrt(sqr(qz1)-4*qd))),exec2( assign(sq1,.75*sqr(qa)-qz2-2*qb+(4*qa*qb-8*qc-sqr(qa)*qa)/(4*qz)), assign(sq2,.75*sqr(qa)-qz2-2*qb-(4*qa*qb-8*qc-sqr(qa)*qa)/(4*qz)))), assign(qv1,sqrt(abs(sq1))),assign(qv2,sqrt(abs(sq2)))),exec3( assign(x0r,0),assign(x1r,0),assign(x2r,0)))),exec2(exec3(exec3( assign(x3r,0),assign(x0i,0),assign(x1i,0)),exec3(assign(x2i,0), assign(x3i,0),if(below(qz2,0),exec2(exec2(assign(x0i,.5*qz), assign(x1i,.5*qz)),exec2(assign(x2i,-.5*qz),assign(x3i,-.5*qz))), exec2(exec2(assign(x0r,.5*qz),assign(x1r,.5*qz)), exec2(assign(x2r,-.5*qz),assign(x3r,-.5*qz))))),exec3(if(below(sq1,0), exec2(assign(x0i,x0i+.5*qv1),assign(x1i,x1i-.5*qv1)), exec2(assign(x0r,x0r+.5*qv1),assign(x1r,x1r-.5*qv1))),if(below(sq2,0), exec2(assign(x2i,x2i+.5*qv2),assign(x3i,x3i-.5*qv2)), exec2(assign(x2r,x2r+.5*qv2),assign(x3r,x3r-.5*qv2))),assign(x0r,x0r-.25*qa))), exec3(assign(x1r,x1r-.25*qa), assign(x2r,x2r-.25*qa),assign(x3r,x3r-.25*qa)))))));

fully condensed into one line of code with tomylobo's avstrans thingy.

edit: i can get rid of the bad linespaces here... i think its windows fault. this code displays as 4 line in an avs code box and also in notepad with word wrap *off*

[removed stretchy code, added spaces --unconed]


10th April 2004 17:09 UTC

/me is confused
*scratches head*

Too bad I don't know anything about this kind of math.


10th April 2004 17:55 UTC

argh i wrote my tool so people can provide human-readable code for others (or oneself :) to understand, while still being able to translate it automatically. :eek:


10th April 2004 20:01 UTC

Next time, just attach it okay? :P


11th April 2004 20:22 UTC

Quote:


I never remember that advice. Its been given too many times to ever sink in. :P

I'll try to remember next time I have such a huge thing to post.

Thanks for cleaning up my mess too.

Originally posted by UnConeD
Next time, just attach it okay? :P

13th April 2004 03:10 UTC

Nice, now I can use it to make some totally useless cool things after a little experimenting. Very nice job, I don't see any errors as far as I know. You must have had a lot of free time or something.


13th April 2004 15:00 UTC

Whoa, good work J!
...Not that I have any idea as to what all that does, but nice work anyway!


14th April 2004 01:55 UTC

Basically this is for solving a polynomial equation, this is extremely useful in certain situations, like creating 3D raytraced DMs for instance.

it means that if a*t + b =0 and you know a and b then we can find t, it also means that if a*t^2+b*t+c=0 and we know a b and c then we can find t, thsi code works up to at^4+bt^3+ct^2+dt+e=0
which are fed in as qa0,qb0,qc0,qd0,qe0 etc
then the maximum of four possible ts are spat out in x0r,x0i,x1r,x1i..etc which are the real and imaginary components of the t's. I would explain what the code does but its way to tedious and complicated and would take up a lot more space than the code.


14th April 2004 04:19 UTC

You know all those fancy raytraced dm's with all complicated looking shapes that have been showing up recintly? Well many of those shapes are expressed with Polynomial equasions, and are gererally very hard to solve.

Untill this thing, the only way to draw those shapes was to use itterative methods, which are basically a loop that guesses for the right number, and gets closer each time you run it.

A natural solver (what this is) on the other hand, solves the equasions numerically, so it is both faster and more accurate than an average iterator. Plus, this one can find all the roots, not just one, and that includes imaginary ones!

Well done J!


18th April 2004 18:08 UTC

Very good job Jheriko, but, may be it can be improved :
* Solving cubic :
- the reset of x0r,x1r, and so on seems to be unuseful (xni were already set, and xnr will be changed).
- I hope there's a little mistake when assigning x1i : no abs()

* Solving quartic :
- one more time the resets seems to be unuseful (see below).
- I think you only need one root of the cubic equation, then it can always be the x0r, in both cases, that can directly be assigned to qz1.

I let you see this in details.

BTW, can someone give a simple example to explain how this can be used in a DM ?


22nd April 2004 00:41 UTC

I have worked again on the formula.
I have made a mistake in my last message. In quartic solve, the xnr must be set. But instead of being reset to 0, they can be initialise with -.25*qa.
The new algorithm is now :
(==> /* old version */)


// these constants helped me to understand
C1S2=1/2; // .5
C1S4=1/4; // .25
C3S4=3/4; // .75
C1S3=1/3; // .3333333
C1S9=1/9; // .1111111
C1S54=1/54; // .0185185
CR3S2=sqrt(3)/2; // .8660254

x0r = 1000000;
x0i = 0;
x1r = 1000000;
x1i = 0;
x2r = 1000000;
x2i = 0;
x3r = 1000000;
x3i = 0;
if ( qa0 == 0 )
{
if ( qb0 == 0 )
{
if ( qc0 == 0 )
{ // solving : qd0.x + qe0 = 0
x0r = -qe0/qd0;
}
else
{ // solving : qc0.x^2 + qd0.x + qe0 = 0
disc = sqr(qd0)-4*qc0*qe0;
dr = 0;
di = 0;
if ( disc < 0 )
di = sqrt(-disc);
else
dr = sqrt(disc);
invsa = 1/(2*qc0);
x0r = (-qd0+dr)*invsa;
x0i = di*invsa;
x1r = (-qd0-dr)*invsa;
x1i = -di*invsa;
}
}
else // qb0 != 0
{ // solving : qb0.x^3 + qc0.x^2 + qd0.x + qe0 = 0
inv = 1/qb0;
a2 = qc0*inv;
a1 = qd0*inv;
a0 = qe0*inv;
q = (3*a1-sqr(a2))*C1S9;
r = ((9*a1-2*sqr(a2))*a2-27*a0)*C1S54;
disc = sqr(q)*q+sqr(r);
/* x0r = 0; */
/* x0i = 0; */
/* x1r = 0; */
/* x1i = 0; */
/* x2r = 0; */
/* x2i = 0; */
if ( disc < 0 )
{
theta = acos(r/sqrt(-sqr(q)*q));
x0r = 2 * sqrt(-q) * cos(theta*C1S3) - a2*C1S3;
x1r = 2 * sqrt(-q) * cos((theta+2*$PI)*C1S3) - a2*C1S3;
x2r = 2 * sqrt(-q) * cos((theta+4*$PI)*C1S3) - a2*C1S3;
}
else // disc >= 0
{
s = sign(r+sqrt(disc))*pow(abs(r+sqrt(disc)), C1S3);
t = sign(r-sqrt(disc))*pow(abs(r-sqrt(disc)), C1S3);
x0r = -a2*C1S3 + (s+t);
x1r = -a2*C1S3 - (s+t)*C1S2;
x2r = -a2*C1S3 - (s+t)*C1S2;
x1i = CR3S2*(s-t); /* abs(CR3S2*(s-t)) ?? */
x2i = -x1i;
}
}
}
else // qa0 != 0
{ // solving : qa0.x^4 + qb0.x^3 + qc0.x^2 + qd0.x + qe0 = 0
inv = 1/qa0;
qa = qb0*inv;
qb = qc0*inv;
qc = qd0*inv;
qd = qe0*inv;
a2 = -qb;
a1 = qa*qc-4*qd;
a0 = qd*(4*qb-sqr(qa))-sqr(qc);
q = (3*a1-sqr(a2))*C1S9;
r = ((9*a1-2*sqr(a2))*a2-27*a0)*C1S54;
disc = sqr(q)*q+sqr(r);
/* x0r = 0; */
/* x0i = 0; */
/* x1r = 0; */
/* x1i = 0; */
/* x2r = 0; */
/* x2i = 0; */
if ( disc < 0 )
{
theta = acos(r/sqrt(-sqr(q)*q));
/* x0r = 2*sqrt(-q)*cos(theta*C1S3)-a2*C1S3; */
/* x1r = 2*sqrt(-q)*cos((theta+2*$PI)*C1S3)-a2*C1S3; */
/* x2r = 2*sqrt(-q)*cos((theta+4*$PI)*C1S3)-a2*C1S3; */
qz1 = 2 * sqrt(-q) * cos(theta*C1S3) - a2*C1S3; /* max(x0r,max(x1r,x2r)); */
}
else // disc >= 0
{
s = sign(r+sqrt(disc))*pow(abs(r+sqrt(disc)),C1S3);
t = sign(r-sqrt(disc))*pow(abs(r-sqrt(disc)),C1S3);
/* x0r = -a2*C1S3+s+t; */
/* x1r = -a2*C1S3 -(s+t)*C1S2; */
/* x2r = -a2*C1S3-(s+t)*C1S2; */
/* x1i = abs(CR3S2*(s-t)); */
/* x2i = -x1i; */
qz1 = -a2*C1S3+s+t; /* x0r; */
}
qz2 = qz1+sqr(qa)*C1S4-qb;
qz = sqrt(abs(qz2));
if ( qz == 0 )
{
sq1 = C3S4*sqr(qa)-2*qb+2*sqrt(sqr(qz1)-4*qd);
sq2 = C3S4*sqr(qa)-2*qb-2*sqrt(sqr(qz1)-4*qd);
}
else // qz != 0
{
sq1 = C3S4*sqr(qa)-qz2-2*qb+(4*qa*qb-8*qc-sqr(qa)*qa)/(4*qz);
sq2 = C3S4*sqr(qa)-qz2-2*qb-(4*qa*qb-8*qc-sqr(qa)*qa)/(4*qz);
}
qv1 = sqrt(abs(sq1));
qv2 = sqrt(abs(sq2));
x0r = -C1S4*qa; /* x0r = 0; */
x1r = -C1S4*qa; /* x1r = 0; */
x2r = -C1S4*qa; /* x2r = 0; */
x3r = -C1S4*qa; /* x3r = 0; */
/* x0i = 0; */
/* x1i = 0; */
/* x2i = 0; */
/* x3i = 0; */
if ( qz2 < 0 )
{
x0i = C1S2*qz;
x1i = C1S2*qz;
x2i = -C1S2*qz;
x3i = -C1S2*qz;
}
else // qz2 >= 0
{
x0r = x0r + C1S2*qz; /* x0r = C1S2*qz; */
x1r = x1r + C1S2*qz; /* x1r = C1S2*qz; */
x2r = x2r - C1S2*qz; /* x2r = -C1S2*qz; */
x3r = x3r - C1S2*qz; /* x3r = -C1S2*qz; */
}
if ( sq1 < 0 )
{
x0i = x0i+C1S2*qv1;
x1i = x1i-C1S2*qv1;
}
else // sq1 >= 0
{
x0r = x0r+C1S2*qv1;
x1r = x1r-C1S2*qv1;
}
if ( sq2 < 0 )
{
x2i = x2i+C1S2*qv2;
x3i = x3i-C1S2*qv2;
}
else // sq2 >= 0
{
x2r = x2r+C1S2*qv2;
x3r = x3r-C1S2*qv2;
}
}


I'm sorry, I do not have tomylobo's avstrans ...

22nd April 2004 06:56 UTC

hmm nice :) so someone understood these solvers actually ;)
problem is: this is C/Perl/PHP/Java code, not AVSTrans compatible code
---
if syntax:
if (condition,
if-branch
,
else-branch
);
---
condition syntaxes:
C: a==b AVS: equal(a,b)
C: a<b AVS: below(a,b)
C: a>b AVS: above(a,b)
---
btw this thingy can be optimised:


dr = 0;
di = 0;
if (below(disc, 0),
di = sqrt(-disc);
,
dr = sqrt(disc);
);

to

if (below(disc, 0),
dr = 0;
di = sqrt(-disc);
,
dr = sqrt(disc);
di = 0;
);

22nd April 2004 07:18 UTC

i attached the AVSTrans compatible code
just copy the whole .txt file to AVSTrans and copy the output to wherever you want it


22nd April 2004 12:49 UTC

Nice job TomyLobo ! (I'm talking about the conversion from C to AVSTrans :))
Thanks.

I promise I'll download all this tonight (currently, I'm at work).

About optimizations, as Jheriko said, there are still a lot to do. All that I proposed was a simplification (if I can say so) of the algorithm. The next step will be a real optimization ;)


23rd April 2004 00:33 UTC

Wow. I don't read these kind of threads to learn anything, I just read them to gaze at the hardcore mathematical spectacle that is AVS.

You guys are all nuts. :p

Anyway, continue your conversation....


23rd April 2004 16:04 UTC

Here is the optimized and final version :


//$AVSTrans_TransFirst=0
//allow "else" instead of "," (for if()s)
//$AVSTrans_Replacement=else->,
//remove spaces
//$AVSTrans_Replacement= ->
//remove tabs
//$AVSTrans_Replacement= ->
//------------------------------------------------------------------
x0r = 1000000;
x0i = 0;
x1r = 1000000;
x1i = 0;
x2r = 1000000;
x2i = 0;
x3r = 1000000;
x3i = 0;
if (equal(qa0, 0),
if (equal(qb0, 0),
if (equal(qc0, 0),
x0r = -qe0/qd0;
else
disc = sqr(qd0)-4*qc0*qe0;
if (below(disc, 0),
dr = 0;
di = sqrt(-disc);
else
dr = sqrt(disc);
di = 0;
);
invsa = 1/(2*qc0);
x0r = (-qd0+dr)*invsa;
x0i = di*invsa;
x1r = (-qd0-dr)*invsa;
x1i = -di*invsa;
);
else
inv = 1/qb0;
a2 = qc0*inv;
a1 = qd0*inv;
a0 = qe0*inv;
q = (3*a1-sqr(a2))*.1111111;
r = ((9*a1-2*sqr(a2))*a2-27*a0)*.0185185;
disc = sqr(q)*q+sqr(r);
a2b3 = a2*.3333333;
if (below(disc, 0),
theta = acos(r/sqrt(-sqr(q)*q));
sqb2 = 2 * sqrt(-q);
x0r = sqb2 * cos(theta*.3333333) - a2b3;
x1r = sqb2 * cos((theta+2*$PI)*.3333333) - a2b3;
x2r = sqb2 * cos((theta+4*$PI)*.3333333) - a2b3;
else
sqd=sqrt(disc);
s = sign(r+sqd)*pow(abs(r+sqd), .3333333);
t = sign(r-sqd)*pow(abs(r-sqd), .3333333);
x0r = -a2b3 + (s+t);
x1r = -a2b3 - (s+t)*.5;
x2r = x1r;
x1i = .8660254*(s-t);
x2i = -x1i;
);
);
else
inv = 1/qa0;
qa = qb0*inv;
qb = qc0*inv;
qc = qd0*inv;
qd = qe0*inv;
a2 = -qb;
a1 = qa*qc-4*qd;
a0 = qd*(4*qb-sqr(qa))-sqr(qc);
q = (3*a1-sqr(a2))*.1111111;
r = ((9*a1-2*sqr(a2))*a2-27*a0)*.0185185;
disc = sqr(q)*q+sqr(r);
if (below(disc, 0),
theta = acos(r/sqrt(-sqr(q)*q));
qz1 = 2 * sqrt(-q) * cos(theta*.3333333) - a2*.3333333;
else
sqd=sqrt(disc);
s = sign(r+sqd)*pow(abs(r+sqd),.3333333);
t = sign(r-sqd)*pow(abs(r-sqd),.3333333);
qz1 = -a2*.3333333+s+t;
);
qz2 = qz1+sqr(qa)*.25-qb;
qz = sqrt(abs(qz2));
sqab34 = .75*sqr(qa);
if (equal(qz, 0),
sq0 = 2*sqrt(sqr(qz1)-4*qd);
sq1 = sqab34-2*qb+sq0;
sq2 = sqab34-2*qb-sq0;
else
sq0 = (4*qa*qb-8*qc-sqr(qa)*qa)/(4*qz);
sq1 = sqab34-qz2-2*qb+sq0;
sq2 = sqab34-qz2-2*qb-sq0;
);
qv1 = sqrt(abs(sq1));
qv2 = sqrt(abs(sq2));
x0r = -.25*qa;
x1r = x0r;
x2r = x0r;
x3r = x0r;
qzb2 = .5*qz;
if (below(qz2, 0),
x0i = qzb2;
x1i = qzb2;
x2i = -qzb2;
x3i = -qzb2;
else
x0r = x0r + qzb2;
x1r = x1r + qzb2;
x2r = x2r - qzb2;
x3r = x3r - qzb2;
);
qv1b2 = .5*qv1;
if (below(sq1, 0),
x0i = x0i+qv1b2;
x1i = x1i-qv1b2;
else
x0r = x0r+qv1b2;
x1r = x1r-qv1b2;
);
qv2b2 = .5*qv2;
if (below(sq2, 0),
x2i = x2i+qv2b2;
x3i = x3i-qv2b2;
else
x2r = x2r+qv2b2;
x3r = x3r-qv2b2;
);
);


Pixelcraft, you're right ! :D

BTW, I still don't know what can be done with this :igor:

TomyLobo, your AVSTrans is really :up:

30th April 2004 17:51 UTC

Cool, nice work.

I'll show you what can be done with this.

The main use for this is raytraced dms. For instance, if you which to raytrace a sphere you substitute the ray equation into the sphere equation and solve for 't' (or k or whatever you want to call it):

x^2+y^2+z^2=r^2
(x1*t+ox)^2+(y1*t+oy)^2+(z1*t+oz)^2=r^2

expanding this gives a quadratic in t
which we solve using the quadratic equation:

(x1^2+y1^2+z1^2)*t^2 + 2*(x1*ox+y1*oy+z1*oz)*t + ox^2+oy^2+oz^2-r^2 = 0

avs code (this can be optimised by playing with constants but i'll leave it like this so that the quadratic equation is more obvious):

// set-up quadratic coefficients (ax^2+bx+c=0)
a=sqr(x1)+sqr(y1)+sqr(z1);
b=2*(x1*ox+y1*oy+z1*oz);
c=sqr(ox)+sqr(oy)+sqr(oz)-sqr(r);
// calculate discriminant
disc=sqr(b)-4*a*c;
// ignore complex roots (disc < 0)
t=if(below(disc,0),10000,sqrt(disc)/(2*a));
// ignore negative roots (t < 0)
t=if(below(t,0),10000,t);

Now using quadratic we have access to all of the shapes which we call quadric surfaces. These are spheres, cylinders, paraboloids, one sheet hyperboloids, two sheet hyperboloids, parabolic cylinders, hyberbolic cylinders, cones or a degenerate (a plane, line, parabola, hyperbola, circle or a singular point). If we want to generate more shapes we need to be able to solve more complex equations. Using cubics we can create a few more shapes, but nothing of major interest. Most cubic surfaces are ugly things with numerous singular points and horrible degenrate sections. Quartics however are much more interesting, there are tens of thousands of different distinct quartics but there are a few of particular interest like, the torus, chumtov surface, cylindrical parabola or the varying trench surfaces. Quartic surface equations are generally several times more complex that quadratics when it comes to expanding the raytracing equation and making mistakes is pretty easy. I won't bother expanding these fully since they are quite long but here is an example:

Torus:
z^2 + (sqrt(x^2+y^2) - R)^2 = r^2
x^4 + y^4 + z^4 + 2*(x^2*y^2 + x^2*z^2 + y^2*z^2) - 2*(R^2 + r^2)*(x^2 + y^2) + 2*(R^2 - r^2)*(1 + z^2) = 0

// unsimplified and probably wrong raytracing equation coefficients
qa0 = sqr(sqr(x1)) + sqr(sqr(y1)) + sqr(sqr(z1)) + 2*(sqr(x1)*sqr(y1) + sqr(x1)*sqr(z1) + sqr(y1)*sqr(z1));
qb0 = 4*sqr(x1)*x1*ox + 4*sqr(y1)*y1*oy + 4*sqr(z1)*z1*oz + 2*(sqr(x1)*2*y1*oy + 2*x1*ox*sqr(y1) + sqr(x1)*2*z1*oz + 2*x1*ox*sqr(z1) + sqr(y1)*2*z1*oz + 2*y1*oy*sqr(z1));
qc0 = 6*sqr(x1)*sqr(ox) + 6*sqr(y1)*sqr(oy) + 6*sqr(z1)*sqr(oz) + 2*(sqr(x1)*sqr(oy) + 4*x1*ox*y1*oy + sqr(ox)*sqr(y1) + sqr(x1)*sqr(oz) + sqr(ox)*sqr(z1) + 4*x1*ox*z1*oz + sqr(y1)*sqr(oz) + sqr(oy)*sqr(z1) + 4*y1*oy*z1*oz) - 2*(sqr(R)+sqr(r))*(sqr(x1)+sqr(y1)) + 4*(sqr(R)-sqr(r))*sqr(z1);
qd0 = 4*x1*sqr(ox)*ox + 4*y1*sqr(oy)*oy + 4*z1*sqr(oz)*oz + 2*(2*x1*ox*sqr(oy) + sqr(ox)*2*oy*y1 + 2*x1*ox*sqr(oz) + sqr(oy)*2*oz*z1 + 2*y1*oy*sqr(oz) + sqr(oy)*2*oz*z1) - 4*(sqr(R)+sqr(r))*(x1*ox+y1*oy) + 4*(sqr(R)-sqr(r))*oz*z1;
qe0 = sqr(sqr(ox)) + sqr(sqr(oy)) + sqr(sqr(oz)) + 2*(sqr(ox)*sqr(oy) + sqr(ox)*sqr(oz) + sqr(oy)*sqr(oz)) - 2*(sqr(R)+sqr(r))*(sqr(ox)+sqr(oy)) + 2*(sqr(R)-sqr(r))*(1+sqr(oz));

A quartic is used in VJ Chmutov, but it is solved with whittaker iterations, you could use the quartic solver in place of this. You just have to be careful about complex roots since they can cause little glitches to pop up in certain places.


2nd May 2004 17:22 UTC

pir, better put these equations into a .txt, zip it and attach it... this preserves tabulators and stuff in all browsers :)
anyway: good work jheriko for working out the equations and pir for optimizing them :)
so we'll get some more dynamic tori from now on *g*
btw:
...2*(sqr(R)-sqr(r))*...
you sure that's correct? :)


6th May 2004 06:33 UTC

I just worked that out as I typed it, so there are probably tons of mistakes. I'm lazy like that.

In theory it should be possible to make pretty much any imaginable DM geometry from quartics, cubics, quadrics and planes. Hopefully we will get a lot more than just dynamic torii in the future. The theory of working the DM has pretty much reached its limits I feel, so now we have to concentrate on producing some really creative geometry to generate unique visuals. The solver isn't perfect though, it falls apart when faced with some really awkward quartics with tiny complex components in the roots for instance. Some shapes can be handled perfectly though, I tried a cylindical parabola but there were seams where glitches would occur, however I also tried the Chmutov surface with perfect results.

It might be a good idea to go through this code and fully optimise it specifically for raytracing so that it deals with complex roots in a better fashion. Perhaps even sorting the roots into a specific order, i.e. x0>x1>x2>x3 .
I might do this later... but I have things to do this morning.

We should definately make some presets with this though, even though it is slower than a good whittaker it can handle some other problems a lot more easily, such as ZGM style surface combinations. I made a torus intersection with a cylinder using whittaker, it was pretty awkward to say the least, using this though you can use the standard methods rather than having to hack some ugly fix.


6th May 2004 09:05 UTC

your formula has definitely one big advantage over whittaker: you can do transparent parts with it!
whittaker always zeros in on the part that is closest to your starting value. you'd have to do the iteration twice to get "through a wall" :)
with your formula, one simply takes solution number 0 1 2 3 to get through walls :)


9th May 2004 02:08 UTC

I need to add some more stuff to this to remove all of the errors. After some extensive testing it seems like a lot of the glitches occur from the raytrace parameter being small and positive from rounding errors from e=0 polynomials (which always have a root x=0) and a few other special cases such as squared quadratics which occasionally seem to cause problems. This solution is fairly complex mathematically and so it might take ages to make sure that it really solves every quartic.

The problem with transparent objects is that to do any decent objects you end up with something really slow.


9th May 2004 11:45 UTC

the squared quadratics problem could be fixed by another check... if e!=0 and d=0 then it's a squared quartic, right?
you can simply substitute x²... you sure know this ;)


13th May 2004 01:46 UTC

I know how to deal with these problems.. i just can't be bothered to do anything :P

for it to all work properly what need to be done is:


if a=0
if b=0
if c=0
if d=0
if e=0
no roots
endif
else
if e=0
roots all zero
else
root is -e/d
endif
endif
else
if e=0
if d=0
roots all zero
else
one root is zero
the other is -d/c
endif
else
if d=0
roots are +-sqrt(-e/c)
else
solve cx^2+dx+e=0
endif
endif
endif
else
if e=0
if d=0
if c=0
all roots zero
else
two roots are zero
the other is -c/b
endif
else
if c=0
one root is zero
the other two are +-sqrt(-d/b)
else
one root is zero
solve bx^2+cx+d=0
endif
endif
else
if d=0
if c=0
roots are the cbrts of -e/b
else
solve bx^3+cx^2+e=0
endif
else
if c=0
solve bx^3+dx+e=0
else
solve bx^2+cx^2+dx+e=0
endif
endif
endif
endif
else
if e=0
if d=0
if c=0
if b=0
all roots are zero
else
three roots are zero
other is -b/a
endif
else
if b=0
two roots are zero
others are +/-sqrt(-c/a)
else
two roots are zero
solve ax^2+bx+c=0
endif
endif
else
if c=0
if b=0
one root is zero
others are cbrts of -d/a
else
solve ax^3+bx^2+d=0
endif
else
if b=0
one root is zero
others are solutions to ax^3+cx+d=0
else
one root is zero
others are solutions to ax^3+bx^2+cx+d=0
endif
endif
endif
else
if d=0
if c=0
if b=0
roots are the 4rts of -e/a
else
solve ax^4+bx^3+e=0
endif
else
if b=0
solve ax^2+cx+e=0
else
two roots are zero
solve ax^4+bx^3+cx^2+e=0
endif
endif
else
if c=0
if b=0
solve ax^4+dx+e=0
else
solve ax^4+bx^3+dx+e=0
endif
else
if b=0
solve ax^4+cx^2+dx+e=0
else
solve ax^4+bx^3+cx^2+dx+e=0
endif
endif
endif
endif
endif


i might just rework it from scratch since there are little tricks for solving some cases like:

bx^3+dx+e=0, ax^4+cx^2+dx+e=0, ax^4+dx+e=0

5th June 2004 21:25 UTC

Very cool! I know what it does but too complicated for me right now. Endless summer days should get me to make something cool to show.

If anyone is discuraged or angry because of the solvers speed, deleteall of the Render/Text. You will be happy.:)


7th June 2004 03:24 UTC

Every time you revive a dead thread with a meaningless and irrelevant post, God kills a kitten.

Please, shut up for the kittens.


26th June 2004 15:15 UTC

Maybe this thread can be locked and placed in the FAQ? Its pretty useful, people != me might actually want to use this code at some point.

And 'lol' at god killing kittens. :)