fsm-designer/js/components/state.js
2019-04-03 16:15:15 +02:00

148 lines
3.8 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 = '';
}
get text() {
return this._text;
}
set text(value) {
documents[activeDocument].addChange('state', this.id, 'edit', ['_text'], [this._text], true);
this._text = value;
documents[activeDocument].addChange('state', this.id, 'edit', ['_text'], [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(convertLatexShortcuts(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;
}
}