diff options
Diffstat (limited to 'hacks/glx/glschool_alg.c')
-rw-r--r-- | hacks/glx/glschool_alg.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/hacks/glx/glschool_alg.c b/hacks/glx/glschool_alg.c new file mode 100644 index 0000000..32b4a5f --- /dev/null +++ b/hacks/glx/glschool_alg.c @@ -0,0 +1,364 @@ +/* school_alg.c, Copyright (c) 2005-2006 David C. Lambert <dcl@panix.com> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "yarandom.h" +#include "glschool_alg.h" + +/* for xscreensaver */ +#undef drand48 +#define drand48() frand(1.0) + +#define RAD2DEG (180.0/3.1415926535) + + +static inline double +norm(double *dv) +{ + return sqrt(dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]); +} + + +static inline void +addVector(double *v, double *d) +{ + v[0] += d[0]; + v[1] += d[1]; + v[2] += d[2]; +} + + +static inline void +clearVector(double *v) +{ + v[0] = v[1] = v[2] = 0.0; +} + + +static inline void +scaleVector(double *v, double s) +{ + v[0] *= s; + v[1] *= s; + v[2] *= s; +} + + +static inline void +addScaledVector(double *v, double *d, double s) +{ + v[0] += d[0]*s; + v[1] += d[1]*s; + v[2] += d[2]*s; +} + + +static inline void +getDifferenceVector(double *v0, double *v1, double *diff) +{ + diff[0] = v0[0] - v1[0]; + diff[1] = v0[1] - v1[1]; + diff[2] = v0[2] - v1[2]; +} + + +void +glschool_initFish(Fish *f, double *mins, double *ranges) +{ + int i; + + for(i = 0; i < 3; i++) { + FISH_IPOS(f, i) = mins[i] + drand48()*ranges[i]; + FISH_IACC(f, i) = 0.0; + FISH_IVEL(f, i) = drand48(); + FISH_IMAGIC(f, i) = 0.70 + 0.60*drand48(); + FISH_IOLDVEL(f, i) = 0.0; + } +} + + +void +glschool_initFishes(School *s) +{ + int i; + Fish *f = (Fish *)0; + int nFish = SCHOOL_NFISH(s); + BBox *bbox = &SCHOOL_BBOX(s); + double *mins = BBOX_MINS(bbox); + double *ranges = SCHOOL_BBRANGES(s); + Fish *theFishes = SCHOOL_FISHES(s); + + for(i = 0, f = theFishes; i < nFish; i++, f++) + glschool_initFish(f, mins, ranges); +} + + +static void +applyFishMovements(Fish *f, BBox *bbox, double minVel, double maxVel, double momentum) +{ + int i; + int oob = 0; + double vMag = 0.0; + + for(i = 0; i < 3; i++) { + double pos = FISH_IPOS(f, i); + oob = (pos > BBOX_IMAX(bbox, i) || pos < BBOX_IMIN(bbox, i)); + if (oob == 0) FISH_IVEL(f, i) += FISH_IACC(f, i) * FISH_IMAGIC(f, i); + vMag += (FISH_IVEL(f, i) * FISH_IVEL(f, i)); + } + vMag = sqrt(vMag); + + if (vMag > maxVel) + scaleVector(FISH_VEL(f), maxVel/vMag); + else if (vMag < minVel) + scaleVector(FISH_VEL(f), minVel/vMag); + + for(i = 0; i < 3; i++) { + FISH_IVEL(f, i) = momentum * FISH_IOLDVEL(f, i) + (1.0-momentum) * FISH_IVEL(f, i); + FISH_IPOS(f, i) += FISH_IVEL(f, i); + FISH_IOLDVEL(f, i) = FISH_IVEL(f, i); + + if (FISH_IPOS(f, i) < BBOX_IMIN(bbox, i)) + FISH_IPOS(f, i) = BBOX_IMAX(bbox, i); + else if (FISH_IPOS(f, i) > BBOX_IMAX(bbox, i)) + FISH_IPOS(f, i) = BBOX_IMIN(bbox, i); + } +} + + +void +glschool_applyMovements(School *s) +{ + int i = 0; + Fish *f = (Fish *)0; + int nFish = SCHOOL_NFISH(s); + double minVel = SCHOOL_MINVEL(s); + double maxVel = SCHOOL_MAXVEL(s); + double momentum = SCHOOL_MOMENTUM(s); + BBox *bbox = &SCHOOL_BBOX(s); + Fish *theFishes = SCHOOL_FISHES(s); + + for(i = 0, f = theFishes; i < nFish; i++, f++) + applyFishMovements(f, bbox, minVel, maxVel, momentum); +} + + +School * +glschool_initSchool(int nFish, double accLimit, double maxV, double minV, double distExp, double momentum, + double minRadius, double avoidFact, double matchFact, double centerFact, double targetFact, + double distComp) +{ + School *s = (School *)0; + + if ((s = (School *)malloc(sizeof(School))) == (School *)0) { + perror("initSchool School allocation failed: "); + return s; + } + + if ((SCHOOL_FISHES(s) = (Fish *)malloc(sizeof(Fish)*nFish)) == (Fish *)0) { + perror("initSchool Fish array allocation failed: "); + free(s); + return (School *)0; + } + + SCHOOL_NFISH(s) = nFish; + SCHOOL_ACCLIMIT(s) = accLimit; + SCHOOL_MAXVEL(s) = maxV; + SCHOOL_MINVEL(s) = minV; + SCHOOL_DISTEXP(s) = distExp; + SCHOOL_MOMENTUM(s) = momentum; + SCHOOL_MINRADIUS(s) = minRadius; + SCHOOL_MINRADIUSEXP(s) = pow(minRadius, distExp); + SCHOOL_MATCHFACT(s) = matchFact; + SCHOOL_AVOIDFACT(s) = avoidFact; + SCHOOL_CENTERFACT(s) = centerFact; + SCHOOL_TARGETFACT(s) = targetFact; + SCHOOL_DISTCOMP(s) = distComp; + + return s; +} + +void +glschool_freeSchool(School *s) +{ + free(SCHOOL_FISHES(s)); + free(s); +} + +void +glschool_setBBox(School *s, double xMin, double xMax, double yMin, double yMax, double zMin, double zMax) +{ + int i; + BBox *bbox = &SCHOOL_BBOX(s); + + BBOX_XMIN(bbox) = xMin; BBOX_XMAX(bbox) = xMax; + BBOX_YMIN(bbox) = yMin; BBOX_YMAX(bbox) = yMax; + BBOX_ZMIN(bbox) = zMin; BBOX_ZMAX(bbox) = zMax; + + for(i = 0; i < 3; i++) { + SCHOOL_IMID(s, i) = BBOX_IMID(bbox, i); + SCHOOL_IRANGE(s, i) = BBOX_IRANGE(bbox, i); + } +} + + +void +glschool_newGoal(School *s) +{ + SCHOOL_IGOAL(s,0) = 0.85*(drand48()-0.5)*SCHOOL_IRANGE(s,0) + SCHOOL_IMID(s,0); + SCHOOL_IGOAL(s,1) = 0.40*(drand48()-0.5)*SCHOOL_IRANGE(s,1) + SCHOOL_IMID(s,1); + SCHOOL_IGOAL(s,2) = 0.85*(drand48()-0.5)*SCHOOL_IRANGE(s,2) + SCHOOL_IMID(s,2); +} + + +double +glschool_computeNormalAndThetaToPlusZ(double *v, double *xV) +{ + double x1 = 0.0; + double y1 = 0.0; + double z1 = 1.0; + double x2 = v[0]; + double y2 = v[1]; + double z2 = v[2]; + double theta = 0.0; + double xVNorm = 0.0; + double sinTheta = 0.0; + double v2Norm = norm(v); + + if (v2Norm == 0.0) { + xV[1] = 1.0; + xV[0] = xV[2] = 0.0; + return theta; + } + xV[0] = (y1*z2 - z1*y2); + xV[1] = -(x1*z2 - z1*x2); + xV[2] = (x1*y2 - y1*x2); + xVNorm = norm(xV); + + sinTheta = xVNorm/v2Norm; + return (asin(sinTheta) * RAD2DEG); +} + + +int +glschool_computeGroupVectors(School *s, Fish *ref, double *avoidance, double *centroid, double *avgVel) +{ + int i; + double dist; + double adjDist; + double diffVect[3]; + int neighborCount = 0; + Fish *test = (Fish *)0; + int nFish = SCHOOL_NFISH(s); + double distExp = SCHOOL_DISTEXP(s); + double distComp = SCHOOL_DISTCOMP(s); + double minRadiusExp = SCHOOL_MINRADIUSEXP(s); + Fish *fishes = SCHOOL_FISHES(s); + + for(i = 0, test = fishes; i < nFish; i++, test++) { + if (test == ref) continue; + + getDifferenceVector(FISH_POS(ref), FISH_POS(test), diffVect); + + dist = norm(diffVect) - distComp; + if (dist < 0.0) dist = 0.1; + + adjDist = pow(dist, distExp); + if (adjDist > minRadiusExp) continue; + + neighborCount++; + + addVector(avgVel, FISH_VEL(test)); + addVector(centroid, FISH_POS(test)); + + addScaledVector(avoidance, diffVect, 1.0/adjDist); + } + if (neighborCount > 0) { + scaleVector(avgVel, 1.0/neighborCount); + scaleVector(centroid, 1.0/neighborCount); + } + return neighborCount; +} + + +void +glschool_computeAccelerations(School *s) +{ + int i; + int j; + int neighborCount; + double dist; + double adjDist; + double accMag; + double avgVel[3]; + double diffVect[3]; + double centroid[3]; + double avoidance[3]; + Fish *ref = (Fish *)0; + int nFish = SCHOOL_NFISH(s); + double *goal = SCHOOL_GOAL(s); + double distExp = SCHOOL_DISTEXP(s); + double distComp = SCHOOL_DISTCOMP(s); + double avoidFact = SCHOOL_AVOIDFACT(s); + double matchFact = SCHOOL_MATCHFACT(s); + double centerFact = SCHOOL_CENTERFACT(s); + double targetFact = SCHOOL_TARGETFACT(s); + double accLimit = SCHOOL_ACCLIMIT(s); + double minRadius = SCHOOL_MINRADIUS(s); + Fish *fishes = SCHOOL_FISHES(s); + + for(i = 0, ref = fishes; i < nFish; i++, ref++) { + clearVector(avgVel); + clearVector(centroid); + clearVector(avoidance); + clearVector(FISH_ACC(ref)); + neighborCount = glschool_computeGroupVectors(s, ref, avoidance, centroid, avgVel); + + /* avoidanceAccel[] = avoidance[] * AvoidFact */ + scaleVector(avoidance, avoidFact); + addVector(FISH_ACC(ref), avoidance); + + accMag = norm(FISH_ACC(ref)); + if (neighborCount > 0 && accMag < accLimit) { + for(j = 0; j < 3; j++) { + FISH_IAVGVEL(ref, j) = avgVel[j]; + FISH_IACC(ref, j) += ((avgVel[j] - FISH_IVEL(ref, j)) * matchFact); + } + + accMag = norm(FISH_ACC(ref)); + if (accMag < accLimit) { + for(j = 0; j < 3; j++) + FISH_IACC(ref, j) += ((centroid[j] - FISH_IPOS(ref, j)) * centerFact); + } + } + + accMag = norm(FISH_ACC(ref)); + if (accMag < accLimit) { + getDifferenceVector(goal, FISH_POS(ref), diffVect); + + dist = norm(diffVect) - distComp; + if (dist < 0.0) dist = 0.1; + + /*adjDist = pow(dist, distExp);*/ + if (dist > minRadius) { + adjDist = pow(dist, distExp); + for(j = 0; j < 3; j++) + FISH_IACC(ref, j) += (diffVect[j]*targetFact/adjDist); + } + } + } +} |