Animations 2a, 2b updated.

master
Ben Burlingham 8 years ago
parent 21ba248754
commit 04a37ceb53
  1. 14
      index.html
  2. 21
      js/animation.js
  3. 95
      js/animation2a.js
  4. 95
      js/animation2b.js
  5. 1345
      js/bundle.js
  6. 37
      js/controls.js
  7. 10
      js/index.js
  8. 45
      js/particle.js

@ -43,7 +43,7 @@
<div class='outerContainer' id='1b'></div> <div class='outerContainer' id='1b'></div>
<!-- <p> <p>
<h4>Exploration 2: Grid-based vision</h4> <h4>Exploration 2: Grid-based vision</h4>
</p> </p>
@ -52,21 +52,15 @@
against the global grid to detect collisions. Here's a small scale system, with the vision grid visualized: against the global grid to detect collisions. Here's a small scale system, with the vision grid visualized:
</p> </p>
<div class='outerContainer'> <div class='outerContainer' id='2a'></div>
<div id="animation2a" class='animationContainer'></div>
<div id="controls2a" class='controlsContainer'></div>
</div>
<p> <p>
Here's a large scale system. Here's a large scale system.
</p> </p>
<div class='outerContainer'> <div class='outerContainer' id='2b'></div>
<div id="animation2b" class='animationContainer'></div>
<div id="controls2b" class='controlsContainer'></div>
</div>
<p> <!-- <p>
The last goal was to play around with flocking behavior. Cohesion: The last goal was to play around with flocking behavior. Cohesion:
</p> </p>

@ -2,9 +2,10 @@ import Rx, { Observable } from 'rxjs';
import Grid from './grid'; import Grid from './grid';
import Particle from './particle'; import Particle from './particle';
import Controls from './controls'; import Controls from './controls';
import Random from './random';
import { CONTROLS, ENTITIES } from './enums'; import { CONTROLS, ENTITIES } from './enums';
function Animation(observables, id) { function Animation(observables, id, showHazards) {
this.id = id; this.id = id;
this.observables = observables; this.observables = observables;
this.particles = []; this.particles = [];
@ -42,4 +43,22 @@ Animation.prototype.subscribeCount = function(count) {
} }
} }
Animation.prototype.addHazards = function() {
const bounds = this.container.getBoundingClientRect();
const n = Random.num(1, 3);
for (let i = 0; i < n; i++) {
const w = Random.num(50, 200);
const h = Random.num(50, 200);
this.grid.setArea({
x: Random.num(0, bounds.width - w),
y: Random.num(0, bounds.height - h),
w,
h,
type: ENTITIES.HAZARD
}, this.container);
}
}
export default Animation; export default Animation;

@ -1,93 +1,14 @@
import Rx, { Observable } from 'rxjs'; import Animation from './animation';
import Particle from './particle';
import Grid from './grid';
import Controls from './controls'; import Controls from './controls';
import Random from './random';
import { CONTROLS, ENTITIES } from './enums';
function Animation2a() { export default function(destroy$) {
this.options = { const id = '2a';
count: 3, const config = {
id,
maxCount: 10, maxCount: 10,
showVisionGrid: true, showVisionGridControl: true
speed: 4
}; };
this.container = document.getElementById('animation2a'); const observables = Controls(destroy$, config);
this.particles = []; (new Animation(observables, id, true)).addHazards();
this.grid = this.createGlobalGrid();
const controls = new Controls(
document.getElementById('controls2a'),
this.options
);
controls.mount().subscribe(this.subscriber.bind(this));
this.updateAnimating(this.options.animating);
this.updateCount(this.options.count);
};
Animation2a.prototype.subscriber = function({ key, value }) {
switch(key) {
case CONTROLS.ANIMATING: this.updateAnimating(value); break;
case CONTROLS.COUNT: this.updateCount(value); break;
case CONTROLS.SPEED: this.updateSpeed(value); break;
}
}
Animation2a.prototype.nextFrame = function() {
this.particles.forEach(p => p.nextFrame());
}
Animation2a.prototype.updateAnimating = function(isAnimating) {
this.options.animating = isAnimating;
if (isAnimating) {
const fps$ = Rx.Observable.interval(1000 / 32)
.takeWhile(_ => this.options.animating);
fps$.subscribe(this.nextFrame.bind(this));
}
}
Animation2a.prototype.updateCount = function(count) {
const bounds = this.container.getBoundingClientRect();
while (this.particles.length > count) {
delete this.particles.pop().remove();
}
while (this.particles.length < count) {
const p = new Particle(this.container, bounds, this.options, this.grid);
this.particles.push(p);
}
} }
Animation2a.prototype.updateSpeed = function(value) {
this.options.speed = value;
this.particles.forEach(p => p.updateConfig({ speed: value }));
}
Animation2a.prototype.createGlobalGrid = function() {
const bounds = this.container.getBoundingClientRect();
const grid = new Grid();
const n = Random.num(1, 3);
for (let i = 0; i < n; i++) {
const w = Random.num(50, 200);
const h = Random.num(50, 200);
grid.setArea({
x: Random.num(0, bounds.width - w),
y: Random.num(0, bounds.height - h),
w,
h,
type: ENTITIES.HAZARD
}, this.container);
}
return grid;
}
export default Animation2a;

@ -1,92 +1,13 @@
import Rx, { Observable } from 'rxjs'; import Animation from './animation';
import Particle from './particle';
import Grid from './grid';
import Controls from './controls'; import Controls from './controls';
import Random from './random';
import { CONTROLS, ENTITIES } from './enums';
function Animation2b() { export default function(destroy$) {
this.options = { const id = '2b';
count: 1, const config = {
maxCount: 1000, id,
speed: 4 maxCount: 1000
}; };
this.container = document.getElementById('animation2b'); const observables = Controls(destroy$, config);
this.particles = []; new Animation(observables, id).addHazards();
this.grid = this.createGlobalGrid(this.container, this.bounds);
const controls = new Controls(
document.getElementById('controls2b'),
this.options
);
controls.mount().subscribe(this.subscriber.bind(this));
this.updateAnimating(this.options.animating);
this.updateCount(this.options.count);
};
Animation2b.prototype.subscriber = function({ key, value }) {
switch(key) {
case CONTROLS.ANIMATING: this.updateAnimating(value); break;
case CONTROLS.COUNT: this.updateCount(value); break;
case CONTROLS.SPEED: this.updateSpeed(value); break;
}
}
Animation2b.prototype.nextFrame = function() {
this.particles.forEach(p => p.nextFrame());
}
Animation2b.prototype.updateAnimating = function(isAnimating) {
this.options.animating = isAnimating;
if (isAnimating) {
const fps$ = Rx.Observable.interval(1000 / 32)
.takeWhile(_ => this.options.animating);
fps$.subscribe(this.nextFrame.bind(this));
}
}
Animation2b.prototype.updateCount = function(count) {
const bounds = this.container.getBoundingClientRect();
while (this.particles.length > count) {
delete this.particles.pop().remove();
}
while (this.particles.length < count) {
const p = new Particle(this.container, bounds, this.options, this.grid);
this.particles.push(p);
}
} }
Animation2b.prototype.updateSpeed = function(value) {
this.options.speed = value;
this.particles.forEach(p => p.updateConfig({ speed: value }));
}
Animation2b.prototype.createGlobalGrid = function() {
const bounds = this.container.getBoundingClientRect();
const grid = new Grid();
const n = Random.num(1, 3);
for (let i = 0; i < n; i++) {
const w = Random.num(50, 200);
const h = Random.num(50, 200);
grid.setArea({
x: Random.num(0, bounds.width - w),
y: Random.num(0, bounds.height - h),
w,
h,
type: ENTITIES.HAZARD
}, this.container);
}
return grid;
}
export default Animation2b;

File diff suppressed because one or more lines are too long

37
js/controls.js vendored

@ -6,7 +6,8 @@ export default function(destroy$, {
id, id,
maxCount = 10, maxCount = 10,
showCircleControl = false, showCircleControl = false,
showRandomizeControl = false showRandomizeControl = false,
showVisionGridControl = false
}) { }) {
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'controlsContainer'; container.className = 'controlsContainer';
@ -17,9 +18,10 @@ export default function(destroy$, {
fps$: new Rx.Subject(), fps$: new Rx.Subject(),
count$: createCountControl(container, 0, maxCount), count$: createCountControl(container, 0, maxCount),
speed$: createSpeedControl(container), speed$: createSpeedControl(container),
circle$: showCircleControl ? createCircleControl(container) : undefined, circle$: showCircleControl && createCircleControl(container),
randomize$: showRandomizeControl ? createRandomizeControl(container) : undefined, randomize$: showRandomizeControl && createRandomizeControl(container),
animating$: createAnimatingControl(container), vision$: showVisionGridControl && createVisionGridControl(container),
animating$: createAnimatingControl(container)
}; };
observables.animating$.subscribe((isAnimating) => { observables.animating$.subscribe((isAnimating) => {
@ -192,3 +194,30 @@ function createRandomizeControl(container) {
return randomize$; return randomize$;
} }
function createVisionGridControl(container) {
const label = document.createElement('label');
label.className = 'controls-checkbox';
const text = document.createElement('span');
text.innerHTML = 'Show vision grid';
text.className = 'controls-checkbox-text';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'controls-checkbox-input';
checkbox.checked = true;
label.appendChild(checkbox);
label.appendChild(text);
container.appendChild(label);
const vision$ = new Rx.BehaviorSubject(true);
label.addEventListener('change', (evt) => {
vision$.next(evt.target.checked);
});
return vision$;
}

@ -1,10 +1,8 @@
import Rx, { Observable } from 'rxjs'; import Rx, { Observable } from 'rxjs';
import Animation1a from './animation1a'; import Animation1a from './animation1a';
import Animation1b from './animation1b'; import Animation1b from './animation1b';
// import Container1b from './container1b'; import Animation2a from './animation2a';
// import Container2a from './container2a'; import Animation2b from './animation2b';
// import Container2b from './container2b';
// import Container3a from './container3a';
require('../css/reset.scss'); require('../css/reset.scss');
require('../css/index.scss'); require('../css/index.scss');
@ -16,6 +14,8 @@ window.addEventListener('load', () => {
Animation1a(destroy$); Animation1a(destroy$);
Animation1b(destroy$); Animation1b(destroy$);
Animation2a(destroy$);
Animation2b(destroy$);
}); });
// TODO remove bottom padding from Disqus // TODO remove bottom padding from Disqus
@ -24,6 +24,8 @@ window.addEventListener('load', () => {
// TODO sort out particle nextframe // TODO sort out particle nextframe
// TODO abs positioning on controls elements so order doesn't matter // TODO abs positioning on controls elements so order doesn't matter
// TODO BehaviorSubject listener on bounds change // TODO BehaviorSubject listener on bounds change
// TODO are vision grid nodes removed properly
// TODO overlapping grid points
// TODO ANIM1ab free movement // TODO ANIM1ab free movement

@ -39,6 +39,12 @@ function Particle(parent, bounds, globalGrid, observables) {
this.isLeader = false; this.isLeader = false;
this.arc = Arc.create(bounds, this.grids.global); this.arc = Arc.create(bounds, this.grids.global);
// If starting in a hazard, recurse.
while (this.grids.global.getPoint({ x: this.arc.endX, y: this.arc.endY, type: ENTITIES.HAZARD}) !== undefined) {
this.arc = Arc.create(bounds, this.grids.global);
}
// this.grids.global.setPoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }, p); // USE ID? // this.grids.global.setPoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }, p); // USE ID?
this.remove$ = new Rx.Subject(); this.remove$ = new Rx.Subject();
@ -51,12 +57,17 @@ function Particle(parent, bounds, globalGrid, observables) {
.takeUntil(this.remove$) .takeUntil(this.remove$)
.subscribe(this.subscribeSpeed.bind(this)); .subscribe(this.subscribeSpeed.bind(this));
observables.randomize$ && observables.randomize$
.takeUntil(this.remove$)
.subscribe(this.subscribeRandomize.bind(this));
observables.circle$ && observables.circle$ observables.circle$ && observables.circle$
.takeUntil(this.remove$) .takeUntil(this.remove$)
.subscribe(this.subscribeCircle.bind(this)); .subscribe(this.subscribeCircle.bind(this));
observables.vision$ && observables.vision$
// observables.randomize$.subscribe(this.subscribeRandomize.bind(this)); .takeUntil(this.remove$)
.subscribe(this.subscribeVision.bind(this));
}; };
// ===== PROTOTYPE ===== // ===== PROTOTYPE =====
@ -85,12 +96,12 @@ Particle.prototype.subscribeNextFrame = function() {
this.arc = Arc.randomize(this.arc); this.arc = Arc.randomize(this.arc);
} }
// this.grids.vision = updateVisionGrid(this.arc, this.config, this.grids); this.grids.vision = updateVisionGrid(this.arc, this.config, this.grids);
// const { hazards, particles } = look(this.arc, this.grids); const { hazards, particles } = look(this.arc, this.grids);
// if (hazards.length > 0) { if (hazards.length > 0) {
// this.arc = Arc.evade(this.arc); this.arc = Arc.evade(this.arc);
// } }
// this.updateLeader(particles); // this.updateLeader(particles);
@ -110,13 +121,6 @@ Particle.prototype.subscribeSpeed = function(value) {
this.config.speed = value; this.config.speed = value;
} }
// if (showVision === true && this.nodes.visionGrid === undefined) {
// this.nodes.visionGrid = createVisionGridNodes(this.config, this.grids, this.nodes);
// }
//
// if (showVision === false && this.nodes.visionGrid !== undefined) {
// delete this.nodex.visionGrid;
// }
Particle.prototype.subscribeCircle = function(show) { Particle.prototype.subscribeCircle = function(show) {
if (show === false) { if (show === false) {
@ -132,6 +136,15 @@ Particle.prototype.subscribeRandomize = function(value) {
this.config.randomize = value; this.config.randomize = value;
} }
Particle.prototype.subscribeVision = function(show) {
if (show === false) {
this.nodes.visionGrid.forEach(n => n.parentNode.removeChild(n));
delete this.nodes.visionGrid;
} else {
this.nodes.visionGrid = createVisionGridNodes(this.config, this.grids, this.nodes);
}
}
Particle.prototype.updateLeader = function(particles) { Particle.prototype.updateLeader = function(particles) {
if (this.config.behavior !== BEHAVIOR.COHESION) { if (this.config.behavior !== BEHAVIOR.COHESION) {
return; return;
@ -249,10 +262,6 @@ function createVisionGrid(config) {
} }
function createVisionGridNodes(config, grids, nodes) { function createVisionGridNodes(config, grids, nodes) {
if (config.showVision === false) {
return undefined;
}
return grids.vision.reduce((acc, { x, y }) => { return grids.vision.reduce((acc, { x, y }) => {
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'particle-vision-dot'; div.className = 'particle-vision-dot';

Loading…
Cancel
Save