Global management for 3a, 3b.

master
Ben Burlingham 8 years ago
parent 04a37ceb53
commit a88b05f478
  1. 35
      index.html
  2. 88
      js/animation3a.js
  3. 16
      js/animation3b.js
  4. 25
      js/arc.js
  5. 1398
      js/bundle.js
  6. 5
      js/index.js
  7. 39
      js/particle.js

@ -29,10 +29,16 @@
</p> </p>
<p> <p>
The trickiest portion of this iteration was animating the curved paths. Elliptical geometry was the initial goal, The trickiest portion of this iteration (and perhaps the whole project) was animating the curved paths:
but calculating arc length (to maintain a scalar speed) is quite difficult. Smoothstep cubic curves </p>
were also an option, but maintaining consistent entry and exit angles could affect performance for large
groups. The current design uses circular paths that allow smooth changes in both direction and rotation.
<ul>
<li>Elliptical geometry was the initial goal, but calculating arc length (to maintain a scalar speed) is quite difficult.</li>
<li>Smoothstep cubic curves were also an option, but maintaining consistent entry and exit angles could affect performance for large
groups.</li>
<p>
The current design uses circular paths that allow smooth changes in both direction and rotation.
</p> </p>
<div class='outerContainer' id='1a'></div> <div class='outerContainer' id='1a'></div>
@ -60,14 +66,23 @@
<div class='outerContainer' id='2b'></div> <div class='outerContainer' id='2b'></div>
<!-- <p> <p>
The last goal was to play around with flocking behavior. Cohesion: The last goal was to play around with flocking behaviors: cohesion, separation, and alignment.
Here's each of the three, with the vision grid visualized:
</p>
<p>
<h4>Exploration 3: Flocking patterns</h4>
</p>
<div class='outerContainer' id='3a'></div>
<p>
The exploration is now complete: arc-based movement, independent AIs, grid-based vision,
following patterns of cohesion, separation, alignment, with hazards, on a large scale:
</p> </p>
<div class='outerContainer'> <div class='outerContainer' id='3b'></div>
<div id="animation3a" class='animationContainer'></div>
<div id="controls3a" class='controlsContainer'></div>
</div> -->
<script src='js/bundle.js'></script> <script src='js/bundle.js'></script>
<script src='/core/js/ui.js'></script> <script src='/core/js/ui.js'></script>

@ -1,81 +1,17 @@
import Rx, { Observable } from 'rxjs'; import Animation from './animation';
import Grid from './grid';
import Particle from './particle';
import Controls from './controls'; import Controls from './controls';
import { CONTROLS, ENTITIES } from './enums';
function Animation3a() { export default function(destroy$) {
this.options = { const id = '3a';
count: 400, const config = {
maxCount: 1000, id,
showVisionGrid: false, maxCount: 10,
speed: 4 showAlignmentControl: true,
showCohesionControl: true,
showSeparationControl: true,
showVisionGridControl: true
}; };
this.container = document.getElementById('animation3a'); const observables = Controls(destroy$, config);
this.particles = []; new Animation(observables, id);
this.grid = new Grid();
const controls = new Controls(
document.getElementById('controls3a'),
this.options
);
controls.mount().subscribe(this.subscriber.bind(this));
this.updateCount(this.options.count);
};
Animation3a.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;
}
}
Animation3a.prototype.nextFrame = function() {
this.particles.forEach(p => {
const prevX = p.arc.endX;
const prevY = p.arc.endY;
p.nextFrame();
this.grid.deletePoint({ x: prevX, y: prevY, type: ENTITIES.PARTICLE });
this.grid.setPoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }, p);
});
}
Animation3a.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));
}
}
Animation3a.prototype.updateCount = function(count) {
const bounds = this.container.getBoundingClientRect();
while (this.particles.length > count) {
const p = this.particles.pop();
this.grid.deletePoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE });
p.remove();
} }
while (this.particles.length < count) {
const p = new Particle(this.container, bounds, this.options, this.grid);
this.grid.setPoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }, p);
this.particles.push(p);
}
}
Animation3a.prototype.updateSpeed = function(value) {
this.options.speed = value;
this.particles.forEach(p => p.updateConfig({ speed: value }));
}
export default Animation3a;

@ -0,0 +1,16 @@
import Animation from './animation';
import Controls from './controls';
export default function(destroy$) {
const id = '3b';
const config = {
id,
maxCount: 10,
showAlignmentControl: true,
showCohesionControl: true,
showSeparationControl: true
};
const observables = Controls(destroy$, config);
new Animation(observables, id).addHazards();
}

@ -128,19 +128,18 @@ const Arc = {
}, },
goto: function (arc, x, y, speed) { goto: function (arc, x, y, speed) {
// WORKING! const prevD = Math.pow(Math.pow(x - arc.prevEndX, 2) + Math.pow(y - arc.prevEndY, 2), 0.5);
// const prevD = Math.pow(Math.pow(x - arc.prevEndX, 2) + Math.pow(y - arc.prevEndY, 2), 0.5); const currD = Math.pow(Math.pow(x - arc.endX, 2) + Math.pow(y - arc.endY, 2), 0.5);
// const currD = Math.pow(Math.pow(x - arc.endX, 2) + Math.pow(y - arc.endY, 2), 0.5); const ratio = (prevD - currD) / speed;
// const ratio = (prevD - currD) / speed;
// if (currD < 10) {
// if (currD < 10) { throw new Error(`Arc end of (${arc.endX},${arc.endY}) is within 50px of (${x},${y})`);
// throw new Error(`Arc end of (${arc.endX},${arc.endY}) is within 50px of (${x},${y})`); } else if (ratio < 0.8) {
// } else if (ratio < 0.8) { // arc = (ratio < 0 ? Arc.reverse(arc) : arc);
// // arc = (ratio < 0 ? Arc.reverse(arc) : arc); arc = Arc.changeRadius(arc, 20);
// arc = Arc.changeRadius(arc, 20); } else {
// } else { arc = Arc.changeRadius(arc, 400);
// arc = Arc.changeRadius(arc, 400); }
// }
return arc; return arc;
}, },

File diff suppressed because one or more lines are too long

@ -3,6 +3,8 @@ import Animation1a from './animation1a';
import Animation1b from './animation1b'; import Animation1b from './animation1b';
import Animation2a from './animation2a'; import Animation2a from './animation2a';
import Animation2b from './animation2b'; import Animation2b from './animation2b';
import Animation3a from './animation3a';
import Animation3b from './animation3b';
require('../css/reset.scss'); require('../css/reset.scss');
require('../css/index.scss'); require('../css/index.scss');
@ -16,6 +18,8 @@ window.addEventListener('load', () => {
Animation1b(destroy$); Animation1b(destroy$);
Animation2a(destroy$); Animation2a(destroy$);
Animation2b(destroy$); Animation2b(destroy$);
Animation3a(destroy$);
Animation3b(destroy$);
}); });
// TODO remove bottom padding from Disqus // TODO remove bottom padding from Disqus
@ -26,6 +30,7 @@ window.addEventListener('load', () => {
// TODO BehaviorSubject listener on bounds change // TODO BehaviorSubject listener on bounds change
// TODO are vision grid nodes removed properly // TODO are vision grid nodes removed properly
// TODO overlapping grid points // TODO overlapping grid points
// TODO grid touches
// TODO ANIM1ab free movement // TODO ANIM1ab free movement

@ -23,7 +23,7 @@ function Particle(parent, bounds, globalGrid, observables) {
vision: createVisionGrid(this.config) vision: createVisionGrid(this.config)
}; };
this.id = Random.id(6); this.id = Random.id(12);
this.nodes = { this.nodes = {
body: createBodyNode(this.config), body: createBodyNode(this.config),
@ -45,7 +45,11 @@ function Particle(parent, bounds, globalGrid, observables) {
this.arc = Arc.create(bounds, this.grids.global); 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: this.arc.endX,
y: this.arc.endY,
type: ENTITIES.PARTICLE
}, this);
this.remove$ = new Rx.Subject(); this.remove$ = new Rx.Subject();
@ -73,7 +77,11 @@ function Particle(parent, bounds, globalGrid, observables) {
// ===== PROTOTYPE ===== // ===== PROTOTYPE =====
Particle.prototype.remove = function() { Particle.prototype.remove = function() {
// this.grids.globals.deletePoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }); this.grids.global.deletePoint({
x: this.arc.endX,
y: this.arc.endY,
type: ENTITIES.PARTICLE
});
const parent = this.nodes.container.parentNode; const parent = this.nodes.container.parentNode;
parent.removeChild(this.nodes.container); parent.removeChild(this.nodes.container);
@ -82,16 +90,23 @@ Particle.prototype.remove = function() {
} }
Particle.prototype.subscribeNextFrame = function() { Particle.prototype.subscribeNextFrame = function() {
// this.arc = Arc.goto(this.arc, 200, 200, this.config.speed) this.grids.global.deletePoint({
x: this.arc.endX,
y: this.arc.endY,
type: ENTITIES.PARTICLE
});
if (this.nodes === undefined) { if (this.nodes === undefined) {
console.warn('no nodes in', this.id); console.warn('no nodes in', this.id);
return; return;
} }
// this.arc = Arc.goto(this.arc, 200, 200, this.config.speed)
this.arc = Arc.step(this.arc, this.config.bounds, this.config.speed); this.arc = Arc.step(this.arc, this.config.bounds, this.config.speed);
if (this.leader !== null) { if (this.leader !== null) {
// this.arc = Arc.follow(this.arc, this.leader.arc); this.arc = Arc.follow(this.arc, this.leader.arc);
} else if (this.arc.length <= 0 && this.config.randomize) { } else if (this.arc.length <= 0 && this.config.randomize) {
this.arc = Arc.randomize(this.arc); this.arc = Arc.randomize(this.arc);
} }
@ -100,16 +115,16 @@ Particle.prototype.subscribeNextFrame = function() {
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);
// const prevX = p.arc.endX;
// const prevY = p.arc.endY;
// this.grid.deletePoint({ x: prevX, y: prevY, type: ENTITIES.PARTICLE }); this.grids.global.setPoint({
// this.grid.setPoint({ x: p.arc.endX, y: p.arc.endY, type: ENTITIES.PARTICLE }, p); x: this.arc.endX,
y: this.arc.endY,
type: ENTITIES.PARTICLE
}, this);
repaintContainer(this.nodes.container, this.arc); repaintContainer(this.nodes.container, this.arc);
repaintBody(this.nodes.body, this.arc, this.isLeader); repaintBody(this.nodes.body, this.arc, this.isLeader);

Loading…
Cancel
Save