Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
For over 300 years, physics has treated inertia as a constant.
But real-world systems are not constant.
Fuel burns
- Fuel burns
Mass changes
- Mass changes
Systems evolve
- Systems evolve
This project explores a different approach:
π Variable Inertia
Instead of describing motion, we compute it directly.
π§ͺ The Core IdeaIntroducing the NKTg Law:
NKTg = f(x, v, m)
Where:
x = position
- x = position
v = velocity
- v = velocity
m = mass
- m = mass
And:
p = m Γ v
- p = m Γ v
NKTgβ = x Γ p
- NKTgβ = x Γ p
NKTgβ = (dm/dt) Γ p
- NKTgβ = (dm/dt) Γ p
These quantities determine motion tendency in real time.
βοΈ How It WorksUnlike traditional physics engines:
No differential equations
- No differential equations
No numerical solvers
- No numerical solvers
No approximation loops
- No approximation loops
Instead:
Input current state
- Input current state
Compute instantly
- Compute instantly
Output system behavior
- Output system behavior
This makes the system suitable for real-time execution.
π‘ What This EnablesAI that reacts like physical systems
- AI that reacts like physical systems
Autonomous vehicles with real-time physics engines
- Autonomous vehicles with real-time physics engines
Simulation that doesnβt approximate β it executes
- Simulation that doesnβt approximate β it executes
Space navigation beyond classical models
- Space navigation beyond classical models
The NKTg Law has been implemented across:
Python (AI / prototyping)
- Python (AI / prototyping)
C++ (performance systems)
- C++ (performance systems)
Assembly (low-level execution)
- Assembly (low-level execution)
Quantum computing (Q#)
- Quantum computing (Q#)
With over 150 language implementations and cross-platform deployment.
π System ArchitectureCore engine (multi-language)
- Core engine (multi-language)
API layer (REST / gRPC)
- API layer (REST / gRPC)
Distributed repositories
- Distributed repositories
DOI-verified datasets
- DOI-verified datasets
Reproducible results across environments
- Reproducible results across environments
πΉ Leanpub
https://leanpub.com/NKTgLawhttps://leanpub.com/NKTgLaw-vihttps://leanpub.com/b/NKTgLaw-global
πΉ Google Play Books
https://play.google.com/store/books/details?id=CNLKEQAAQBAJ&plihttps://play.google.com/store/books/details?id=buDMEQAAQBAJhttps://play.google.com/store/books/series?id=Fa6gHAAAABC0sM
πΉ Amazon
https://www.amazon.com/dp/B0GTSBBRYRhttps://www.amazon.com/dp/B0GHZMSKYBhttps://www.amazon.com/dp/B0D6579Q3G
π This Is the ShiftNot better formulas.Not faster simulations.
A new way to build reality in code.
π KeywordsVariable Inertia β’ NKTg Law β’ Varying InertiaAI that reacts like physical systemsAutonomous vehicles with real-time physics enginesSimulation that doesnβt approximate β it executesSpace navigation beyond classical models
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NKTg Auto Simulation</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Exo+2:wght@300;400;700;900&display=swap');
:root {
--cyan: #00f5ff;
--purple: #a855f7;
--green: #00ff88;
--red: #ff3366;
--yellow: #ffd700;
--orange: #ff8c00;
--bg-dark: #020818;
--glass: rgba(0, 245, 255, 0.04);
--border: rgba(0, 245, 255, 0.15);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Exo 2', sans-serif;
background: var(--bg-dark);
color: #e0f7ff;
min-height: 100vh;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(0,245,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,245,255,0.03) 1px, transparent 1px);
background-size: 40px 40px;
pointer-events: none;
z-index: 0;
}
body::after {
content: '';
position: fixed;
top: -50%; left: -50%;
width: 200%; height: 200%;
background:
radial-gradient(ellipse at 30% 20%, rgba(0,100,255,0.08) 0%, transparent 50%),
radial-gradient(ellipse at 70% 80%, rgba(168,85,247,0.06) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
.glass {
background: var(--glass);
backdrop-filter: blur(12px);
border: 1px solid var(--border);
}
.mono { font-family: 'Share Tech Mono', monospace; }
header {
position: sticky; top: 0; z-index: 100;
background: rgba(2,8,24,0.9);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
padding: 12px 24px;
display: flex; justify-content: space-between; align-items: center;
}
.logo-text {
font-size: 18px; font-weight: 900;
letter-spacing: 2px; color: var(--cyan);
text-shadow: 0 0 20px rgba(0,245,255,0.5);
}
main {
position: relative; z-index: 1;
padding: 20px;
max-width: 1400px; margin: 0 auto;
display: grid;
grid-template-columns: 300px 1fr;
gap: 20px;
}
@media (max-width: 1024px) { main { grid-template-columns: 1fr; } }
/* SCENARIO BUTTONS */
.scenario-btn {
width: 100%;
padding: 12px 16px;
border-radius: 8px;
border: 1px solid;
cursor: pointer;
font-family: 'Exo 2', sans-serif;
font-weight: 700;
font-size: 13px;
letter-spacing: 1px;
transition: all 0.3s;
display: flex; align-items: center; gap: 10px;
margin-bottom: 8px;
background: transparent;
color: inherit;
}
.scenario-btn.rocket {
border-color: var(--orange);
color: var(--orange);
}
.scenario-btn.rocket:hover, .scenario-btn.rocket.active {
background: rgba(255,140,0,0.15);
box-shadow: 0 0 20px rgba(255,140,0,0.3);
}
.scenario-btn.drone {
border-color: var(--cyan);
color: var(--cyan);
}
.scenario-btn.drone:hover, .scenario-btn.drone.active {
background: rgba(0,245,255,0.1);
box-shadow: 0 0 20px rgba(0,245,255,0.3);
}
.scenario-btn.car {
border-color: var(--green);
color: var(--green);
}
.scenario-btn.car:hover, .scenario-btn.car.active {
background: rgba(0,255,136,0.1);
box-shadow: 0 0 20px rgba(0,255,136,0.3);
}
/* PLAY/STOP BUTTON */
#playBtn {
width: 100%;
padding: 14px;
border-radius: 8px;
border: 2px solid var(--purple);
background: rgba(168,85,247,0.1);
color: var(--purple);
font-family: 'Share Tech Mono', monospace;
font-size: 15px;
font-weight: bold;
letter-spacing: 2px;
cursor: pointer;
transition: all 0.3s;
margin-top: 4px;
}
#playBtn:hover {
background: rgba(168,85,247,0.25);
box-shadow: 0 0 25px rgba(168,85,247,0.4);
}
#playBtn.running {
border-color: var(--red);
background: rgba(255,51,102,0.1);
color: var(--red);
animation: runPulse 1s ease-in-out infinite;
}
@keyframes runPulse {
0%, 100% { box-shadow: 0 0 15px rgba(255,51,102,0.2); }
50% { box-shadow: 0 0 35px rgba(255,51,102,0.5); }
}
/* STATUS BOX */
#statusBox {
font-family: 'Share Tech Mono', monospace;
font-size: 24px; font-weight: bold;
text-align: center; padding: 16px;
border-radius: 8px; letter-spacing: 4px;
transition: all 0.4s; border: 1px solid;
}
#statusBox.stable {
color: var(--green);
background: rgba(0,255,136,0.07);
border-color: rgba(0,255,136,0.3);
}
#statusBox.danger {
color: var(--red);
background: rgba(255,51,102,0.07);
border-color: rgba(255,51,102,0.3);
animation: dangerPulse 0.8s ease-in-out infinite;
}
@keyframes dangerPulse {
0%, 100% { box-shadow: 0 0 20px rgba(255,51,102,0.1) inset; }
50% { box-shadow: 0 0 40px rgba(255,51,102,0.3) inset; }
}
#statusBox.balance {
color: var(--cyan);
background: rgba(0,245,255,0.07);
border-color: rgba(0,245,255,0.3);
}
#statusBox.waiting {
color: rgba(224,247,255,0.3);
background: rgba(224,247,255,0.02);
border-color: rgba(224,247,255,0.08);
}
/* METRIC CARDS */
.metric-card {
border-radius: 10px; padding: 14px;
text-align: center;
}
.metric-label {
font-size: 10px; letter-spacing: 2px;
text-transform: uppercase;
color: rgba(224,247,255,0.4); margin-bottom: 6px;
}
.metric-value {
font-family: 'Share Tech Mono', monospace;
font-size: 20px; font-weight: bold;
transition: color 0.3s;
}
/* PROGRESS BAR */
.progress-wrap {
background: rgba(255,255,255,0.04);
border-radius: 4px; height: 6px;
overflow: hidden; margin-top: 8px;
}
.progress-fill {
height: 100%; border-radius: 4px;
transition: width 0.1s linear;
background: linear-gradient(to right, var(--purple), var(--cyan));
}
/* PHYSICS DISPLAY */
.phys-row {
display: flex; justify-content: space-between;
align-items: center; padding: 8px 0;
border-bottom: 1px solid rgba(0,245,255,0.06);
font-size: 13px;
}
.phys-row:last-child { border-bottom: none; }
.phys-label { color: rgba(224,247,255,0.5); letter-spacing: 1px; }
.phys-value {
font-family: 'Share Tech Mono', monospace;
font-size: 15px; font-weight: bold;
color: var(--cyan); min-width: 100px; text-align: right;
}
/* SECTION TITLE */
.sec-label {
font-size: 10px; letter-spacing: 3px;
color: rgba(0,245,255,0.4); margin-bottom: 3px;
}
.sec-title {
font-size: 14px; font-weight: 700;
color: var(--cyan); margin-bottom: 14px;
display: flex; align-items: center; gap: 8px;
}
.sec-title::after {
content: ''; flex: 1; height: 1px;
background: linear-gradient(to right, rgba(0,245,255,0.3), transparent);
}
/* SCENARIO INFO */
.scenario-info {
background: rgba(0,0,0,0.3);
border-radius: 8px; padding: 12px;
font-size: 12px; line-height: 1.7;
color: rgba(224,247,255,0.55);
border-left: 3px solid var(--purple);
min-height: 80px;
}
.scenario-info strong { color: var(--cyan); }
/* TIME DISPLAY */
#timeDisplay {
font-family: 'Share Tech Mono', monospace;
font-size: 28px; font-weight: bold;
color: var(--yellow);
text-align: center; padding: 10px;
text-shadow: 0 0 20px rgba(255,215,0,0.4);
}
.chart-container { position: relative; height: 280px; }
</style>
</head>
<body>
<!-- HEADER -->
<header>
<div style="display:flex; align-items:center; gap:12px;">
<div style="width:36px;height:36px;border:2px solid var(--cyan);border-radius:8px;display:flex;align-items:center;justify-content:center;color:var(--cyan);box-shadow:0 0 12px rgba(0,245,255,0.3);">
<i class="fas fa-rocket"></i>
</div>
<div>
<div class="logo-text">NKTg AUTO-SIMULATION</div>
<div style="font-size:10px;color:rgba(0,245,255,0.4);letter-spacing:2px;">DYNAMIC PHYSICS ENGINE</div>
</div>
</div>
<div id="timeDisplay">T = 0.00s</div>
</header>
<main>
<!-- LEFT PANEL -->
<div style="display:flex;flex-direction:column;gap:16px;">
<!-- CHN KCH BN -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div class="sec-label">SELECT SCENARIO</div>
<div class="sec-title"><i class="fas fa-list" style="font-size:12px;"></i> Chn Kch bn</div>
<button class="scenario-btn rocket active" onclick="selectScenario('rocket')">
<i class="fas fa-rocket"></i> TN LA Tiu hao nhin liu
</button>
<button class="scenario-btn drone" onclick="selectScenario('drone')">
<i class="fas fa-plane"></i> DRONE Th hng t ngt
</button>
<button class="scenario-btn car" onclick="selectScenario('car')">
<i class="fas fa-car"></i> XE HI Tng tc & Phanh
</button>
<div class="scenario-info" id="scenarioInfo">
<strong>Tn la:</strong> Khi ng vi m=500kg, t nhin liu -5kg/s u n. Quan st NKTg tng khi tn la bay xa v NKTg m n nh khi nhin liu t u.
</div>
</div>
<!-- PLAY/STOP -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div class="sec-label">SIMULATION CONTROL</div>
<div class="sec-title"><i class="fas fa-play-circle" style="font-size:12px;"></i> iu khin</div>
<button id="playBtn" onclick="toggleSim()">
CHY M PHNG
</button>
<div style="margin-top:14px;">
<div style="display:flex;justify-content:space-between;font-size:11px;color:rgba(224,247,255,0.4);margin-bottom:6px;">
<span>TIN M PHNG</span>
<span id="progressLabel" class="mono">0%</span>
</div>
<div class="progress-wrap">
<div class="progress-fill" id="progressFill" style="width:0%;"></div>
</div>
</div>
<div style="margin-top:12px;font-size:11px;color:rgba(224,247,255,0.3);text-align:center;">
Thi gian m phng: <span class="mono" style="color:var(--yellow);">30 giy</span> 50ms/bc
</div>
</div>
<!-- TRNG THI -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div class="sec-label">SYSTEM STATE</div>
<div class="sec-title"><i class="fas fa-broadcast-tower" style="font-size:12px;"></i> Trng thi</div>
<div id="statusBox" class="waiting">ANG CH...</div>
<p id="advice" style="font-size:11px;color:rgba(224,247,255,0.4);text-align:center;margin-top:8px;min-height:16px;font-style:italic;"></p>
</div>
<!-- GI TR VT L -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div class="sec-label">PHYSICS VALUES</div>
<div class="sec-title"><i class="fas fa-atom" style="font-size:12px;"></i> Thng s thc</div>
<div class="phys-row">
<span class="phys-label">V TR (x)</span>
<span class="phys-value" id="disp_x">0.00 m</span>
</div>
<div class="phys-row">
<span class="phys-label">VN TC (v)</span>
<span class="phys-value" id="disp_v">0.00 m/s</span>
</div>
<div class="phys-row">
<span class="phys-label">KHI LNG (m)</span>
<span class="phys-value" id="disp_m">0.00 kg</span>
</div>
<div class="phys-row">
<span class="phys-label">dm/dt</span>
<span class="phys-value" id="disp_dmt">0.00 kg/s</span>
</div>
<div class="phys-row">
<span class="phys-label">NG LNG (p)</span>
<span class="phys-value" id="disp_p" style="color:var(--cyan);">0.00</span>
</div>
</div>
</div>
<!-- RIGHT PANEL -->
<div style="display:flex;flex-direction:column;gap:16px;">
<!-- METRIC CARDS -->
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;">
<div class="glass metric-card" style="border-color:rgba(168,85,247,0.3);">
<div class="metric-label" style="color:rgba(168,85,247,0.6);">NKTg N NH</div>
<div class="metric-value" id="res_n1" style="color:var(--purple);">0.00</div>
<div style="font-size:10px;color:rgba(224,247,255,0.3);margin-top:4px;">NKTm</div>
</div>
<div class="glass metric-card" style="border-color:rgba(0,255,136,0.2);">
<div class="metric-label" style="color:rgba(0,255,136,0.5);">NKTg XUNG LC</div>
<div class="metric-value" id="res_n2" style="color:var(--green);">0.00</div>
<div style="font-size:10px;color:rgba(224,247,255,0.3);margin-top:4px;">NKTm/s</div>
</div>
<div class="glass metric-card">
<div class="metric-label">BC M PHNG</div>
<div class="metric-value" id="res_step" style="color:var(--yellow);">0 / 600</div>
<div style="font-size:10px;color:rgba(224,247,255,0.3);margin-top:4px;">steps</div>
</div>
</div>
<!-- CHART NKTg1 & NKTg2 -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;">
<div>
<div class="sec-label">REAL-TIME ANALYSIS</div>
<div style="font-size:14px;font-weight:700;color:var(--cyan);">Biu NKTg & NKTg theo thi gian</div>
</div>
<button onclick="resetSim()" style="font-size:10px;color:rgba(224,247,255,0.3);background:none;border:1px solid rgba(224,247,255,0.1);padding:4px 10px;border-radius:4px;cursor:pointer;letter-spacing:1px;">RESET</button>
</div>
<div class="chart-container">
<canvas id="nktgChart"></canvas>
</div>
</div>
<!-- CHART PHYSICS -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div style="margin-bottom:14px;">
<div class="sec-label">PHYSICS DYNAMICS</div>
<div style="font-size:14px;font-weight:700;color:var(--cyan);">Bin thin x, v, m theo thi gian</div>
</div>
<div class="chart-container">
<canvas id="physChart"></canvas>
</div>
</div>
<!-- S KIN LOG -->
<div class="glass" style="border-radius:12px;padding:18px;">
<div class="sec-label">EVENT LOG</div>
<div class="sec-title"><i class="fas fa-terminal" style="font-size:12px;"></i> Nht k s kin NKTg</div>
<div id="eventLog" style="font-family:'Share Tech Mono',monospace;font-size:11px;background:rgba(0,0,0,0.4);border:1px solid rgba(0,245,255,0.1);border-radius:8px;padding:10px;height:100px;overflow-y:auto;color:rgba(0,245,255,0.6);line-height:1.7;">
<p style="color:rgba(0,245,255,0.4);">> Chn kch bn v nhn CHY M PHNG...</p>
</div>
</div>
</div>
</main>
<script>
// =========================================================
// CU HNH CC KCH BN
// =========================================================
const SCENARIOS = {
rocket: {
name: 'Tn la',
icon: '',
// Trng thi ban u
x0: 0, v0: 0, m0: 500, dm_dt: -5,
// Lc y (N) gim dn khi ht nhin liu
thrust: 15000,
// Thi gian m phng (giy)
duration: 30,
// M t s kin
events: [
{ t: 0, msg: ' Tn la khai ha! Lc y 15,000N', type: 'info' },
{ t: 10, msg: ' tiu hao 50kg nhin liu xe nh hn, v tng nhanh hn', type: 'warn' },
{ t: 20, msg: ' Nhin liu cn 40% NKTg tng vt do x v v u ln', type: 'danger' },
{ t: 28, msg: ' Ht nhin liu dm/dt = 0, h thng n nh qun tnh', type: 'success' },
],
info: '<strong>Tn la:</strong> Khi ng m=500kg, t -5kg/s u n, lc y 15,000N. Quan st NKTg tng khi tn la bay xa v NKTg m n nh khi nhin liu t u.'
},
drone: {
name: 'Drone',
icon: '',
x0: 0, v0: 5, m0: 50, dm_dt: -0.1,
thrust: 500,
duration: 30,
events: [
{ t: 0, msg: ' Drone ct cnh v=5m/s, m=50kg', type: 'info' },
{ t: 10, msg: ' Drone n im th hng chun b th 20kg', type: 'warn' },
{ t: 12, msg: ' TH HNG! dm/dt = -20 kg/s t ngt NKTg m mnh!', type: 'danger' },
{ t: 13, msg: ' Drone bt ln do mt khi lng t ngt nguy him!', type: 'danger' },
{ t: 16, msg: ' H thng b lc rotor Drone ly li cn bng', type: 'success' },
],
info: '<strong>Drone:</strong> Bay bnh thng, n t=12s th hng 20kg t ngt. Quan st NKTg m nhy vt y l cnh bo PID khng c!'
},
car: {
name: 'Xe hi',
icon: '',
x0: 0, v0: 0, m0: 1500, dm_dt: -0.02,
thrust: 3000,
duration: 30,
events: [
{ t: 0, msg: ' Xe xut pht tng tc t 0', type: 'info' },
{ t: 8, msg: ' t 100 km/h NKTg tng nhanh', type: 'warn' },
{ t: 15, msg: ' NKTg vt ngng 1,000,000 phanh khn cp!', type: 'danger' },
{ t: 16, msg: ' Kch hot phanh lc y = 0, xe gim tc', type: 'warn' },
{ t: 25, msg: ' Xe dng an ton NKTg v 0', type: 'success' },
],
info: '<strong>Xe hi:</strong> Tng tc t 0, n t=15s NKTg vt ngng nguy him phanh khn cp. Quan st qu trnh gim tc v NKTg tr v an ton.'
}
};
// =========================================================
// BIN TRNG THI
// =========================================================
let currentScenario = 'rocket';
let simTimer = null;
let isRunning = false;
let simTime = 0;
const DT = 0.05; // 50ms mi bc
let state = {};
let brakingActive = false;
// Lu lch s v th
const MAXPOINTS = 600;
let histTime = [];
let histN1 = [], histN2 = [];
let histX = [], histV = [], histM = [];
// =========================================================
// KHI TO TH
// =========================================================
const nktgChart = new Chart(document.getElementById('nktgChart').getContext('2d'), {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'NKTg (n nh)',
data: [], borderColor: '#a855f7', borderWidth: 2,
tension: 0.3, fill: true,
backgroundColor: 'rgba(168,85,247,0.06)',
pointRadius: 0
},
{
label: 'NKTg (Xung lc)',
data: [], borderColor: '#00ff88', borderWidth: 2,
tension: 0.3, fill: false,
pointRadius: 0
}
]
},
options: {
responsive: true, maintainAspectRatio: false,
animation: { duration: 0 },
scales: {
x: { ticks: { color: 'rgba(224,247,255,0.3)', maxTicksLimit: 10, font: { family: "'Share Tech Mono'" } }, grid: { color: 'rgba(0,245,255,0.04)' } },
y: { ticks: { color: 'rgba(224,247,255,0.3)', font: { family: "'Share Tech Mono'" } }, grid: { color: 'rgba(0,245,255,0.04)' } }
},
plugins: {
legend: { labels: { color: 'rgba(224,247,255,0.5)', font: { family: "'Exo 2'" }, boxWidth: 14, padding: 12 } }
}
}
});
const physChart = new Chart(document.getElementById('physChart').getContext('2d'), {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'V tr x (m)',
data: [], borderColor: '#00f5ff', borderWidth: 2,
tension: 0.3, fill: false, pointRadius: 0
},
{
label: 'Vn tc v (m/s)',
data: [], borderColor: '#ffd700', borderWidth: 2,
tension: 0.3, fill: false, pointRadius: 0
},
{
label: 'Khi lng m (kg/10)',
data: [], borderColor: '#ff8c00', borderWidth: 2,
tension: 0.3, fill: false, pointRadius: 0,
borderDash: [5, 3]
}
]
},
options: {
responsive: true, maintainAspectRatio: false,
animation: { duration: 0 },
scales: {
x: { ticks: { color: 'rgba(224,247,255,0.3)', maxTicksLimit: 10, font: { family: "'Share Tech Mono'" } }, grid: { color: 'rgba(0,245,255,0.04)' } },
y: { ticks: { color: 'rgba(224,247,255,0.3)', font: { family: "'Share Tech Mono'" } }, grid: { color: 'rgba(0,245,255,0.04)' } }
},
plugins: {
legend: { labels: { color: 'rgba(224,247,255,0.5)', font: { family: "'Exo 2'" }, boxWidth: 14, padding: 12 } }
}
}
});
// =========================================================
// CHN KCH BN
// =========================================================
function selectScenario(name) {
if (isRunning) return;
currentScenario = name;
// Cp nht nt
document.querySelectorAll('.scenario-btn').forEach(b => b.classList.remove('active'));
document.querySelector(`.scenario-btn.${name}`).classList.add('active');
// Cp nht m t
document.getElementById('scenarioInfo').innerHTML = SCENARIOS[name].info;
resetSim();
}
// =========================================================
// PLAY / STOP
// =========================================================
function toggleSim() {
if (isRunning) {
stopSim();
} else {
startSim();
}
}
function startSim() {
const sc = SCENARIOS[currentScenario];
// Khi to trng thi
state = {
x: sc.x0, v: sc.v0, m: sc.m0,
dm_dt: sc.dm_dt,
thrust: sc.thrust,
t: 0
};
brakingActive = false;
simTime = 0;
// Xa lch s
histTime = []; histN1 = []; histN2 = [];
histX = []; histV = []; histM = [];
nktgChart.data.labels = [];
nktgChart.data.datasets.forEach(d => d.data = []);
physChart.data.labels = [];
physChart.data.datasets.forEach(d => d.data = []);
// Xa log
document.getElementById('eventLog').innerHTML = '';
addLog(`${sc.icon} Bt u m phng: ${sc.name}`, 'info');
isRunning = true;
document.getElementById('playBtn').className = 'running';
document.getElementById('playBtn').innerText = ' DNG M PHNG';
simTimer = setInterval(simStep, 50);
}
function stopSim() {
clearInterval(simTimer);
isRunning = false;
document.getElementById('playBtn').className = '';
document.getElementById('playBtn').innerText = ' CHY LI';
addLog(' M phng dng.', 'info');
}
function resetSim() {
stopSim();
simTime = 0;
histTime = []; histN1 = []; histN2 = [];
histX = []; histV = []; histM = [];
nktgChart.data.labels = [];
nktgChart.data.datasets.forEach(d => d.data = []);
physChart.data.labels = [];
physChart.data.datasets.forEach(d => d.data = []);
nktgChart.update(); physChart.update();
document.getElementById('timeDisplay').innerText = 'T = 0.00s';
document.getElementById('statusBox').className = 'waiting';
document.getElementById('statusBox').innerText = 'ANG CH...';
document.getElementById('advice').innerText = '';
document.getElementById('res_n1').innerText = '0.00';
document.getElementById('res_n2').innerText = '0.00';
document.getElementById('res_step').innerText = '0 / 600';
document.getElementById('progressFill').style.width = '0%';
document.getElementById('progressLabel').innerText = '0%';
document.getElementById('disp_x').innerText = '0.00 m';
document.getElementById('disp_v').innerText = '0.00 m/s';
document.getElementById('disp_m').innerText = '0.00 kg';
document.getElementById('disp_dmt').innerText = '0.00 kg/s';
document.getElementById('disp_p').innerText = '0.00';
document.getElementById('playBtn').innerText = ' CHY M PHNG';
document.getElementById('eventLog').innerHTML = '<p style="color:rgba(0,245,255,0.4);">> Nhn CHY M PHNG bt u...</p>';
}
// =========================================================
// BC M PHNG VT L
// =========================================================
function simStep() {
const sc = SCENARIOS[currentScenario];
const totalSteps = sc.duration / DT;
const currentStep = Math.round(simTime / DT);
// Kim tra s kin theo thi gian
sc.events.forEach(ev => {
if (Math.abs(simTime - ev.t) < DT / 2) {
addLog(ev.msg, ev.type);
}
});
// === VT L TNG KCH BN ===
if (currentScenario === 'rocket') {
// Nhin liu cn th t, ht th dm_dt = 0
if (state.m > 50) { // 50kg = khi lng khung tn la
state.m += state.dm_dt * DT;
state.m = Math.max(50, state.m);
} else {
state.dm_dt = 0;
}
// a = F/m
const a = state.thrust / state.m;
state.v += a * DT;
state.x += state.v * DT;
}
else if (currentScenario === 'drone') {
// Giai on bnh thng
if (simTime < 12) {
state.m += state.dm_dt * DT; // tiu hao nhin liu nh
const a = (state.thrust - state.m * 9.81) / state.m;
state.v += a * DT * 0.1; // gi tc n nh
state.x += state.v * DT;
}
// Th hng t ngt t=12s
else if (simTime >= 12 && simTime < 13) {
state.dm_dt = -20; // th 20kg/s trong 1 giy
state.m += state.dm_dt * DT;
state.m = Math.max(28, state.m); // ti thiu 28kg
// Drone bt ln do mt ti t ngt
state.v += 3 * DT;
state.x += state.v * DT;
}
// Sau th hng ly li cn bng
else {
state.dm_dt = -0.05; // ch tiu hao nhin liu nh
state.m += state.dm_dt * DT;
state.m = Math.max(20, state.m);
const a = (state.thrust * 0.6 - state.m * 9.81) / state.m;
state.v += a * DT * 0.05;
state.x += state.v * DT;
}
}
else if (currentScenario === 'car') {
// Tng tc n t=15s
if (simTime < 15 && !brakingActive) {
state.m += state.dm_dt * DT; // tiu hao xng nh
const a = state.thrust / state.m;
state.v += a * DT;
state.x += state.v * DT;
// Kim tra ngng NKTg
const p_check = state.m * state.v;
const n1_check = state.x * p_check;
if (n1_check > 1000000) {
brakingActive = true;
addLog(' NKTg vt ngng! Phanh khn cp kch hot!', 'danger');
}
}
// Phanh
else {
brakingActive = true;
state.dm_dt = 0;
// Gim tc do phanh
state.v -= 8 * DT; // gia tc phanh -8 m/s
state.v = Math.max(0, state.v);
state.x += state.v * DT;
}
}
// === TNH NKTg ===
const p = state.m * state.v;
const n1 = state.x * p;
const n2 = state.dm_dt * p;
// === CP NHT UI ===
document.getElementById('timeDisplay').innerText = `T = ${simTime.toFixed(2)}s`;
document.getElementById('disp_x').innerText = state.x.toFixed(2) + ' m';
document.getElementById('disp_v').innerText = state.v.toFixed(2) + ' m/s';
document.getElementById('disp_m').innerText = state.m.toFixed(2) + ' kg';
document.getElementById('disp_dmt').innerText = state.dm_dt.toFixed(3) + ' kg/s';
document.getElementById('disp_p').innerText = p.toFixed(2);
document.getElementById('res_n1').innerText = n1.toFixed(0);
document.getElementById('res_n2').innerText = n2.toFixed(0);
document.getElementById('res_step').innerText = `${currentStep} / ${totalSteps}`;
// Progress
const pct = Math.min(100, (simTime / sc.duration) * 100);
document.getElementById('progressFill').style.width = pct + '%';
document.getElementById('progressLabel').innerText = pct.toFixed(0) + '%';
// Status
const sb = document.getElementById('statusBox');
if (n1 > 0) {
sb.innerText = ' NGUY HIM'; sb.className = 'danger';
document.getElementById('advice').innerText = 'H thng ri xa n nh! Kch hot phanh phn x.';
} else if (n1 < 0) {
sb.innerText = ' N NH'; sb.className = 'stable';
document.getElementById('advice').innerText = 'H thng hi t tt. Duy tr qun tnh bn.';
} else {
sb.innerText = ' CN BNG'; sb.className = 'balance';
document.getElementById('advice').innerText = 'im cn bng l tng.';
}
// === CP NHT TH ===
const tLabel = simTime.toFixed(1) + 's';
nktgChart.data.labels.push(tLabel);
nktgChart.data.datasets[0].data.push(n1);
nktgChart.data.datasets[1].data.push(n2);
physChart.data.labels.push(tLabel);
physChart.data.datasets[0].data.push(state.x);
physChart.data.datasets[1].data.push(state.v);
physChart.data.datasets[2].data.push(state.m / 10); // chia 10 fit th
nktgChart.update('none');
physChart.update('none');
// === TNG THI GIAN ===
simTime += DT;
// Kt thc
if (simTime >= sc.duration) {
stopSim();
addLog(` M phng hon thnh sau ${sc.duration}s!`, 'success');
document.getElementById('playBtn').innerText = ' CHY LI';
}
}
// =========================================================
// EVENT LOG
// =========================================================
function addLog(msg, type = 'info') {
const log = document.getElementById('eventLog');
const colors = {
info: 'rgba(0,245,255,0.7)',
warn: 'rgba(255,215,0,0.8)',
danger: 'rgba(255,51,102,0.9)',
success: 'rgba(0,255,136,0.8)'
};
const p = document.createElement('p');
p.style.color = colors[type] || colors.info;
p.style.margin = '0';
p.style.padding = '1px 0';
p.textContent = `[T=${simTime.toFixed(1)}s] ${msg}`;
log.appendChild(p);
while (log.children.length > 50) log.removeChild(log.firstChild);
log.scrollTop = log.scrollHeight;
}
// Khi chy
selectScenario('rocket');
</script>
</body>
</html>





Comments