In article <2479@orca.TEK.COM> brucec@orca.UUCP (Bruce Cohen) writes: >In article <6019@iuvax.UUCP> viking@iuvax writes: >> >>The major enhancements that Jim added to the program were to recode it in >>C and to use "a CORDIC rotator instead of trig functions". I want to talk >>with Jim about his program....where is he?! >> >>Alternately, does anyone know anything about "CORDIC rotators"? I'd like to >>see the source to his modification of Michiel's program. The algorithm used >>was based on one developed by LucasFilm Ltd. and published in the Sept. '84 >>Scientific American. The idea is really simple. A rotation can be expressed as: [ x' ] [ cos t -sin y ] [ x ] [ y' ] = [ sin t cos t ] [ y ] Factor out cos t, giving: [ x' ] [ 1 -tan y ] [ x ] [ y' ] = cos t [ tan t 1 ] [ y ] Now let t be expressed as a signed sum of -1 -i t = Sum { d tan 2 }, where d = {+1, -1} i i i This yields (please forgive the cruddy typesetting; Mathtype doesn't work on UNIX): -i [ x' ] [ 1 -d 2 ] [ x ] [ ] [ i ] [ ] [ ] = Sum { C } Sum { [ -i ] [ ] } [ y' ] i i i [ d 2 1 ] [ y ] i The first sum is a constant. The second sum is a series of shifts and adds (or subtracts). This is illustrated by the following working code, which performs all of the following operations: rotate a vector polar to rectangular conversion rectangular to polar conversion simultaneous calculation of sine and cosine. atan2() Note that the normalization and denormalization is done mainly to increase accuracy; if you're more concerned about speed, you'd probably hack this out entirely or just shift by a fixed amount. You can do similar sorts of things with hyperbolic functions, thereby being able to do exponential and logarithms. I have never written such code, although I would like to have it, especially to calculate gamma correction tables. ------------------------------------------------------------ # define RADIANS 1 # define COSCALE 0x22c2dd1c /* 0.271572 */ static long arctantab[32] = { # ifdef DEGREES /* MS 10 integral bits */ # define QUARTER (90 << 22) 266065460, 188743680, 111421900, 58872272, 29884485, 15000234, 7507429, 3754631, 1877430, 938729, 469366, 234683, 117342, 58671, 29335, 14668, 7334, 3667, 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1, 0, 0, # else # ifdef RADIANS /* MS 4 integral bits */ # define QUARTER ((int)(3.141592654 / 2.0 * (1 << 28))) 297197971, 210828714, 124459457, 65760959, 33381290, 16755422, 8385879, 4193963, 2097109, 1048571, 524287, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0, 0, # else # define BRADS 1 # define QUARTER (1 << 30) 756808418, 536870912, 316933406, 167458907, 85004756, 42667331, 21354465, 10679838, 5340245, 2670163, 1335087, 667544, 333772, 166886, 83443, 41722, 20861, 10430, 5215, 2608, 1304, 652, 326, 163, 81, 41, 20, 10, 5, 3, 1, 1, # endif # endif }; /* To rotate a vector through an angle of theta, we calculate: * * x' = x cos(theta) - y sin(theta) * y' = x sin(theta) + y cos(theta) * * The rotate() routine performs multiple rotations of the form: * * x[i+1] = cos(theta[i]) { x[i] - y[i] tan(theta[i]) } * y[i+1] = cos(theta[i]) { y[i] + x[i] tan(theta[i]) } * * with the constraint that tan(theta[i]) = pow(2, -i), which can be * implemented by shifting. We always shift by either a positive or * negative angle, so the convergence has the ringing property. Since the * cosine is always positive for positive and negative angles between -90 * and 90 degrees, a predictable magnitude scaling occurs at each step, * and can be compensated for instead at the end of the iterations by a * composite scaling of the product of all the cos(theta[i])'s. */ static pseudorotate(px, py, theta) long *px, *py; register long theta; { register int i; register long x, y, xtemp; register long *arctanptr; x = *px; y = *py; /* Get angle between -90 and 90 degrees */ while (theta < -QUARTER) { x = -x; y = -y; theta += 2 * QUARTER; } while (theta > QUARTER) { x = -x; y = -y; theta -= 2 * QUARTER; } /* Initial pseudorotation, with left shift */ arctanptr = arctantab; if (theta < 0) { xtemp = x + (y << 1); y = y - (x << 1); x = xtemp; theta += *arctanptr++; } else { xtemp = x - (y << 1); y = y + (x << 1); x = xtemp; theta -= *arctanptr++; } /* Subsequent pseudorotations, with right shifts */ for (i = 0; i <= 28; i++) { if (theta < 0) { xtemp = x + (y >> i); y = y - (x >> i); x = xtemp; theta += *arctanptr++; } else { xtemp = x - (y >> i); y = y + (x >> i); x = xtemp; theta -= *arctanptr++; } } *px = x; *py = y; } static pseudopolarize(argx, argy) long *argx, *argy; { register long theta; register long yi, i; register long x, y; register long *arctanptr; x = *argx; y = *argy; /* Get the vector into the right half plane */ theta = 0; if (x < 0) { x = -x; y = -y; theta = 2 * QUARTER; } if (y > 0) theta = - theta; arctanptr = arctantab; if (y < 0) { /* Rotate positive */ yi = y + (x << 1); x = x - (y << 1); y = yi; theta -= *arctanptr++; /* Subtract angle */ } else { /* Rotate negative */ yi = y - (x << 1); x = x + (y << 1); y = yi; theta += *arctanptr++; /* Add angle */ } for (i = 0; i <= 28; i++) { if (y < 0) { /* Rotate positive */ yi = y + (x >> i); x = x - (y >> i); y = yi; theta -= *arctanptr++; } else { /* Rotate negative */ yi = y - (x >> i); x = x + (y >> i); y = yi; theta += *arctanptr++; } } *argx = x; *argy = theta; } /* Fxprenorm() block normalizes the arguments to a magnitude suitable for * CORDIC pseudorotations. The returned value is the block exponent. */ static int fxprenorm(argx, argy) long *argx, *argy; { register long x, y; register int shiftexp; int signx, signy; shiftexp = 0; /* Block normalization exponent */ signx = signy = 1; if ((x = *argx) < 0) { x = -x; signx = -signx; } if ((y = *argy) < 0) { y = -y; signy = -signy; } /* Prenormalize vector for maximum precision */ if (x < y) { /* |y| > |x| */ while (y < (1 << 27)) { x <<= 1; y <<= 1; shiftexp--; } while (y > (1 << 28)) { x >>= 1; y >>= 1; shiftexp++; } } else { /* |x| > |y| */ while (x < (1 << 27)) { x <<= 1; y <<= 1; shiftexp--; } while (x > (1 << 28)) { x >>= 1; y >>= 1; shiftexp++; } } *argx = (signx < 0) ? -x : x; *argy = (signy < 0) ? -y : y; return(shiftexp); } /* Return a unit vector corresponding to theta. * sin and cos are fixed-point fractions. */ fxunitvec(cos, sin, theta) long *cos, *sin, theta; { *cos = COSCALE >> 1; /* Save a place for the integer bit */ *sin = 0; pseudorotate(cos, sin, theta); /* Saturate results to fractions less than 1, shift out integer bit */ if (*cos >= (1 << 30)) *cos = 0x7FFFFFFF; /* Just shy of 1 */ else if (*cos <= -(1 << 30)) *cos = 0x80000001; /* Just shy of -1*/ else *cos <<= 1; if (*sin >= (1 << 30)) *sin = 0x7FFFFFFF; /* Just shy of 1 */ else if (*sin <= -(1 << 30)) *sin = 0x80000001; /* Just shy of -1*/ else *sin <<= 1; } /* Fxrotate() rotates the vector (argx, argy) by the angle theta. */ fxrotate(argx, argy, theta) long *argx, *argy; long theta; { register long x, y; int shiftexp; long frmul(); if (((*argx || *argy) == 0) || (theta == 0)) return; shiftexp = fxprenorm(argx, argy); /* Prenormalize vector */ pseudorotate(argx, argy, theta); /* Perform CORDIC pseudorotation */ x = frmul(*argx, COSCALE); /* Compensate for CORDIC enlargement */ y = frmul(*argy, COSCALE); if (shiftexp < 0) { /* Denormalize vector */ *argx = x >> -shiftexp; *argy = y >> -shiftexp; } else { *argx = x << shiftexp; *argy = y << shiftexp; } } fxatan2(x, y) long x, y; { if ((x || y) == 0) return(0); fxprenorm(&x, &y); /* Prenormalize vector for maximum precision */ pseudopolarize(&x, &y); /* Convert to polar coordinates */ return(y); } fxpolarize(argx, argy) long *argx, *argy; { int shiftexp; long frmul(); if ((*argx || *argy) == 0) { *argx = 0; /* Radius */ *argy = 0; /* Angle */ return; } /* Prenormalize vector for maximum precision */ shiftexp = fxprenorm(argx, argy); /* Perform CORDIC conversion to polar coordinates */ pseudopolarize(argx, argy); /* Scale radius to undo pseudorotation enlargement factor */ *argx = frmul(*argx, COSCALE); /* Denormalize radius */ *argx = (shiftexp < 0) ? (*argx >> -shiftexp) : (*argx << shiftexp); } # ifdef vax long frmul(a, b) /* Just for testing */ long a, b; { /* This routine should be written in assembler to calculate the * high part of the product, i.e. the product when the operands * are considered fractions. */ return((a >> 16) * (b >> 15)); } # endif ##### This is the end of my posting ##### -- Ken Turkowski @ Apple Computer, Inc., Cupertino, CA UUCP: {mtxinu,sun,decwrl,nsc,voder}!apple!turk CSNET: turk@Apple.CSNET ARPA: turk%Apple@csnet-relay.ARPA Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|