|
|
|
|
|
const { Engine, Render, Runner, Bodies, World, Events } = Matter; |
|
|
|
|
|
|
|
|
const engine = Engine.create(); |
|
|
const world = engine.world; |
|
|
|
|
|
|
|
|
const render = Render.create({ |
|
|
element: document.body, |
|
|
engine: engine, |
|
|
canvas: document.getElementById('world'), |
|
|
options: { |
|
|
width: 800, |
|
|
height: 600, |
|
|
wireframes: false, |
|
|
background: '#ffffff' |
|
|
} |
|
|
}); |
|
|
|
|
|
Render.run(render); |
|
|
const runner = Runner.create(); |
|
|
Runner.run(runner, engine); |
|
|
|
|
|
|
|
|
let ball = null; |
|
|
let vxData = []; |
|
|
let vyData = []; |
|
|
let xData = []; |
|
|
let tData = []; |
|
|
let vxyData = []; |
|
|
let breadcrumbs = []; |
|
|
let startTime = null; |
|
|
|
|
|
|
|
|
function getCanvasBounds() { |
|
|
return render.canvas.getBoundingClientRect(); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('click', (event) => { |
|
|
const canvasBounds = getCanvasBounds(); |
|
|
|
|
|
|
|
|
if (event.clientX < canvasBounds.left || event.clientX > canvasBounds.right || |
|
|
event.clientY < canvasBounds.top || event.clientY > canvasBounds.bottom) { |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
if (ball) { |
|
|
return; |
|
|
} |
|
|
|
|
|
const x = event.clientX - canvasBounds.left; |
|
|
const y = event.clientY - canvasBounds.top; |
|
|
const velocityX = parseFloat(document.getElementById('vx').value); |
|
|
const velocityY = parseFloat(document.getElementById('vy').value); |
|
|
|
|
|
ball = Bodies.circle(x, y, 20, { restitution: 0.8 }); |
|
|
Matter.Body.setVelocity(ball, { x: velocityX, y: velocityY }); |
|
|
World.add(world, ball); |
|
|
|
|
|
|
|
|
startTime = new Date().getTime(); |
|
|
}); |
|
|
|
|
|
|
|
|
Events.on(engine, 'beforeUpdate', () => { |
|
|
if (ball) { |
|
|
const elapsedTime = (new Date().getTime() - startTime) / 1000; |
|
|
vxData.push(ball.velocity.x); |
|
|
vyData.push(ball.velocity.y); |
|
|
xData.push(ball.position.x); |
|
|
tData.push(elapsedTime); |
|
|
vxyData.push({ x: ball.velocity.x, y: ball.velocity.y }); |
|
|
|
|
|
|
|
|
breadcrumbs.push(Bodies.circle(ball.position.x, ball.position.y, 2, { isStatic: true })); |
|
|
World.add(world, breadcrumbs[breadcrumbs.length - 1]); |
|
|
|
|
|
|
|
|
if (ball.position.x < 0 || ball.position.x > render.options.width || |
|
|
ball.position.y < 0 || ball.position.y > render.options.height) { |
|
|
World.remove(world, ball); |
|
|
ball = null; |
|
|
plotGraphs(); |
|
|
resetVariables(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function resetVariables() { |
|
|
vxData = []; |
|
|
vyData = []; |
|
|
xData = []; |
|
|
tData = []; |
|
|
vxyData = []; |
|
|
breadcrumbs.forEach(breadcrumb => World.remove(world, breadcrumb)); |
|
|
breadcrumbs = []; |
|
|
} |
|
|
|
|
|
|
|
|
function plotGraphs() { |
|
|
plotChart('vxChart', 'Velocity-X vs Time', tData, vxData); |
|
|
plotChart('vyChart', 'Velocity-Y vs Time', tData, vyData); |
|
|
plotChart('xtChart', 'Position-X vs Time', tData, xData); |
|
|
plotScatterChart('vxyChart', 'Velocity-Y vs Velocity-X', vxyData); |
|
|
} |
|
|
|
|
|
function plotChart(canvasId, label, xData, yData) { |
|
|
const ctx = document.getElementById(canvasId).getContext('2d'); |
|
|
new Chart(ctx, { |
|
|
type: 'line', |
|
|
data: { |
|
|
labels: xData, |
|
|
datasets: [{ |
|
|
label: label, |
|
|
data: yData, |
|
|
borderColor: 'rgba(75, 192, 192, 1)', |
|
|
borderWidth: 1, |
|
|
fill: false |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
scales: { |
|
|
x: { |
|
|
beginAtZero: true |
|
|
}, |
|
|
y: { |
|
|
beginAtZero: true |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
function plotScatterChart(canvasId, label, data) { |
|
|
const ctx = document.getElementById(canvasId).getContext('2d'); |
|
|
new Chart(ctx, { |
|
|
type: 'scatter', |
|
|
data: { |
|
|
datasets: [{ |
|
|
label: label, |
|
|
data: data, |
|
|
borderColor: 'rgba(75, 192, 192, 1)', |
|
|
backgroundColor: 'rgba(75, 192, 192, 0.5)', |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
scales: { |
|
|
x: { |
|
|
type: 'linear', |
|
|
position: 'bottom', |
|
|
beginAtZero: true, |
|
|
title: { |
|
|
display: true, |
|
|
text: 'Velocity-X' |
|
|
} |
|
|
}, |
|
|
y: { |
|
|
beginAtZero: true, |
|
|
title: { |
|
|
display: true, |
|
|
text: 'Velocity-Y' |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|