UNPKG

ts-useful

Version:

Functions for animation, color transitions, ecliptic, bezier, decasteljau, curves, three dimensional curves, smooth scrolling, random range, randomItem, mobius index, vectors, physics vectors, and easing.

121 lines 6.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Boids = void 0; const randomrange_1 = require("./randomrange"); class Boids { constructor(options) { this.attractors = []; this.boids = []; this.defAttractor = { position: { x: Infinity, y: Infinity }, radius: 10, force: .25 }; this.deadPoint = { x: 0, y: 0 }; // double-dog-leg hypothenuse approximation // http://forums.parallax.com/discussion/147522/dog-leg-hypotenuse-approximation this.hypot = (a, b) => { [a, b] = [Math.abs(a), Math.abs(b)]; const [n, x] = [Math.min(a, b), Math.max(a, b)]; return x + 3 * n / 32 + Math.max(0, 2 * n - x) / 8 + Math.max(0, 4 * n - x) / 16; }; this.hypotCoord = (coord) => this.hypot(coord.x, coord.y); this.sqr = (n) => n * n; this.dSqr = (p) => this.sqr(p.x) + this.sqr(p.y); this.ap = (p1, p2) => { return { x: p1.x + p2.x, y: p1.y + p2.y }; }; this.sp = (p1, p2) => { return { x: p1.x - p2.x, y: p1.y - p2.y }; }; this.mp = (p, m) => { return { x: p.x * m, y: p.y * m }; }; this.af = (m, p, fp) => { const h = this.hypotCoord(fp); p.x += (m * (fp.x / h)) || 0; p.y += (m * (fp.y / h)) || 0; return p; }; this.sf = (m, p, fp) => { const h = this.hypotCoord(fp); p.x -= (m * (fp.x / h)) || 0; p.y -= (m * (fp.y / h)) || 0; return p; }; // Update the attractors to the target position. this.TargetPositions = (targets) => { const defaults = { speed: this.speedLimit, distance: this.separationDistance }; this.attractors = targets.map((t) => { return { ...this.defAttractor, ...t }; }); }; this.Update = async () => { const { accelerationLimitRoot, accelerationLimit, alignmentDistance, alignmentForce, attractors, boids, cohesionDistance, cohesionForce, separationDistance, separationForce, speedLimitRoot, speedLimit } = this; let size = boids.length; let current = size; let [target, attractorCount, ratio] = [0, attractors.length, 0]; while (current--) { let [sforce, cforce, aforce, xforce, attractor] = [this.deadPoint, this.deadPoint, this.deadPoint, this.deadPoint, {}]; // Attractors target = attractorCount; while (target--) { attractor = attractors[target]; xforce = this.sp(boids[current].position, attractor.position); if (this.dSqr(xforce) < Math.sqrt(attractor.radius)) { boids[current].speed = this.sf(attractor.force, boids[current].speed, xforce); } } target = size; while (target--) { if (target === current) continue; xforce = this.sp(boids[current].position, boids[target].position); const ds = this.dSqr(xforce); if (ds < separationDistance) { sforce = this.ap(sforce, xforce); } else { if (ds < cohesionDistance) { cforce = this.ap(cforce, xforce); } if (ds < alignmentDistance) { aforce = this.ap(aforce, boids[target].speed); } } } boids[current].acceleration = this.af(separationForce, boids[current].acceleration, sforce); // Separation boids[current].acceleration = this.sf(cohesionForce, boids[current].acceleration, cforce); // Cohesion boids[current].acceleration = this.sf(alignmentForce, boids[current].acceleration, aforce); // Alignment } current = size; // Apply speed/acceleration for this tick. while (current--) { if (accelerationLimit && this.dSqr(boids[current].acceleration) > accelerationLimit) { ratio = accelerationLimitRoot / this.hypotCoord(boids[current].acceleration); boids[current].acceleration = this.mp(boids[current].acceleration, ratio); } boids[current].speed = this.ap(boids[current].speed, boids[current].acceleration); if (speedLimit && this.dSqr(boids[current].speed) > speedLimit) { ratio = speedLimitRoot / this.hypotCoord(boids[current].speed); boids[current].speed = this.mp(boids[current].speed, ratio); } boids[current].position = this.ap(boids[current].position, boids[current].speed); } return; }; this.blankBoids = (count, position) => { const rPos = () => (0, randomrange_1.RandomRange)(0, this.separationDistance * 2) - this.separationDistance; const [defSpeed, defAcceleration] = [{ x: this.speedLimit, y: this.speedLimit }, { x: this.accelerationLimit, y: this.accelerationLimit }]; return Array.apply(null, new Array(count)).map(() => { return { position: position ?? { x: rPos(), y: rPos() }, speed: defSpeed, acceleration: defAcceleration }; }); }; let { speedLimit, accelerationLimit, separationDistance, alignmentDistance, cohesionDistance, separationForce, cohesionForce, alignmentForce, attractors, alignment, boids } = (options ?? {}); boids ?? (boids = 50); this.speedLimitRoot = (speedLimit || 0); this.accelerationLimitRoot = (accelerationLimit || 1); this.speedLimit = Math.pow(this.speedLimitRoot, 2); this.accelerationLimit = Math.pow(this.accelerationLimitRoot, 2); this.separationDistance = Math.pow(separationDistance || 60, 2); this.alignmentDistance = Math.pow(alignmentDistance || 180, 2); this.cohesionDistance = Math.pow(cohesionDistance || 180, 2); this.separationForce = separationForce || .15; this.cohesionForce = cohesionForce || .1; this.alignmentForce = alignmentForce || alignment || .25; this.attractors = attractors || []; this.boids = this.blankBoids(boids); } } exports.Boids = Boids; //# sourceMappingURL=boids.js.map