/* school_alg.c, Copyright (c) 2005-2006 David C. Lambert * * 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 #include #include #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); } } } }