class State { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; this.text = ''; } draw() { ctx.fillStyle = this.color; ctx.strokeStyle = '#000'; ctx.beginPath(); ctx.arc(this.x, this.y, radius, 0, 360); ctx.fill(); ctx.stroke(); ctx.closePath(); } jumpTo(x, y) { this.x = Math.min(Math.max(x, 0), width); this.y = Math.min(Math.max(y, 0), height); } moveTo(x, y) { x = Math.min(Math.max(0, x), width); y = Math.min(Math.max(0, y), height); this.goal = { x, y }; const angle = this.direction(x, y); this.v = { x: settings.speed * Math.cos(angle), y: settings.speed * Math.sin(angle) }; } moveToStep() { this.x += this.v.x; this.y += this.v.y; if ((this.v.x > 0 && this.goal.x <= this.x) || (this.v.x < 0 && this.goal.x >= this.x)) { this.x = this.goal.x; this.v.x = 0; } if((this.v.y > 0 && this.goal.y <= this.y) || (this.v.y < 0 && this.goal.y >= this.y)) { this.y = this.goal.y; this.v.y = 0; } } intersection(state) { return -Math.hypot(this.x - state.x, this.y - state.y) + radius * 2; } intersects(state) { return this.intersection(state) > 0; } directionTo(state) { return this.direction(state.x, state.y); } direction(x, y) { return Math.atan2(this.y - y, this.x - x); } closestPointOnCircle(x, y) { const dx = x - this.x; const dy = y - this.y; const scale = Math.sqrt(dx ^ 2 + dy ^ 2); return { x: this.x + dx * radius / scale, y: this.y + dy * radius / scale, }; } }