From a88b05f478f58cf8ecd7f76292c33957bbb2e399 Mon Sep 17 00:00:00 2001
From: Ben Burlingham
- The trickiest portion of this iteration was animating the curved paths. Elliptical geometry was the initial goal, - but calculating arc length (to maintain a scalar speed) is quite difficult. Smoothstep cubic curves - 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. + The trickiest portion of this iteration (and perhaps the whole project) was animating the curved paths: +
+ + ++ The current design uses circular paths that allow smooth changes in both direction and rotation.
@@ -60,14 +66,23 @@ - + diff --git a/js/animation3a.js b/js/animation3a.js index d7038f7..0d522b1 100644 --- a/js/animation3a.js +++ b/js/animation3a.js @@ -1,81 +1,17 @@ -import Rx, { Observable } from 'rxjs'; -import Grid from './grid'; -import Particle from './particle'; +import Animation from './animation'; import Controls from './controls'; -import { CONTROLS, ENTITIES } from './enums'; -function Animation3a() { - this.options = { - count: 400, - maxCount: 1000, - showVisionGrid: false, - speed: 4 +export default function(destroy$) { + const id = '3a'; + const config = { + id, + maxCount: 10, + showAlignmentControl: true, + showCohesionControl: true, + showSeparationControl: true, + showVisionGridControl: true }; - this.container = document.getElementById('animation3a'); - this.particles = []; - 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); - }); + const observables = Controls(destroy$, config); + new Animation(observables, id); } - -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; diff --git a/js/animation3b.js b/js/animation3b.js new file mode 100644 index 0000000..4d0f8c5 --- /dev/null +++ b/js/animation3b.js @@ -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(); +} diff --git a/js/arc.js b/js/arc.js index 652f167..a119b9e 100644 --- a/js/arc.js +++ b/js/arc.js @@ -128,19 +128,18 @@ const Arc = { }, 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 currD = Math.pow(Math.pow(x - arc.endX, 2) + Math.pow(y - arc.endY, 2), 0.5); - // const ratio = (prevD - currD) / speed; - // - // if (currD < 10) { - // throw new Error(`Arc end of (${arc.endX},${arc.endY}) is within 50px of (${x},${y})`); - // } else if (ratio < 0.8) { - // // arc = (ratio < 0 ? Arc.reverse(arc) : arc); - // arc = Arc.changeRadius(arc, 20); - // } else { - // arc = Arc.changeRadius(arc, 400); - // } + 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 ratio = (prevD - currD) / speed; + + if (currD < 10) { + throw new Error(`Arc end of (${arc.endX},${arc.endY}) is within 50px of (${x},${y})`); + } else if (ratio < 0.8) { + // arc = (ratio < 0 ? Arc.reverse(arc) : arc); + arc = Arc.changeRadius(arc, 20); + } else { + arc = Arc.changeRadius(arc, 400); + } return arc; }, diff --git a/js/bundle.js b/js/bundle.js index 31ba7d5..8b3c151 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -63,7 +63,7 @@ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 81); +/******/ return __webpack_require__(__webpack_require__.s = 85); /******/ }) /************************************************************************/ /******/ ([ @@ -76,7 +76,7 @@ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\nvar root_1 = __webpack_require__(/*! ./util/root */ 7);\nvar toSubscriber_1 = __webpack_require__(/*! ./util/toSubscriber */ 357);\nvar observable_1 = __webpack_require__(/*! ./symbol/observable */ 23);\n/**\n * A representation of any set of values over any amount of time. This the most basic building block\n * of RxJS.\n *\n * @class Observable