Add simulation mode with animations from the different states
This commit is contained in:
parent
1af3c8a72e
commit
a79bc8347c
215
js/simulate.js
Normal file
215
js/simulate.js
Normal file
|
@ -0,0 +1,215 @@
|
|||
let simulationStates = [];
|
||||
let singleCharMode = true;
|
||||
let simulationStepDuration = 500;
|
||||
|
||||
function simulate(word) {
|
||||
let steps = [];
|
||||
|
||||
if (singleCharMode) {
|
||||
steps = word.split('');
|
||||
} else {
|
||||
steps = word.split(/ /g);
|
||||
}
|
||||
|
||||
simulationStates.forEach(state => {
|
||||
state.isActive = false;
|
||||
});
|
||||
simulationStates.splice();
|
||||
|
||||
const startConnections = connections.filter(conn => conn instanceof StartConnection);
|
||||
startConnections.forEach(connection => animations.push(new ConnectionAnimation(connection)));
|
||||
simulationStates = startConnections.map(conn => conn.state);
|
||||
|
||||
setTimeout(() => {
|
||||
simulationStates.forEach(state => {
|
||||
state.isActive = true;
|
||||
state.activeTime = Date.now();
|
||||
});
|
||||
|
||||
let stepIndex = 0;
|
||||
const stepInterval = setInterval(() => {
|
||||
if (stepIndex >= steps.length) {
|
||||
clearInterval(stepInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
simulationStep(steps[stepIndex]);
|
||||
stepIndex++;
|
||||
}, simulationStepDuration * 4);
|
||||
}, simulationStepDuration);
|
||||
}
|
||||
|
||||
function simulationStep(input) {
|
||||
const len = simulationStates.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const state = simulationStates[i];
|
||||
const possibilities = getOutgoingConnections(state);
|
||||
|
||||
simulationStates.splice(0, 1);
|
||||
state.isActive = false;
|
||||
state.activeTime = Date.now();
|
||||
|
||||
possibilities.forEach(conn => {
|
||||
const conditions = conn.text.split(/, ?/g);
|
||||
|
||||
console.log(conn, conditions);
|
||||
|
||||
if (input.match(new RegExp(conditions.join('|'), 'g'))) {
|
||||
if (conn instanceof Connection) {
|
||||
animations.push(new ConnectionAnimation(conn));
|
||||
|
||||
setTimeout(() => {
|
||||
simulationStates.push(conn.stateB);
|
||||
conn.stateB.isActive = true;
|
||||
conn.stateB.activeTime = Date.now();
|
||||
}, simulationStepDuration);
|
||||
} else if (conn instanceof SelfConnection) {
|
||||
animations.push(new SelfConnectionAnimation(conn));
|
||||
|
||||
setTimeout(() => {
|
||||
simulationStates.push(conn.state);
|
||||
conn.state.isActive = true;
|
||||
conn.state.activeTime = Date.now();
|
||||
}, simulationStepDuration);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getOutgoingConnections(state) {
|
||||
return connections.filter(connection => connection instanceof SelfConnection ? connection.state === state : connection.stateA === state);
|
||||
}
|
||||
|
||||
class ConnectionAnimation {
|
||||
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
getSize(percent) {
|
||||
if (percent < .33) {
|
||||
return easeInOutCubic(percent * 3) * 10;
|
||||
} else if (percent < .66) {
|
||||
return 10;
|
||||
} else {
|
||||
return easeInOutCubic(1 - (percent - .8) * 3) * 10;
|
||||
}
|
||||
}
|
||||
|
||||
getPosLinear(percent, points) {
|
||||
|
||||
}
|
||||
|
||||
getPosCircular(percent, points) {
|
||||
const distanceSquared = (points.end.x - points.start.x) ** 2 + (points.end.y - points.start.y) ** 2;
|
||||
let phi = Math.acos(1 - (distanceSquared / (2 * points.circle.radius ** 2)));
|
||||
|
||||
if (distanceSquared > points.circle.radius ** 2) {
|
||||
phi = 2 * Math.PI - phi;
|
||||
}
|
||||
|
||||
let deltaPhi = phi * percent;
|
||||
|
||||
const startX = points.start.x - points.circle.x;
|
||||
const startY = points.start.y - points.circle.y;
|
||||
|
||||
if (!points.isReversed) {
|
||||
deltaPhi *= -1;
|
||||
}
|
||||
|
||||
const x = startX * Math.cos(deltaPhi) + startY * Math.sin(deltaPhi) + points.circle.x;
|
||||
const y = -startX * Math.sin(deltaPhi) + startY * Math.cos(deltaPhi) + points.circle.y;
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
draw() {
|
||||
const deltaTime = Date.now() - this.startTime;
|
||||
const percent = Math.min(Math.max(deltaTime / simulationStepDuration, 0), 1);
|
||||
|
||||
let curX = 0;
|
||||
let curY = 0;
|
||||
|
||||
const endPoints = this.connection instanceof StartConnection ? this.connection.getEndPoints() : this.connection.getEndPointsAndCircle();
|
||||
|
||||
if (endPoints.isCircle) {
|
||||
const pos = this.getPosCircular(percent, endPoints);
|
||||
|
||||
curX = pos.x;
|
||||
curY = pos.y;
|
||||
} else {
|
||||
const deltaX = endPoints.end.x - endPoints.start.x;
|
||||
const deltaY = endPoints.end.y - endPoints.start.y;
|
||||
|
||||
curX = endPoints.start.x + deltaX * percent;
|
||||
curY = endPoints.start.y + deltaY * percent;
|
||||
}
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(curX, curY, this.getSize(percent), 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
if (percent >= 1) {
|
||||
animations.splice(animations.findIndex(anim => anim === this), 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SelfConnectionAnimation {
|
||||
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
draw() {
|
||||
const deltaTime = Date.now() - this.startTime;
|
||||
const percent = Math.min(Math.max(deltaTime / simulationStepDuration, 0), 1);
|
||||
|
||||
const endPoints = this.connection.getEndPointsAndCircle();
|
||||
|
||||
const distanceSquared = (endPoints.end.x - endPoints.start.x) ** 2 + (endPoints.end.y - endPoints.start.y) ** 2;
|
||||
let phi = Math.acos(1 - (distanceSquared / (2 * endPoints.circle.radius ** 2)));
|
||||
|
||||
if (distanceSquared > endPoints.circle.radius ** 2) {
|
||||
phi = 2 * Math.PI - phi;
|
||||
}
|
||||
|
||||
let deltaPhi = phi * percent;
|
||||
|
||||
const startX = endPoints.start.x - endPoints.circle.x;
|
||||
const startY = endPoints.start.y - endPoints.circle.y;
|
||||
|
||||
if (!endPoints.isReversed) {
|
||||
deltaPhi *= -1;
|
||||
}
|
||||
|
||||
let x = startX * Math.cos(deltaPhi) + startY * Math.sin(deltaPhi) + endPoints.circle.x;
|
||||
let y = -startX * Math.sin(deltaPhi) + startY * Math.cos(deltaPhi) + endPoints.circle.y;
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(x, y, 10, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
if (percent >= 1) {
|
||||
animations.splice(animations.findIndex(anim => anim === this), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function easeInOutCubic(t) {
|
||||
return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
|
||||
}
|
Loading…
Reference in New Issue
Block a user