import { ENTITIES, RAD } from './enums'; import Random from './random'; // "How much of movement is in the correct direction" const rigidity = 0.9; // "How close to the leader is enough" const sensitivity = 30; const Arc = { create: function(bounds) { 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), prevEndX: 0, prevEndY: 0, radius: Random.num(100, 200), speed: 0, theta: Random.num(RAD.t90, RAD.t360) }; arc.cosTheta = Math.cos(arc.theta); arc.sinTheta = Math.sin(arc.theta); arc.endX = arc.centerX + arc.radius * arc.cosTheta; arc.endY = arc.centerY - arc.radius * arc.sinTheta; arc = Arc.overflow(arc, bounds); return arc; }, step: function(arc, bounds) { // Ensure constant velocity and theta between 0 and 2π. const delta = arc.speed / arc.radius; arc.length -= delta; arc.theta += (arc.clockwise ? -delta : +delta); arc.theta = (arc.theta > 0 ? arc.theta % RAD.t360 : RAD.t360 + arc.theta); arc.cosTheta = Math.cos(arc.theta); arc.sinTheta = Math.sin(arc.theta); arc.prevEndX = arc.endX; arc.prevEndY = arc.endY; arc.endX = arc.centerX + arc.radius * arc.cosTheta; arc.endY = arc.centerY - arc.radius * arc.sinTheta; arc = Arc.overflow(arc, bounds); return arc; }, randomize: function(arc) { arc.length = Random.num(RAD.t90, RAD.t360); arc = Arc.changeRadius(arc, Random.num(100, 200)); if (Random.bool(0.8)) { arc = Arc.reverse(arc); } return arc; }, overflow: function(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; }, changeRadius: function(arc, newRadius) { const r0 = arc.radius; const r1 = newRadius; // Moves arc center to new radius while keeping theta constant. arc.centerX -= (r1 - r0) * arc.cosTheta; arc.centerY += (r1 - r0) * arc.sinTheta; arc.radius = r1; return arc; }, changeSpeed: function(arc, newSpeed) { arc.speed = newSpeed * 1; return arc; }, reverse: function(arc) { arc.clockwise = !arc.clockwise; arc.theta = (arc.theta + RAD.t180) % RAD.t360; arc.cosTheta = Math.cos(arc.theta); arc.sinTheta = Math.sin(arc.theta); arc.centerX -= (2 * arc.radius) * arc.cosTheta; arc.centerY += (2 * arc.radius) * arc.sinTheta; return arc; }, follow: function(arc, arcToFollow) { const prevD = Math.pow( Math.pow(arcToFollow.endX - arc.prevEndX, 2) + Math.pow(arcToFollow.endY - arc.prevEndY, 2) , 0.5); const currD = Math.pow( Math.pow(arcToFollow.endX - arc.endX, 2) + Math.pow(arcToFollow.endY - arc.endY, 2) , 0.5); const rigidityCoeff = (prevD - currD) / arc.speed; if (currD < sensitivity) { arc = (arc.clockwise !== arcToFollow.clockwise ? Arc.reverse(arc) : arc); arc = Arc.changeRadius(arc, arcToFollow.radius); if (arc.speed > arcToFollow.speed) { arc = Arc.changeSpeed(arc, arc.speed - 1); } if (arc.speed < arcToFollow.speed) { arc = Arc.changeSpeed(arc, arc.speed + 1); } } else if (rigidityCoeff < rigidity) { arc = Arc.changeRadius(arc, 20); if (arc.speed > arcToFollow.speed - 1) { arc = Arc.changeSpeed(arc, arc.speed - 1); } } else { arc = Arc.changeRadius(arc, 4000); if (arc.speed < (arcToFollow.speed + 2)) { arc = Arc.changeSpeed(arc, arc.speed + 1); } } return arc; }, evade: function(arc) { arc = Arc.changeRadius(arc, 20); arc.length = 1; return arc; } } export default Arc;