You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
4.5 KiB
159 lines
4.5 KiB
// Single particle movement.
|
|
// Goal: per-frame decisions
|
|
// 20 x 20 grid
|
|
|
|
// The trickiest portion of this iteration was animating the curved paths. Calculating arc length (for scalar speed) along an elliptical
|
|
// geometry is quite difficult, so the current solution uses circular paths, which change radius and sometimes rotation after a random
|
|
// number of frames have emitted. A smoothstep cubic curve was considered, but maintaining consistent entry and exit angles will affect
|
|
// performance for large particle counts.
|
|
|
|
import Rx, { Observable } from 'rxjs';
|
|
import AnimationBase from './animation0';
|
|
import DOM from './dom';
|
|
import Store from './store';
|
|
|
|
const speed = 2;
|
|
const visionRadius = 50;
|
|
const grid = {};
|
|
|
|
const movementCircle = document.createElement('div');
|
|
movementCircle.className = 'anim3-movement-circle';
|
|
|
|
const visionCircle = document.createElement('div');
|
|
// visionCircle.className = 'anim3-vision-circle';
|
|
|
|
const particle = document.createElement('div');
|
|
particle.className = 'anim3-particle';
|
|
|
|
function move(store) {
|
|
let {
|
|
clockwise,
|
|
interval,
|
|
particleX,
|
|
particleY,
|
|
radius,
|
|
theta
|
|
} = store.get();
|
|
|
|
|
|
if (interval <= 0) {
|
|
interval = Math.round(Math.random() * 10) + 50;
|
|
radius = Math.round(Math.random() * 200) + 50
|
|
|
|
// IF SEEING A WALL: tight 180
|
|
// if ( < visionRadius
|
|
// if (Math.random() < 0.3) {
|
|
// console.warn("turn!")
|
|
// state.radius = 20;
|
|
// state.interval = Math.PI * 20 / speed;
|
|
// }
|
|
|
|
detectWall(store);
|
|
|
|
if (Math.random() < 0.5) {
|
|
clockwise = !clockwise;
|
|
theta = (theta > Math.PI ? theta - Math.PI : theta + Math.PI);
|
|
}
|
|
}
|
|
|
|
interval -= 1;
|
|
|
|
const prevTheta = theta;
|
|
const delta = speed / radius;
|
|
theta += (clockwise ? -delta : delta);
|
|
|
|
particleX -= radius * Math.cos(theta) - radius * Math.cos(prevTheta);
|
|
particleY -= radius * Math.sin(theta) - radius * Math.sin(prevTheta);
|
|
|
|
store.set({
|
|
clockwise,
|
|
interval,
|
|
particleX,
|
|
particleY,
|
|
radius,
|
|
theta
|
|
});
|
|
}
|
|
|
|
function detectWall(store) {
|
|
const { particleX, particleY, radius, theta } = store.get();
|
|
}
|
|
|
|
function paintMovementCircle(store) {
|
|
// TODO UPDATE ONLY IF THETA CHANGED - MUST HAVE PREVSTATE
|
|
const { particleX, particleY, radius, theta } = store.get();
|
|
|
|
movementCircle.style.left = `${particleX - radius - radius * Math.cos(theta)}px`;
|
|
movementCircle.style.top = `${particleY - radius + radius * Math.sin(theta)}px`;
|
|
movementCircle.style.height = `${2 * radius}px`;
|
|
movementCircle.style.width = `${2 * radius}px`;
|
|
movementCircle.style.borderRadius = `${radius}px`;
|
|
}
|
|
|
|
function paintParticle(store) {
|
|
const { clockwise, particleX, particleY, theta } = store.get();
|
|
const rad = (clockwise ? theta + Math.PI / 2 : theta - Math.PI / 2);
|
|
|
|
particle.style.left = `${particleX}px`;
|
|
particle.style.top = `${particleY}px`;
|
|
particle.style.transform = `rotate(${rad}rad)`;
|
|
}
|
|
|
|
function reset() {
|
|
while (DOM.container.childNodes.length) {
|
|
DOM.container.removeChild(DOM.container.firstChild);
|
|
}
|
|
|
|
const store = new Store({
|
|
clockwise: false,
|
|
interval: 10,
|
|
particleX: 300,
|
|
particleY: 150,
|
|
radius: 150,
|
|
theta: Math.PI / 2
|
|
});
|
|
|
|
move(store);
|
|
|
|
DOM.container.appendChild(particle);
|
|
DOM.container.appendChild(movementCircle);
|
|
|
|
return store;
|
|
};
|
|
|
|
function init() {
|
|
const store = reset();
|
|
|
|
for (let x = 0; x <= 600; x += 5) {
|
|
grid[x] = {};
|
|
for (let y = 0; y <= 600; y += 5) {
|
|
grid[x][y] = { type: null };
|
|
|
|
if (x === 0 || y === 0 || x === 600 || y === 600) {
|
|
grid[x][y] = { type: 'wall' };
|
|
}
|
|
}
|
|
}
|
|
|
|
const a = Math.round(Math.random() * 180) + 20;
|
|
const b = Math.round(Math.random() * 180) + 20;
|
|
|
|
const stop$ = Rx.Observable.fromEvent(DOM.container, 'stop');
|
|
const fps$ = Rx.Observable.interval(1000 / 32)
|
|
.map(_ => store)
|
|
// .take(100)
|
|
.takeUntil(stop$); console.error("CLICK TO STOP");
|
|
|
|
const click$ = Rx.Observable.fromEvent(DOM.container, 'click');
|
|
click$.subscribe(() => {
|
|
DOM.container.dispatchEvent(new CustomEvent('stop'));
|
|
});
|
|
|
|
fps$.subscribe(move);
|
|
fps$.subscribe(paintParticle);
|
|
// fps$.subscribe(paintMovementCircle);
|
|
};
|
|
|
|
const Animation3 = Object.assign({}, AnimationBase, { init, reset });
|
|
|
|
export default Animation3;
|
|
|