|
|
@ -27,16 +27,7 @@ function Particle(parent, bounds, config, globalGrid) { |
|
|
|
vision: createVisionGrid(this.config) |
|
|
|
vision: createVisionGrid(this.config) |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
this.arc = { |
|
|
|
this.arc = createArc(bounds, globalGrid, this.config); // TODO no need to pass config after testing
|
|
|
|
centerX: random.num(0, bounds.width), |
|
|
|
|
|
|
|
centerY: random.num(0, bounds.height), |
|
|
|
|
|
|
|
clockwise: random.bool(), |
|
|
|
|
|
|
|
endX: 0, |
|
|
|
|
|
|
|
endY: 0, |
|
|
|
|
|
|
|
length: random.num(RAD.t90, RAD.t360), |
|
|
|
|
|
|
|
radius: random.num(100, 200), |
|
|
|
|
|
|
|
theta: random.num(RAD.t90, RAD.t360), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.nodes = { |
|
|
|
this.nodes = { |
|
|
|
body: createBodyNode(this.config), |
|
|
|
body: createBodyNode(this.config), |
|
|
@ -57,6 +48,9 @@ function Particle(parent, bounds, config, globalGrid) { |
|
|
|
|
|
|
|
|
|
|
|
Particle.prototype.remove = function() { |
|
|
|
Particle.prototype.remove = function() { |
|
|
|
this.nodes.parent.removeChild(this.nodes.container); |
|
|
|
this.nodes.parent.removeChild(this.nodes.container); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.nodes.visionGrid.forEach(node => this.nodes.parent.removeChild(node)); |
|
|
|
|
|
|
|
|
|
|
|
return this; |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -64,6 +58,8 @@ Particle.prototype.nextFrame = function() { |
|
|
|
this.arc = updateArc(this.arc, this.config); |
|
|
|
this.arc = updateArc(this.arc, this.config); |
|
|
|
this.grids.vision = updateVisionGrid(this.arc, this.config, this.grids); |
|
|
|
this.grids.vision = updateVisionGrid(this.arc, this.config, this.grids); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.arc = evade(this.arc, this.grids.vision); |
|
|
|
|
|
|
|
|
|
|
|
repaintContainer(this.nodes.container, this.arc); |
|
|
|
repaintContainer(this.nodes.container, this.arc); |
|
|
|
repaintBody(this.nodes.body, this.arc); |
|
|
|
repaintBody(this.nodes.body, this.arc); |
|
|
|
repaintCircle(this.nodes.circle, this.arc); |
|
|
|
repaintCircle(this.nodes.circle, this.arc); |
|
|
@ -96,6 +92,34 @@ Particle.prototype.updateConfig = function(config) { |
|
|
|
|
|
|
|
|
|
|
|
// ===== CREATION =====
|
|
|
|
// ===== CREATION =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createArc(bounds, globalGrid, config) { |
|
|
|
|
|
|
|
let arc = { |
|
|
|
|
|
|
|
centerX: random.num(0, bounds.width), |
|
|
|
|
|
|
|
centerY: random.num(0, bounds.height), |
|
|
|
|
|
|
|
clockwise: random.bool(), |
|
|
|
|
|
|
|
endX: 0, |
|
|
|
|
|
|
|
endY: 0, |
|
|
|
|
|
|
|
length: random.num(RAD.t90, RAD.t360), |
|
|
|
|
|
|
|
radius: random.num(100, 200), |
|
|
|
|
|
|
|
theta: random.num(RAD.t90, RAD.t360), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
arc.endX = arc.centerX + arc.radius * Math.cos(arc.theta); |
|
|
|
|
|
|
|
arc.endY = arc.centerY - arc.radius * Math.sin(arc.theta); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
arc = overflowArc(arc, bounds); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const x = arc.endX - arc.endX % 5; |
|
|
|
|
|
|
|
const y = arc.endY - arc.endY % 5; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If starting in a hazard, recurse.
|
|
|
|
|
|
|
|
if (globalGrid[x] !== undefined && globalGrid[x][y] !== undefined) { |
|
|
|
|
|
|
|
arc = createArc(bounds, globalGrid, config); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return arc; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createBodyNode(config) { |
|
|
|
function createBodyNode(config) { |
|
|
|
const node = document.createElement('div'); |
|
|
|
const node = document.createElement('div'); |
|
|
|
node.className = 'particle-body'; |
|
|
|
node.className = 'particle-body'; |
|
|
@ -121,10 +145,6 @@ function createContainerNode(config) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createVisionGrid(config) { |
|
|
|
function createVisionGrid(config) { |
|
|
|
if (config.showVisionGrid === false) { |
|
|
|
|
|
|
|
return []; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { gridSize: side, visionRadius: radius } = config; |
|
|
|
const { gridSize: side, visionRadius: radius } = config; |
|
|
|
const r0 = radius; |
|
|
|
const r0 = radius; |
|
|
|
const r1 = radius - side; |
|
|
|
const r1 = radius - side; |
|
|
@ -133,8 +153,8 @@ function createVisionGrid(config) { |
|
|
|
|
|
|
|
|
|
|
|
for (let x = -radius; x <= radius; x += side) { |
|
|
|
for (let x = -radius; x <= radius; x += side) { |
|
|
|
for (let y = -radius; y <= radius; y += side) { |
|
|
|
for (let y = -radius; y <= radius; y += side) { |
|
|
|
// Omit lower half
|
|
|
|
// Omit large slices of unused circle
|
|
|
|
if (y < 0) { |
|
|
|
if (x > y || x < -y) { |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -146,7 +166,7 @@ function createVisionGrid(config) { |
|
|
|
|
|
|
|
|
|
|
|
let alpha = Math.atan(y / x); |
|
|
|
let alpha = Math.atan(y / x); |
|
|
|
if (x < 0) { |
|
|
|
if (x < 0) { |
|
|
|
alpha = RAD.t180 + alpha; // TODO +=
|
|
|
|
alpha += RAD.t180; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
points.push({ x, y, r, alpha, touch: false }); |
|
|
|
points.push({ x, y, r, alpha, touch: false }); |
|
|
@ -201,21 +221,7 @@ function updateArc(arc, { bounds, randomize, speed }) { |
|
|
|
arc.endY = arc.centerY - arc.radius * Math.sin(arc.theta); |
|
|
|
arc.endY = arc.centerY - arc.radius * Math.sin(arc.theta); |
|
|
|
|
|
|
|
|
|
|
|
// Overflow.
|
|
|
|
// Overflow.
|
|
|
|
if (arc.endX < 0) { |
|
|
|
arc = overflowArc(arc, bounds); |
|
|
|
arc.endX += bounds.width; |
|
|
|
|
|
|
|
arc.centerX += bounds.width |
|
|
|
|
|
|
|
} else if (arc.endX > bounds.width) { |
|
|
|
|
|
|
|
arc.endX -= bounds.width; |
|
|
|
|
|
|
|
arc.centerX -= bounds.width |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (arc.endY < 0) { |
|
|
|
|
|
|
|
arc.endY += bounds.height; |
|
|
|
|
|
|
|
arc.centerY += bounds.height |
|
|
|
|
|
|
|
} else if (arc.endY > bounds.height) { |
|
|
|
|
|
|
|
arc.endY -= bounds.height; |
|
|
|
|
|
|
|
arc.centerY -= bounds.height |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return arc; |
|
|
|
return arc; |
|
|
|
} |
|
|
|
} |
|
|
@ -243,6 +249,26 @@ function updateVisionGrid(arc, config, grids) { |
|
|
|
}, []); |
|
|
|
}, []); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function overflowArc(arc, bounds) { |
|
|
|
|
|
|
|
if (arc.endX < 0) { |
|
|
|
|
|
|
|
arc.endX += bounds.width; |
|
|
|
|
|
|
|
arc.centerX += bounds.width |
|
|
|
|
|
|
|
} else if (arc.endX > bounds.width) { |
|
|
|
|
|
|
|
arc.endX -= bounds.width; |
|
|
|
|
|
|
|
arc.centerX -= bounds.width |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (arc.endY < 0) { |
|
|
|
|
|
|
|
arc.endY += bounds.height; |
|
|
|
|
|
|
|
arc.centerY += bounds.height |
|
|
|
|
|
|
|
} else if (arc.endY > bounds.height) { |
|
|
|
|
|
|
|
arc.endY -= bounds.height; |
|
|
|
|
|
|
|
arc.centerY -= bounds.height |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return arc; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function moveArc(arc, newRadius) { |
|
|
|
function moveArc(arc, newRadius) { |
|
|
|
const r0 = arc.radius; |
|
|
|
const r0 = arc.radius; |
|
|
|
const r1 = newRadius; |
|
|
|
const r1 = newRadius; |
|
|
@ -263,6 +289,20 @@ function changeDirection(arc) { |
|
|
|
return arc; |
|
|
|
return arc; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== ACTIONS =====
|
|
|
|
|
|
|
|
function evade(arc, visionGrid) { |
|
|
|
|
|
|
|
const danger = visionGrid.reduce((acc, v) => acc || v.touch, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (danger === false) { |
|
|
|
|
|
|
|
return arc; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const evasionArc = moveArc(arc, 20); |
|
|
|
|
|
|
|
evasionArc.length = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return evasionArc; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ===== RENDERING =====
|
|
|
|
// ===== RENDERING =====
|
|
|
|
function repaintContainer(node, arc) { |
|
|
|
function repaintContainer(node, arc) { |
|
|
|
node.style.left = `${arc.endX}px`; |
|
|
|
node.style.left = `${arc.endX}px`; |
|
|
|