Phisics / script.js
OzoneAsai's picture
Update script.js
82d9cf6 verified
// モジュールの設定
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); // ユーザー入力の初速度X
const velocityY = parseFloat(document.getElementById('vy').value); // ユーザー入力の初速度Y
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の追加
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'
}
}
}
}
});
}