class Boid { constructor(radius, color, x, y, direction, id, scene_width, scene_height) { // Boid properties this.radius = radius; this.color = color; this.x = x; this.y = y; this.direction = direction; this.id = id; // World properties this.scene_width = scene_width; this.scene_height = scene_height; // Boid "dynamic" properties this.rayLength = 40; this.turnStepAmount = 20; this.stepAmount = 4; this.fieldOfView = 270; this.boidBuffer = 20; } move(boids) { // this.direction += Math.random() * (2 * this.turnStepAmount) - this.turnStepAmount this.direction = this.findNextRay(boids); var vector = this.findPoint(this.x, this.y, this.stepAmount, this.direction); this.x = vector.x; this.y = vector.y; } buildRays() { let rays = new Array(); let rayInteval = 2; let noOfSteps = this.fieldOfView / rayInteval; for (let i = 0; i < noOfSteps / 2; i++) { if (i != 0) { rays.push(rayInteval * -i); } rays.push(rayInteval * i); } return rays; } findNextRay(boids) { let rays = this.buildRays(); for (let i = 0; i < rays.length; i++) { let tweakAngle = 0; // if (Math.random() > 0.95) { // tweakAngle = this.turnStepAmount * Math.random() - (this.turnStepAmount / 2); // } let rayAngle = tweakAngle + this.direction + rays[i]; if (i == 0 && this.detectBoid(rayAngle, boids)) { continue; } if (this.detectBox(this.scene_width, this.scene_height, rayAngle)) { continue; } return rayAngle; } console.log(this.x, this.y); console.log('cannot find suitable ray'); } detectBoid(direction, boids) { for (let i = 0; i < boids.length; i++) { // rule out ourselves if (this.id == boids[i].id) { continue; } let thisFututrePosition = this.findPoint(this.x, this.y, this.boidBuffer, direction); let thisPath = { x1: this.x, y1: this.y, x2: thisFututrePosition.x, y2: thisFututrePosition.y }; let boidFuturePosition = this.findPoint(boids[i].x, boids[i].y, boids[i].boidBuffer, boids[i].direction); let boidPath = { x1: boids[i].x, y1: boids[i].y, x2: boidFuturePosition.x, y2: boidFuturePosition.y }; let thisIntersectsBoid = this.pathsIntersect( thisPath.x1, thisPath.y1, thisPath.x2, thisPath.y2, boidPath.x1, boidPath.y1, boidPath.x2, boidPath.y2 ); if (thisIntersectsBoid) { return true; } } return false; } pathsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) { // Check if none of the lines are of length 0 if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) { return false } let denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)) // // Lines are parallel // if (denominator === 0) { // return false // } let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator // is the intersection along the segments if (ua < 0 || ua > 1 || ub < 0 || ub > 1) { return false } // Return a object with the x and y coordinates of the intersection let x = x1 + ua * (x2 - x1) let y = y1 + ua * (y2 - y1) return { x, y } } detectBox(width, height, direction) { let point = this.findPoint(this.x, this.y, this.rayLength, direction); if (point.x - this.radius < 0 || point.y - this.radius < 0 || point.x + this.radius > width - 0 || point.y + this.radius > height - 0 ) { return true; } return false; } findPoint(x1, y1, length, angle) { angle *= Math.PI / 180; var x2 = x1 + length * Math.cos(angle), y2 = y1 + length * Math.sin(angle); return { x: x2, y: y2 }; } draw(context) { this.drawBoid(context); this.drawRay(context, this.x, this.y, this.rayLength, this.direction); } drawBoid(context) { context.beginPath(); context.fillStyle = "blue"; context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); context.stroke(); context.restore(); } drawRay(context, x, y, perceptionDistance, direction) { let point = this.findPoint(x, y, perceptionDistance, direction); context.lineWidth = 1; context.beginPath(); context.moveTo(x, y); context.lineTo(point.x, point.y); context.stroke(); context.restore(); } }