139 lines
3.5 KiB
JavaScript
139 lines
3.5 KiB
JavaScript
class State {
|
|
|
|
constructor(x, y) {
|
|
this.id = guid();
|
|
this.x = x;
|
|
this.y = y;
|
|
this.mouseOffsetX = 0;
|
|
this.mouseOffsetY = 0;
|
|
this.color = '#fff';
|
|
this.isActive = false;
|
|
this.activeTime = 0;
|
|
this.isAcceptState = false;
|
|
this.text = '';
|
|
}
|
|
|
|
setMouseStart(x, y) {
|
|
this.mouseOffsetX = this.x - x;
|
|
this.mouseOffsetY = this.y - y;
|
|
}
|
|
|
|
setAnchorPoint(x, y) {
|
|
this.x = x + this.mouseOffsetX;
|
|
this.y = y + this.mouseOffsetY;
|
|
}
|
|
|
|
draw() {
|
|
const activeTimeDuration = Date.now() - this.activeTime;
|
|
|
|
ctx.fillStyle = this.isActive && activeTimeDuration > simulationStepDuration ? '#0f0' : this.color;
|
|
ctx.strokeStyle = settings.colors.getColor(this);
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(this.x, this.y, radius, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
|
|
ctx.closePath();
|
|
|
|
if(activeTimeDuration < simulationStepDuration) {
|
|
let size = 0;
|
|
const percent = Math.min(Math.max(activeTimeDuration / simulationStepDuration, 0), 1);
|
|
|
|
if(this.isActive) {
|
|
size = easeInOutCubic(percent) * radius;
|
|
} else {
|
|
size = easeInOutCubic(1 - percent) * radius;
|
|
}
|
|
|
|
ctx.fillStyle = '#0f0';
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(this.x, this.y, size, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
|
|
ctx.closePath();
|
|
}
|
|
|
|
ctx.fillStyle = settings.colors.getColor(this);
|
|
const width = ctx.measureText(this.text).width;
|
|
if(width < radius * .9)
|
|
ctx.drawText(this.text, this.x, this.y, null, selectedObject === this);
|
|
else
|
|
ctx.drawText(this.text, this.x, this.y + radius * 1.75, null, selectedObject === this);
|
|
|
|
if(this.isAcceptState) {
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(this.x, this.y, radius - 6, 0, 2 * Math.PI);
|
|
ctx.stroke();
|
|
|
|
ctx.closePath();
|
|
}
|
|
}
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
containsPoint(x, y) {
|
|
return (x - this.x) ** 2 + (y - this.y) ** 2 < radius ** 2;
|
|
}
|
|
|
|
}
|