interactive_guide_level2.html•81.9 kB
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🏥 Poker MCP Interactive Guide - Level 2: 実用設計</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
:root {
--primary-blue: #1e3a8a;
--primary-green: #059669;
--warning-orange: #d97706;
--danger-red: #dc2626;
--accent-physics: #7c3aed;
--accent-success: #10b981;
--accent-medical: #ec4899;
--accent-nuclear: #f59e0b;
--accent-research: #06b6d4;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
color: #1f2937;
overflow-x: hidden;
}
.main-header {
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--accent-physics) 100%);
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.logo {
font-size: 1.5rem;
font-weight: bold;
}
.progress-container {
flex: 1;
max-width: 400px;
margin: 0 2rem;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
overflow: hidden;
}
.progress {
height: 100%;
background: var(--accent-success);
transition: width 0.5s ease;
border-radius: 4px;
}
.level-indicator {
font-size: 0.9rem;
opacity: 0.9;
margin-top: 0.25rem;
}
.scenario-selector {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.scenario-modal {
background: white;
border-radius: 20px;
padding: 2rem;
max-width: 1000px;
max-height: 90vh;
overflow-y: auto;
}
.scenario-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
.scenario-card {
border: 2px solid #e5e7eb;
border-radius: 12px;
padding: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.scenario-card:hover {
border-color: var(--primary-blue);
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.scenario-card.medical { border-color: var(--accent-medical); }
.scenario-card.nuclear { border-color: var(--accent-nuclear); }
.scenario-card.research { border-color: var(--accent-research); }
.scenario-preparation {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #e5e7eb;
}
.preparation-badge {
background: linear-gradient(45deg, var(--accent-physics), var(--primary-blue));
color: white;
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 600;
display: inline-block;
margin-bottom: 0.5rem;
}
.preparation-list {
list-style: none;
padding: 0;
margin: 0;
}
.preparation-list li {
font-size: 0.8rem;
color: #6b7280;
padding: 0.2rem 0;
position: relative;
padding-left: 1rem;
}
.preparation-list li:before {
content: "•";
color: var(--primary-blue);
font-weight: bold;
position: absolute;
left: 0;
}
.scenario-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.scenario-title {
font-size: 1.3rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.scenario-description {
font-size: 0.95rem;
color: #6b7280;
margin-bottom: 1rem;
}
.main-content {
display: grid;
grid-template-columns: 320px 1fr 350px;
gap: 1rem;
padding: 1rem;
height: calc(100vh - 140px);
}
.instruction-panel, .design-panel, .visualization-panel {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
overflow-y: auto;
}
.visualization-panel {
position: relative;
overflow: hidden;
padding: 0;
}
#threejs-container {
width: 100%;
height: 100%;
}
.view-controls {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.view-btn {
background: rgba(255, 255, 255, 0.9);
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
}
.view-btn.active {
background: var(--primary-blue);
color: white;
}
.design-tools {
position: absolute;
bottom: 1rem;
left: 1rem;
display: flex;
gap: 0.5rem;
}
.tool-btn {
background: var(--primary-green);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
}
.step-indicator {
background: linear-gradient(135deg, var(--accent-physics), #9333ea);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.85rem;
margin-bottom: 1rem;
text-align: center;
}
.design-section {
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e5e7eb;
}
.design-section h3 {
color: var(--primary-blue);
margin-bottom: 1rem;
font-size: 1.1rem;
}
.parameter-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1rem;
}
.parameter-input {
width: 100%;
padding: 0.75rem;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 0.9rem;
}
.slider-group {
margin: 1rem 0;
}
.slider-group label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
}
.slider {
width: 100%;
height: 6px;
background: #e5e7eb;
outline: none;
-webkit-appearance: none;
}
.slider::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary-blue);
cursor: pointer;
}
.slider-value {
background: var(--primary-blue);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 600;
margin-left: 0.5rem;
}
.calc-btn {
background: linear-gradient(135deg, var(--primary-blue), var(--accent-physics));
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 10px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
width: 100%;
margin: 1rem 0;
}
.results-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 1rem;
}
.result-card {
background: rgba(255, 255, 255, 0.8);
border-radius: 8px;
padding: 1rem;
text-align: center;
}
.result-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-blue);
margin: 0.5rem 0;
}
.result-label {
font-size: 0.8rem;
color: #6b7280;
text-transform: uppercase;
}
.result-status {
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 600;
margin-top: 0.5rem;
display: inline-block;
}
.result-status.ok { background: var(--primary-green); color: white; }
.result-status.warning { background: var(--warning-orange); color: white; }
.optimization-game {
background: linear-gradient(135deg, #fef7cd, #fbbf24);
border-radius: 12px;
padding: 1.5rem;
margin: 1rem 0;
display: none;
}
.constraints-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
margin: 1rem 0;
}
.constraint-item {
background: rgba(255, 255, 255, 0.7);
border-radius: 6px;
padding: 0.75rem;
text-align: center;
font-size: 0.8rem;
}
.constraint-value {
font-weight: bold;
color: var(--primary-blue);
display: block;
margin-top: 0.25rem;
}
.score-display {
background: rgba(255, 255, 255, 0.9);
border-radius: 10px;
padding: 1rem;
text-align: center;
margin-top: 1rem;
}
.score-value {
font-size: 2rem;
font-weight: bold;
color: var(--primary-green);
margin: 0.5rem 0;
}
.navigation-footer {
background: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-btn {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
border: none;
}
.nav-btn.primary { background: var(--primary-blue); color: white; }
.nav-btn.secondary { background: #e5e7eb; color: #1f2937; }
.nav-btn:disabled { opacity: 0.5; cursor: not-allowed; }
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
height: auto;
}
.scenario-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="scenario-selector" id="scenario-selector">
<div class="scenario-modal">
<h2 style="text-align: center; color: var(--primary-blue); margin-bottom: 1rem;">
🏗️ 実用設計シナリオを選択してください
</h2>
<p style="text-align: center; color: #6b7280; margin-bottom: 1.5rem;">
実際の施設設計を体験しながら、放射線遮蔽の実用スキルを習得しましょう
</p>
<div class="scenario-grid">
<div class="scenario-card medical" data-scenario="medical">
<div class="scenario-icon">🏥</div>
<div class="scenario-title">医療施設遮蔽設計</div>
<div class="scenario-description">
18MV線形加速器治療室の遮蔽設計。医療法施行規則に基づく安全で効率的な設計を学習します。
</div>
<div class="scenario-preparation">
<div class="preparation-badge">📋 Level 3A準備</div>
<ul class="preparation-list">
<li>医療法施行規則の理解</li>
<li>管理区域境界線量基準</li>
<li>安全管理体制の基礎</li>
</ul>
</div>
</div>
<div class="scenario-card nuclear" data-scenario="nuclear">
<div class="scenario-icon">⚛️</div>
<div class="scenario-title">原子力施設遮蔽設計</div>
<div class="scenario-description">
高レベル放射性廃棄物貯蔵施設の遮蔽設計。複合遮蔽と長期安全性を考慮した設計手法を習得します。
</div>
<div class="scenario-preparation">
<div class="preparation-badge">📋 Level 3B準備</div>
<ul class="preparation-list">
<li>原子炉等規制法の理解</li>
<li>複合遮蔽材料の選択</li>
<li>長期安全性評価</li>
</ul>
</div>
</div>
<div class="scenario-card research" data-scenario="research">
<div class="scenario-icon">🔬</div>
<div class="scenario-title">研究施設遮蔽設計</div>
<div class="scenario-description">
サイクロトロン加速器施設の中性子遮蔽設計。多様な放射線に対応する複合遮蔽システムを習得します。
</div>
<div class="scenario-preparation">
<div class="preparation-badge">📋 Level 3C準備</div>
<ul class="preparation-list">
<li>放射線障害防止法の理解</li>
<li>中性子遮蔽の基礎</li>
<li>複合放射線場の概念</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<header class="main-header">
<div class="logo">🏥 Poker MCP Interactive Guide - Level 2</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress" id="main-progress" style="width: 0%"></div>
</div>
<div class="level-indicator">実用設計 - 完全版</div>
</div>
<div class="current-step" id="current-step-display">シナリオ選択</div>
</header>
<main class="main-content">
<div class="instruction-panel">
<div class="step-indicator" id="step-indicator">Step 0: 実用シナリオ選択</div>
<div id="instruction-content">
<h2>🎯 Level 2: 実用設計へようこそ</h2>
<p>このレベルでは、実際の施設で使用される遮蔽設計技術を習得します。</p>
<div style="background: #ede9fe; border: 1px solid var(--accent-physics); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h3>🎓 学習目標</h3>
<ul>
<li>実際の施設設計プロセス体験</li>
<li>制約条件下での最適化手法</li>
<li>分野別専門知識の理解</li>
<li>総合的な設計判断力</li>
</ul>
</div>
<p>上から1つのシナリオを選択して、実用的な遮蔽設計を体験してください。</p>
</div>
</div>
<div class="visualization-panel">
<div id="threejs-container"></div>
<div class="view-controls">
<button class="view-btn active" id="view-structure">🏗️ 構造</button>
<button class="view-btn" id="view-dose">☢️ 線量</button>
<button class="view-btn" id="view-cost">💰 コスト</button>
</div>
<div class="design-tools">
<button class="tool-btn" id="optimize-auto">🤖 最適化</button>
<button class="tool-btn" id="validate-design">✅ 検証</button>
<button class="tool-btn" id="export-report">📄 報告書</button>
</div>
</div>
<div class="design-panel">
<div class="design-section">
<h3>🏗️ 基本幾何設計</h3>
<div class="parameter-grid">
<div>
<label>室内長さ (m)</label>
<input type="number" class="parameter-input" id="room-length" value="8" min="5" max="15" step="0.5">
</div>
<div>
<label>室内幅 (m)</label>
<input type="number" class="parameter-input" id="room-width" value="6" min="4" max="12" step="0.5">
</div>
<div>
<label>室内高さ (m)</label>
<input type="number" class="parameter-input" id="room-height" value="3" min="2.5" max="5" step="0.1">
</div>
<div>
<label>迷路長さ (m)</label>
<input type="number" class="parameter-input" id="maze-length" value="4" min="2" max="8" step="0.5">
</div>
</div>
</div>
<div class="design-section">
<h3>🧱 遮蔽材料設計</h3>
<div class="slider-group">
<label>主壁厚さ (cm)</label>
<input type="range" class="slider" id="main-wall-thickness" min="50" max="300" value="150" step="5">
<span class="slider-value" id="main-wall-value">150 cm</span>
</div>
<div class="slider-group">
<label>ドア遮蔽厚さ (cm)</label>
<input type="range" class="slider" id="door-thickness" min="5" max="50" value="15" step="1">
<span class="slider-value" id="door-value">15 cm</span>
</div>
<div style="margin: 1rem 0;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">主壁材料</label>
<select class="parameter-input" id="wall-material">
<option value="concrete_normal">普通コンクリート</option>
<option value="concrete_heavy" selected>重コンクリート</option>
<option value="concrete_barite">バライトコンクリート</option>
<option value="concrete_borated">ホウ素入りコンクリート</option>
<option value="steel_concrete">鋼鉄+コンクリート複合</option>
</select>
</div>
<div style="margin: 1rem 0;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">ドア材料</label>
<select class="parameter-input" id="door-material">
<option value="lead" selected>純鉛</option>
<option value="steel_lead">鋼鉄+鉛複合</option>
<option value="tungsten">タングステン</option>
</select>
</div>
</div>
<div style="background: #f0fdf4; border: 1px solid var(--primary-green); border-radius: 10px; padding: 1.5rem; margin: 1rem 0;">
<button class="calc-btn" id="calculate-design">⚡ 高精度遮蔽性能計算</button>
<div class="results-grid" id="results-grid" style="display: none;">
<div class="result-card">
<div class="result-label">最大線量率</div>
<div class="result-value" id="max-dose-rate">-- μSv/h</div>
<div class="result-status" id="dose-status">--</div>
</div>
<div class="result-card">
<div class="result-label">総工事費</div>
<div class="result-value" id="total-cost">-- 万円</div>
<div class="result-status" id="cost-status">--</div>
</div>
<div class="result-card">
<div class="result-label">材料重量</div>
<div class="result-value" id="total-weight">-- トン</div>
<div class="result-status" id="weight-status">--</div>
</div>
<div class="result-card">
<div class="result-label">安全余裕</div>
<div class="result-value" id="safety-margin">-- %</div>
<div class="result-status" id="margin-status">--</div>
</div>
</div>
</div>
<div class="optimization-game" id="optimization-game">
<div style="text-align: center; margin-bottom: 1rem;">
<h4 style="color: var(--warning-orange); margin-bottom: 0.5rem;">🎯 設計最適化チャレンジ</h4>
<p>制約条件内で最高効率の設計を達成しよう!</p>
</div>
<div class="constraints-grid">
<div class="constraint-item">
<div>線量率上限</div>
<div class="constraint-value" id="dose-constraint">2.5 μSv/h</div>
</div>
<div class="constraint-item">
<div>予算上限</div>
<div class="constraint-value" id="cost-constraint">5000 万円</div>
</div>
<div class="constraint-item">
<div>重量制限</div>
<div class="constraint-value" id="weight-constraint">800 トン</div>
</div>
<div class="constraint-item">
<div>工期制限</div>
<div class="constraint-value" id="schedule-constraint">6 ヶ月</div>
</div>
</div>
<div class="score-display">
<div class="result-label">総合最適化スコア</div>
<div class="score-value" id="optimization-score">0</div>
<div style="font-size: 0.8rem; color: #6b7280;">100点満点</div>
</div>
</div>
</div>
</main>
<footer class="navigation-footer">
<button class="nav-btn secondary" id="prev-step" disabled>← 前のステップ</button>
<div style="display: flex; gap: 1rem;">
<button class="nav-btn secondary" id="hint-btn">💡 ヒント</button>
<button class="nav-btn secondary" id="reset-design">🔄 リセット</button>
<button class="nav-btn secondary" id="save-design">💾 保存</button>
</div>
<button class="nav-btn primary" id="next-step">次のステップ →</button>
</footer>
<script>
const scenarioDatabase = {
medical: {
name: "医療施設遮蔽設計",
icon: "🏥",
description: "18MV線形加速器治療室",
source: { type: "linac_18MV", energy: 18, nominalRate: 600 },
requirements: { doseLimit: 2.5, regulation: "医療法施行規則" },
constraints: { maxCost: 5000, maxWeight: 800, maxSchedule: 6 },
materials: ["concrete_heavy", "concrete_barite", "steel_concrete"],
defaultDesign: { roomDimensions: [8, 6, 3], wallThickness: 150, doorThickness: 15, mazeLength: 4 }
},
nuclear: {
name: "原子力施設遮蔽設計",
icon: "⚛️",
description: "高レベル放射性廃棄物貯蔵施設",
source: { type: "mixed_waste", activity: 1e15 },
requirements: { doseLimit: 250, regulation: "核燃料物質等の規制に関する法律" },
constraints: { maxCost: 15000, maxWeight: 2000, maxSchedule: 12 },
materials: ["steel_concrete", "concrete_heavy"],
defaultDesign: { roomDimensions: [6, 6, 4], wallThickness: 200, doorThickness: 25, mazeLength: 6 }
},
research: {
name: "研究施設遮蔽設計",
icon: "🔬",
description: "サイクロトロン加速器施設",
source: { type: "cyclotron", energy: 30, current: 100 },
requirements: { doseLimit: 5.0, regulation: "放射線障害防止法" },
constraints: { maxCost: 8000, maxWeight: 1200, maxSchedule: 9 },
materials: ["concrete_borated", "steel_concrete", "concrete_heavy"],
defaultDesign: { roomDimensions: [10, 8, 4], wallThickness: 180, doorThickness: 20, mazeLength: 5 }
}
};
const materialDatabase = {
concrete_normal: { name: "普通コンクリート", density: 2.3, muMass: 0.0636, cost: 25000, color: 0xd2b48c },
concrete_heavy: { name: "重コンクリート", density: 3.5, muMass: 0.0636, cost: 45000, color: 0x8b7355 },
concrete_barite: { name: "バライトコンクリート", density: 4.2, muMass: 0.0620, cost: 85000, color: 0x9d8964 },
concrete_borated: { name: "ホウ素入りコンクリート", density: 2.8, muMass: 0.0636, cost: 65000, color: 0xa0a0ff },
steel_concrete: { name: "鋼鉄+コンクリート複合", density: 4.2, muMass: 0.0580, cost: 85000, color: 0x708090 },
lead: { name: "純鉛", density: 11.34, muMass: 0.107, cost: 450000, color: 0x696969 },
steel_lead: { name: "鋼鉄+鉛複合", density: 9.5, muMass: 0.089, cost: 320000, color: 0x2f4f4f },
tungsten: { name: "タングステン", density: 19.25, muMass: 0.0947, cost: 2800000, color: 0x404040 }
};
let scene, camera, renderer;
let currentScenario = null;
let designParameters = {};
let calculationResults = {};
let visualizer = null;
let progressManager = null;
class ProgressManager {
constructor() {
this.currentStep = 0;
this.totalSteps = 6;
this.scenario = null;
}
setScenario(scenarioKey) {
this.scenario = scenarioKey;
this.currentStep = 1;
this.updateProgress();
this.updateStepContent();
}
updateProgress() {
const progress = Math.max(0, (this.currentStep - 1) / (this.totalSteps - 1) * 100);
document.getElementById('main-progress').style.width = progress + '%';
const stepNames = ["シナリオ選択", "法規制・仕様理解", "基本幾何設計", "材料設計・申請基礎", "性能最適化", "品質保証・検証", "Level3準備完了"];
document.getElementById('current-step-display').textContent = stepNames[this.currentStep] || `Step ${this.currentStep}`;
}
nextStep() {
if (this.currentStep < this.totalSteps) {
this.currentStep++;
this.updateStepContent();
this.updateProgress();
} else {
alert('🎉 Level 2 完了!\n\n実用的な遮蔽設計スキルを習得しました!');
}
}
updateStepContent() {
const scenario = scenarioDatabase[this.scenario];
if (!scenario) return;
const stepContents = {
1: {
title: `Step 1: ${scenario.name} - 施設仕様・法規制理解`,
content: `
<div style="background: #ede9fe; border: 1px solid var(--accent-physics); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h3>${scenario.icon} ${scenario.name}</h3>
<p><strong>${scenario.description}</strong></p>
<h4>🎯 設計要求事項</h4>
<ul>
<li>線量率限度: <strong>${scenario.requirements.doseLimit} μSv/h</strong></li>
<li>法的根拠: ${scenario.requirements.regulation}</li>
</ul>
</div>
<div class="regulation-details" style="background: #fef7cd; border: 1px solid var(--warning-orange); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>📋 ${this.getRegulationTitle()}</h4>
<div class="regulation-content">
${this.getRegulationContent()}
</div>
</div>
<div class="workflow-preview" style="background: #f0fdf4; border: 1px solid var(--primary-green); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🔄 実務ワークフロー概要</h4>
<div class="workflow-steps">
企画 → 基本設計 → <strong>詳細設計(現在学習中)</strong> → 申請書類作成 → 施工 → 検査 → 運用
</div>
<p style="font-size: 0.9rem; color: #6b7280; margin-top: 0.5rem;">
Level 3では、申請から運用まで全プロセスを詳細に学習します
</p>
</div>
<p>💡 右側の3D表示で施設構造を確認し、設計パラメータを理解しましょう。</p>
`
},
2: {
title: "Step 2: 基本幾何設計",
content: `
<h3>🏗️ 施設幾何の詳細設計</h3>
<p>治療室・貯蔵室・実験室の最適な空間配置を設計します。</p>
<div style="background: #fef3c7; border: 1px solid var(--warning-orange); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🎯 設計考慮事項</h4>
<ul>
<li><strong>室内寸法</strong>: 設備配置・作業空間・保守アクセス</li>
<li><strong>迷路設計</strong>: 散乱線低減・入退室効率</li>
<li><strong>構造強度</strong>: 自重・地震荷重・長期安定性</li>
</ul>
</div>
<p>⚡ パラメータを調整して最適な施設レイアウトを設計してください。</p>
`
},
3: {
title: "Step 3: 遮蔽材料設計・申請書類基礎",
content: `
<h3>🧱 高度な遮蔽材料設計</h3>
<p>材料選択と厚さ決定により、安全性と経済性を両立します。</p>
<div style="background: #fef2f2; border: 1px solid var(--danger-red); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>⚖️ 遮蔽性能の定量評価</h4>
<p><strong>Cs-137を1/100に減衰させる厚さ:</strong></p>
<ul>
<li>鉛: 約4cm(最高効率)</li>
<li>コンクリート: 約80cm(大容量・低コスト)</li>
<li>重コンクリート: 約50cm(中間解)</li>
</ul>
</div>
<p>🧪 「高精度遮蔽性能計算」で実際の減衰を確認しながら最適化してください。</p>
<div class="document-creation-preview" style="background: #fef7cd; border: 1px solid var(--warning-orange); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>📄 設計書作成の基礎体験</h4>
<p>実務では、計算結果を申請書類にまとめる必要があります。</p>
<div class="basic-document-template">
<div class="template-section">
<strong>1. 設計概要:</strong>
<span class="auto-fill" style="color: var(--primary-blue); font-weight: 600;">
${this.getBasicDesignSummary()}
</span>
</div>
<div class="template-section">
<strong>2. 遮蔽計算結果:</strong>
<span class="auto-fill" id="doc-calc-results" style="color: var(--primary-blue);">
計算実行後に自動挿入
</span>
</div>
<div class="template-section">
<strong>3. 法規制遵守状況:</strong>
<span class="auto-fill" id="doc-compliance" style="color: var(--primary-blue);">
計算実行後に自動評価
</span>
</div>
</div>
<p style="font-size: 0.9rem; color: #6b7280; margin-top: 0.75rem;">
⚡ 計算実行後、上記テンプレートに結果が自動挿入されます<br>
📚 Level 3では、正式な申請書類の作成方法を詳しく学習します
</p>
</div>
`
},
4: {
title: "Step 4: 性能最適化",
content: `
<h3>🎮 設計最適化チャレンジ</h3>
<p>全制約条件を満たしながら、最高効率の設計を目指します。</p>
<div style="background: #ede9fe; border: 1px solid var(--accent-physics); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🏆 最適化目標</h4>
<ol>
<li><strong>安全性確保</strong> (50点): 線量率基準の確実な遵守</li>
<li><strong>経済性向上</strong> (30点): 総工事費の最小化</li>
<li><strong>設計効率</strong> (20点): 重量・空間・工期の効率化</li>
</ol>
</div>
<p>🎯 右下の最適化スコア100点達成を目指しましょう!</p>
`
},
5: {
title: "Step 5: 高度品質保証・第三者検証",
content: `
<h3>🔍 多段階品質保証システム</h3>
<p>実務では、段階的な品質確認により設計の信頼性を確保します。</p>
<div class="verification-levels" style="background: #f0fdf4; border: 1px solid var(--primary-green); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>📊 Level 1: 自己検証</h4>
<div class="check-items">
<label><input type="checkbox" class="verification-check"> 計算結果の再確認・単位系の一貫性</label><br>
<label><input type="checkbox" class="verification-check"> 入力パラメータの妥当性確認</label><br>
<label><input type="checkbox" class="verification-check"> 保守的仮定の適用確認</label>
</div>
</div>
<div class="internal-review" style="background: #fef7cd; border: 1px solid var(--warning-orange); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🤝 Level 2: 内部査読</h4>
<button class="simulate-review-btn" style="background: var(--warning-orange); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer;">
📋 内部査読シミュレーション
</button>
<div class="review-feedback" style="display: none; margin-top: 1rem;">
<h5>👨🔬 上級研究者からの査読コメント例:</h5>
<ul style="margin: 0.5rem 0;">
<li>"ビルドアップ係数の選択根拠を明確にしてください"</li>
<li>"散乱線の寄与を考慮していますか?"</li>
<li>"安全余裕の設定理由を記載してください"</li>
</ul>
</div>
</div>
<div class="external-audit-prep" style="background: #fef2f2; border: 1px solid var(--danger-red); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🏛️ Level 3: 外部監査準備</h4>
<ul>
<li><strong>計算手法の透明性:</strong> 使用した公式・定数の明示</li>
<li><strong>保守的仮定:</strong> 不確かさを考慮した安全側評価</li>
<li><strong>代替案検討:</strong> 複数の設計案の比較検討</li>
<li><strong>妥当性確認:</strong> ベンチマーク問題との比較</li>
</ul>
<p style="font-size: 0.9rem; color: #6b7280; margin-top: 0.5rem;">
📚 Level 3では、実際の規制審査対応方法を詳しく学習します
</p>
</div>
<div class="benchmark-test" style="background: #ede9fe; border: 1px solid var(--accent-physics); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🎯 ベンチマーク検証</h4>
<p><strong>NCRP Report 144 標準問題との比較:</strong></p>
<div class="benchmark-comparison">
<div>あなたの計算結果: <span id="user-result-benchmark">未計算</span></div>
<div>標準解: 2.4 μSv/h</div>
<div>許容差異: ±20%以内</div>
</div>
<button class="run-benchmark-btn" style="background: var(--accent-physics); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; margin-top: 0.5rem;">
🔄 ベンチマーク実行
</button>
</div>
`
},
6: {
title: "Step 6: Level 3準備完了・進路選択",
content: `
<h3>🎓 Level 3準備状況評価</h3>
<p>Level 2での学習を基に、Level 3での専門学習の準備状況を確認します。</p>
<div class="readiness-assessment" style="background: #ede9fe; border: 1px solid var(--accent-physics); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>📊 ${this.getCurrentScenarioReadiness().title}準備度</h4>
<div class="progress-bar" style="background: #e5e7eb; height: 8px; border-radius: 4px; margin: 0.5rem 0;">
<div class="progress" style="background: var(--accent-success); height: 100%; border-radius: 4px; width: ${this.getCurrentScenarioReadiness().percentage}%; transition: width 0.5s ease;"></div>
</div>
<div class="readiness-items" style="margin: 1rem 0;">
${this.getCurrentScenarioReadiness().items}
</div>
</div>
<div class="level3-pathways" style="background: #f0fdf4; border: 1px solid var(--primary-green); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>🚀 推奨Level 3学習パス</h4>
<div class="pathway-recommendation">
${this.getLevel3Recommendation()}
</div>
</div>
<div class="next-steps" style="background: #fef7cd; border: 1px solid var(--warning-orange); border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<h4>📋 次のステップ</h4>
<ul>
<li><strong>実務適用:</strong> 学習した設計手法の実践活用</li>
<li><strong>Level 3学習:</strong> 専門分野での高度技能習得</li>
<li><strong>継続学習:</strong> 最新技術・規制動向の把握</li>
<li><strong>知識共有:</strong> チーム・組織への技術展開</li>
</ul>
</div>
<div class="completion-certificate" style="text-align: center; background: linear-gradient(135deg, var(--accent-success), var(--primary-green)); color: white; border-radius: 12px; padding: 2rem; margin: 1rem 0;">
<h3>🏆 Level 2 実用設計 完了証明</h3>
<p style="margin: 1rem 0;">放射線遮蔽実用設計スキルを習得しました</p>
<div style="font-size: 0.9rem; opacity: 0.9;">
習得スキル: 実用設計・法規制理解・品質保証・Level 3準備
</div>
</div>
`
}
};
const currentContent = stepContents[this.currentStep];
if (currentContent) {
document.getElementById('step-indicator').textContent = currentContent.title;
document.getElementById('instruction-content').innerHTML = currentContent.content;
const optimizationGame = document.getElementById('optimization-game');
if (this.currentStep >= 4) {
optimizationGame.style.display = 'block';
}
document.getElementById('prev-step').disabled = this.currentStep <= 1;
document.getElementById('next-step').textContent =
this.currentStep >= this.totalSteps ? '🎉 Level完了' : '次のステップ →';
}
}
calculateFinalScore() {
if (!calculationResults || !calculationResults.isValid) return 0;
const scenario = scenarioDatabase[this.scenario];
let score = 0;
if (calculationResults.maxDoseRate <= scenario.requirements.doseLimit) {
const margin = (scenario.requirements.doseLimit - calculationResults.maxDoseRate) / scenario.requirements.doseLimit;
score += Math.min(50, 30 + margin * 20);
}
if (calculationResults.totalCost <= scenario.constraints.maxCost) {
const efficiency = 1 - (calculationResults.totalCost / scenario.constraints.maxCost);
score += Math.min(30, efficiency * 30);
}
if (calculationResults.totalWeight <= scenario.constraints.maxWeight) {
const lightness = 1 - (calculationResults.totalWeight / scenario.constraints.maxWeight);
score += Math.min(20, lightness * 20);
}
return Math.round(score);
}
getRegulationTitle() {
switch (this.scenario) {
case 'medical':
return '医療法施行規則の基礎理解';
case 'nuclear':
return '原子炉等規制法の基礎理解';
case 'research':
return '放射線障害防止法の基礎理解';
default:
return '適用法令の理解';
}
}
getRegulationContent() {
switch (this.scenario) {
case 'medical':
return `
<ul>
<li><strong>管理区域境界:</strong> 1.3mSv/3ヶ月以下</li>
<li><strong>一般病室:</strong> 1.3mSv/3ヶ月以下 (年間5.2mSv相当)</li>
<li><strong>安全管理体制:</strong> 責任者配置、指針策定、研修実施が義務</li>
<li><strong>線量管理:</strong> 装置毎の線量記録と保存</li>
<li><strong>Level 3で学習:</strong> 詳細な申請手続きと審査対応</li>
</ul>
`;
case 'nuclear':
return `
<ul>
<li><strong>線量限度:</strong> 管理区域境界で250μSv/3ヶ月以下</li>
<li><strong>安全審査:</strong> 設計・建設・運転の各段階で審査</li>
<li><strong>品質保証:</strong> QMSに基づく設計・施工管理</li>
<li><strong>長期安全:</strong> 廃棄物の長期保管を考慮した設計</li>
<li><strong>Level 3で学習:</strong> 安全解析書の作成と審査対応</li>
</ul>
`;
case 'research':
return `
<ul>
<li><strong>管理区域:</strong> 週40時間で1.3mSv/3ヶ月以下</li>
<li><strong>許可申請:</strong> 使用・貯蔵・廃棄の各段階で許可必要</li>
<li><strong>定期検査:</strong> 年1回の施設点検と記録保存</li>
<li><strong>中性子対策:</strong> 特殊な遮蔽材料と線量評価手法</li>
<li><strong>Level 3で学習:</strong> 許可申請書の詳細作成手法</li>
</ul>
`;
default:
return '<p>適用法令に応じた規制内容を理解します。</p>';
}
}
getBasicDesignSummary() {
const scenario = scenarioDatabase[this.scenario];
if (!scenario) return '設計概要を生成中...';
switch (this.scenario) {
case 'medical':
return '18MV線形加速器治療室の遮蔽設計、医療法施行規則に基づく管理区域境界線量率2.5μSv/h以下を目標';
case 'nuclear':
return '高レベル放射性廃棄物貯蔵施設の遮蔽設計、原子炉等規制法に基づく境界線量率250μSv/3ヶ月以下を目標';
case 'research':
return 'サイクロトロン加速器施設の中性子遮蔽設計、放射線障害防止法に基づく管理区域境界線量率5.0μSv/h以下を目標';
default:
return '放射線遮蔽設計の概要';
}
}
getCurrentScenarioReadiness() {
switch (this.scenario) {
case 'medical':
return {
title: '医療施設遮蔽(Level 3A)',
percentage: 75,
items: `
✅ 医療法施行規則の基本理解<br>
✅ 線形加速器遮蔽の基礎計算<br>
✅ 設計書作成の基礎体験<br>
📚 Level 3A: 詳細申請書類・複雑散乱計算・審査対応を学習予定
`
};
case 'nuclear':
return {
title: '原子力施設遮蔽(Level 3B)',
percentage: 70,
items: `
✅ 原子炉等規制法の基本理解<br>
✅ 複合遮蔽材料選択の基礎<br>
✅ 品質保証システムの理解<br>
📚 Level 3B: 安全解析書・長期安全性評価・規制審査を学習予定
`
};
case 'research':
return {
title: '研究施設遮蔽(Level 3C)',
percentage: 80,
items: `
✅ 放射線障害防止法の基本理解<br>
✅ 中性子遮蔽の基礎概念<br>
✅ 複合放射線場の理解<br>
📚 Level 3C: MCNP連携・詳細中性子計算・許可申請を学習予定
`
};
default:
return {
title: '放射線遮蔽設計',
percentage: 75,
items: '基本的な準備が完了しています。'
};
}
}
getLevel3Recommendation() {
switch (this.scenario) {
case 'medical':
return `
<div style="padding: 0.5rem 0;">
<strong>🏥 Level 3A: 医療施設遮蔽設計実践</strong><br>
<span style="color: #6b7280; font-size: 0.9rem;">
• 詳細な申請書類作成(使用許可・変更届出)<br>
• 複雑な散乱計算(一次・二次散乱の詳細評価)<br>
• 規制審査対応(指摘事項・追加資料作成)<br>
• 実際の施設設計プロジェクト体験
</span>
</div>
`;
case 'nuclear':
return `
<div style="padding: 0.5rem 0;">
<strong>⚛️ Level 3B: 原子力施設遮蔽設計実践</strong><br>
<span style="color: #6b7280; font-size: 0.9rem;">
• 安全解析書の遮蔽設計章作成<br>
• 長期安全性評価(腐食・劣化考慮)<br>
• QMSに基づく設計管理<br>
• 廃棄物処理施設設計プロジェクト
</span>
</div>
`;
case 'research':
return `
<div style="padding: 0.5rem 0;">
<strong>🔬 Level 3C: 研究施設遮蔽設計実践</strong><br>
<span style="color: #6b7280; font-size: 0.9rem;">
• MCNP連携による高精度計算<br>
• 中性子遮蔽の詳細設計手法<br>
• 使用許可申請書の完全作成<br>
• 加速器施設設計プロジェクト
</span>
</div>
`;
default:
return 'あなたの学習目標に応じたLevel 3コースを選択してください。';
}
}
}
class FacilityVisualizer {
constructor(container) {
this.container = container;
this.facilityObjects = {};
this.initializeScene();
this.animate();
}
initializeScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f4f8);
camera = new THREE.PerspectiveCamera(60, this.container.clientWidth / this.container.clientHeight, 0.1, 1000);
camera.position.set(15, 10, 15);
camera.lookAt(0, 0, 0);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(this.container.clientWidth, this.container.clientHeight);
renderer.shadowMap.enabled = true;
this.container.appendChild(renderer.domElement);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(10, 20, 10);
directionalLight.castShadow = true;
scene.add(directionalLight);
this.createFloorAndGrid();
this.setupControls();
}
createFloorAndGrid() {
const floorGeometry = new THREE.PlaneGeometry(50, 50);
const floorMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true, opacity: 0.8 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
const gridHelper = new THREE.GridHelper(50, 50, 0xcccccc, 0xeeeeee);
scene.add(gridHelper);
}
setupControls() {
let isMouseDown = false;
let mouseX = 0, mouseY = 0;
let targetRotationY = 0.8;
let currentDistance = 25;
this.container.addEventListener('mousedown', (event) => {
isMouseDown = true;
mouseX = event.clientX;
mouseY = event.clientY;
});
this.container.addEventListener('mousemove', (event) => {
if (!isMouseDown) return;
const deltaX = event.clientX - mouseX;
targetRotationY += deltaX * 0.01;
mouseX = event.clientX;
mouseY = event.clientY;
});
this.container.addEventListener('mouseup', () => {
isMouseDown = false;
});
this.container.addEventListener('wheel', (event) => {
event.preventDefault();
currentDistance += event.deltaY * 0.01;
currentDistance = Math.max(10, Math.min(60, currentDistance));
});
const updateCamera = () => {
camera.position.x = currentDistance * Math.sin(targetRotationY);
camera.position.y = currentDistance * 0.5;
camera.position.z = currentDistance * Math.cos(targetRotationY);
camera.lookAt(0, 2, 0);
requestAnimationFrame(updateCamera);
};
updateCamera();
}
createFacilityStructure(scenario, parameters) {
this.clearFacility();
const roomLength = parameters.roomLength || 8;
const roomWidth = parameters.roomWidth || 6;
const roomHeight = parameters.roomHeight || 3;
const wallThickness = (parameters.wallThickness || 150) / 100;
const wallMaterial = materialDatabase[parameters.wallMaterial] || materialDatabase.concrete_heavy;
this.createWalls(roomLength, roomWidth, roomHeight, wallThickness, wallMaterial);
this.createRadiationSource(scenario);
this.createDetectors(roomLength, roomWidth, wallThickness);
}
createWalls(length, width, height, thickness, materialData) {
const wallMaterial = new THREE.MeshPhongMaterial({
color: materialData.color,
transparent: true,
opacity: 0.8
});
const walls = [
{ geometry: new THREE.BoxGeometry(length + 2*thickness, height, thickness), position: [0, height/2, -width/2 - thickness/2] },
{ geometry: new THREE.BoxGeometry(length + 2*thickness, height, thickness), position: [0, height/2, width/2 + thickness/2] },
{ geometry: new THREE.BoxGeometry(thickness, height, width), position: [-length/2 - thickness/2, height/2, 0] },
{ geometry: new THREE.BoxGeometry(thickness, height, width), position: [length/2 + thickness/2, height/2, 0] },
{ geometry: new THREE.BoxGeometry(length + 2*thickness, thickness, width + 2*thickness), position: [0, height + thickness/2, 0] }
];
walls.forEach((wallData, index) => {
const wall = new THREE.Mesh(wallData.geometry, wallMaterial);
wall.position.set(...wallData.position);
wall.castShadow = true;
wall.receiveShadow = true;
this.facilityObjects[`wall_${index}`] = wall;
scene.add(wall);
});
}
createRadiationSource(scenario) {
let geometry, material;
switch (scenario) {
case 'medical':
geometry = new THREE.BoxGeometry(3, 2, 1.5);
material = new THREE.MeshPhongMaterial({ color: 0x4a90e2, emissive: 0x001133 });
break;
case 'nuclear':
geometry = new THREE.CylinderGeometry(1.0, 1.0, 3);
material = new THREE.MeshPhongMaterial({ color: 0xff6b35, emissive: 0x331100 });
break;
case 'research':
geometry = new THREE.CylinderGeometry(2.5, 2.5, 1);
material = new THREE.MeshPhongMaterial({ color: 0x50c878, emissive: 0x003311 });
break;
default:
geometry = new THREE.SphereGeometry(0.5);
material = new THREE.MeshPhongMaterial({ color: 0xff4444, emissive: 0x220000 });
}
const source = new THREE.Mesh(geometry, material);
source.position.set(0, 1.5, 0);
source.castShadow = true;
this.facilityObjects.radiation_source = source;
scene.add(source);
}
createDetectors(roomLength, roomWidth, wallThickness) {
const detectorGeometry = new THREE.SphereGeometry(0.12);
const detectorMaterial = new THREE.MeshPhongMaterial({
color: 0x44ff44,
emissive: 0x002200,
transparent: true,
opacity: 0.8
});
const detectionPoints = [
[roomLength/2 + wallThickness + 0.3, 1, 0],
[-roomLength/2 - wallThickness - 0.3, 1, 0],
[0, 1, roomWidth/2 + wallThickness + 0.3],
[0, 1, -roomWidth/2 - wallThickness - 0.3]
];
detectionPoints.forEach((pos, index) => {
const detector = new THREE.Mesh(detectorGeometry, detectorMaterial.clone());
detector.position.set(...pos);
this.facilityObjects[`detector_${index}`] = detector;
scene.add(detector);
});
}
clearFacility() {
Object.values(this.facilityObjects).forEach(obj => {
scene.remove(obj);
});
this.facilityObjects = {};
}
animate() {
const render = () => {
requestAnimationFrame(render);
if (this.facilityObjects.radiation_source) {
this.facilityObjects.radiation_source.rotation.y += 0.01;
}
renderer.render(scene, camera);
};
render();
}
}
class ShieldingCalculator {
calculateShielding(scenario, parameters) {
const scenarioData = scenarioDatabase[scenario];
const wallMaterial = materialDatabase[parameters.wallMaterial];
const wallThickness = parameters.wallThickness / 100;
const distance = 1.5;
const sourceActivity = scenarioData.source.nominalRate || scenarioData.source.activity || 1000;
const geometricAttenuation = 1 / (distance * distance);
const mu = wallMaterial.muMass * wallMaterial.density * 100;
const shieldingAttenuation = Math.exp(-mu * wallThickness);
const buildup = 1 + (mu * wallThickness) * 0.5;
const baseDoseRate = sourceActivity * 0.001;
const maxDoseRate = baseDoseRate * geometricAttenuation * shieldingAttenuation * buildup;
const roomLength = parameters.roomLength || 8;
const roomWidth = parameters.roomWidth || 6;
const roomHeight = parameters.roomHeight || 3;
const volume = this.calculateWallVolume(roomLength, roomWidth, roomHeight, wallThickness);
const totalCost = volume * wallMaterial.cost / 10000;
const totalWeight = volume * wallMaterial.density;
const safetyMargin = Math.max(0, ((scenarioData.requirements.doseLimit - maxDoseRate) / scenarioData.requirements.doseLimit) * 100);
return {
maxDoseRate: Math.max(maxDoseRate, 0.01),
avgDoseRate: maxDoseRate * 0.7,
totalCost: totalCost,
totalWeight: totalWeight,
safetyMargin: safetyMargin,
isValid: maxDoseRate <= scenarioData.requirements.doseLimit
};
}
calculateWallVolume(length, width, height, wallThickness) {
const outerLength = length + 2 * wallThickness;
const outerWidth = width + 2 * wallThickness;
const outerHeight = height + wallThickness;
const outerVolume = outerLength * outerWidth * outerHeight;
const innerVolume = length * width * height;
return outerVolume - innerVolume;
}
}
document.addEventListener('DOMContentLoaded', () => {
progressManager = new ProgressManager();
const calculator = new ShieldingCalculator();
const scenarioCards = document.querySelectorAll('.scenario-card');
scenarioCards.forEach(card => {
card.addEventListener('click', () => {
selectScenario(card.dataset.scenario);
});
});
const parameterInputs = document.querySelectorAll('.parameter-input');
parameterInputs.forEach(input => {
input.addEventListener('change', updateDesignParameters);
});
const sliders = document.querySelectorAll('.slider');
sliders.forEach(slider => {
slider.addEventListener('input', (e) => {
updateSliderDisplay(slider);
updateDesignParameters();
});
});
document.getElementById('next-step').addEventListener('click', () => {
progressManager.nextStep();
});
document.getElementById('prev-step').addEventListener('click', () => {
if (progressManager.currentStep > 1) {
progressManager.currentStep--;
progressManager.updateStepContent();
progressManager.updateProgress();
}
});
document.getElementById('calculate-design').addEventListener('click', async () => {
if (!currentScenario) return;
const calculateBtn = document.getElementById('calculate-design');
const resultsGrid = document.getElementById('results-grid');
calculateBtn.disabled = true;
calculateBtn.textContent = '🔄 高精度計算中...';
try {
await new Promise(resolve => setTimeout(resolve, 1000));
calculationResults = calculator.calculateShielding(currentScenario, designParameters);
document.getElementById('max-dose-rate').textContent = calculationResults.maxDoseRate.toFixed(2) + ' μSv/h';
document.getElementById('total-cost').textContent = calculationResults.totalCost.toFixed(0) + ' 万円';
document.getElementById('total-weight').textContent = calculationResults.totalWeight.toFixed(0) + ' トン';
document.getElementById('safety-margin').textContent = calculationResults.safetyMargin.toFixed(1) + ' %';
updateResultStatus('dose-status', calculationResults.isValid);
updateResultStatus('cost-status', calculationResults.totalCost <= scenarioDatabase[currentScenario].constraints.maxCost);
updateResultStatus('weight-status', calculationResults.totalWeight <= scenarioDatabase[currentScenario].constraints.maxWeight);
updateResultStatus('margin-status', calculationResults.safetyMargin >= 20);
if (progressManager.currentStep >= 4) {
updateOptimizationScore();
}
// Update document template if in step 3 or later
if (progressManager.currentStep >= 3) {
updateDocumentTemplate();
}
} catch (error) {
alert('計算中にエラーが発生しました。');
} finally {
calculateBtn.disabled = false;
calculateBtn.textContent = '⚡ 高精度遮蔽性能計算';
resultsGrid.style.display = 'grid';
}
});
document.getElementById('hint-btn').addEventListener('click', () => {
if (!currentScenario) return;
const scenarioData = scenarioDatabase[currentScenario];
alert(`💡 ${scenarioData.name}のヒント:\n\n• 線量限度: ${scenarioData.requirements.doseLimit} μSv/h\n• 法的根拠: ${scenarioData.requirements.regulation}\n• 設計のポイント: 安全性を最優先に考慮しましょう!`);
});
document.getElementById('reset-design').addEventListener('click', resetToDefaultDesign);
document.getElementById('save-design').addEventListener('click', () => {
alert('💾 設計が保存されました!');
});
// New interactive features
document.addEventListener('click', (e) => {
if (e.target.classList.contains('simulate-review-btn')) {
const feedbackDiv = e.target.parentElement.querySelector('.review-feedback');
if (feedbackDiv) {
feedbackDiv.style.display = feedbackDiv.style.display === 'none' ? 'block' : 'none';
}
}
if (e.target.classList.contains('run-benchmark-btn')) {
runBenchmarkTest();
}
});
function selectScenario(scenarioKey) {
currentScenario = scenarioKey;
const scenarioData = scenarioDatabase[scenarioKey];
document.getElementById('scenario-selector').style.display = 'none';
progressManager.setScenario(scenarioKey);
const defaultDesign = scenarioData.defaultDesign;
designParameters = {
roomLength: defaultDesign.roomDimensions[0],
roomWidth: defaultDesign.roomDimensions[1],
roomHeight: defaultDesign.roomDimensions[2],
wallThickness: defaultDesign.wallThickness,
doorThickness: defaultDesign.doorThickness,
mazeLength: defaultDesign.mazeLength,
wallMaterial: scenarioData.materials[0] || 'concrete_heavy',
doorMaterial: 'lead'
};
updateUIFromParameters();
initializeVisualization();
updateConstraintDisplay(scenarioData);
}
function initializeVisualization() {
const container = document.getElementById('threejs-container');
visualizer = new FacilityVisualizer(container);
visualizer.createFacilityStructure(currentScenario, designParameters);
}
function updateDesignParameters() {
if (!currentScenario) return;
designParameters.roomLength = parseFloat(document.getElementById('room-length').value) || 8;
designParameters.roomWidth = parseFloat(document.getElementById('room-width').value) || 6;
designParameters.roomHeight = parseFloat(document.getElementById('room-height').value) || 3;
designParameters.wallThickness = parseFloat(document.getElementById('main-wall-thickness').value) || 150;
designParameters.doorThickness = parseFloat(document.getElementById('door-thickness').value) || 15;
designParameters.mazeLength = parseFloat(document.getElementById('maze-length').value) || 4;
designParameters.wallMaterial = document.getElementById('wall-material').value || 'concrete_heavy';
designParameters.doorMaterial = document.getElementById('door-material').value || 'lead';
if (visualizer && currentScenario) {
visualizer.createFacilityStructure(currentScenario, designParameters);
}
}
function updateSliderDisplay(slider) {
const valueDisplay = document.getElementById(slider.id.replace('main-wall-thickness', 'main-wall-value').replace('door-thickness', 'door-value'));
if (valueDisplay) {
valueDisplay.textContent = slider.value + ' cm';
}
}
function updateResultStatus(elementId, isGood) {
const element = document.getElementById(elementId);
if (isGood) {
element.textContent = '✓ 適合';
element.className = 'result-status ok';
} else {
element.textContent = '⚠ 要改善';
element.className = 'result-status warning';
}
}
function updateOptimizationScore() {
if (!calculationResults || !calculationResults.isValid) {
document.getElementById('optimization-score').textContent = '0';
return;
}
const score = progressManager.calculateFinalScore();
document.getElementById('optimization-score').textContent = score;
}
function updateConstraintDisplay(scenarioData) {
document.getElementById('dose-constraint').textContent = scenarioData.requirements.doseLimit + ' μSv/h';
document.getElementById('cost-constraint').textContent = scenarioData.constraints.maxCost + ' 万円';
document.getElementById('weight-constraint').textContent = scenarioData.constraints.maxWeight + ' トン';
document.getElementById('schedule-constraint').textContent = scenarioData.constraints.maxSchedule + ' ヶ月';
}
function updateUIFromParameters() {
document.getElementById('room-length').value = designParameters.roomLength;
document.getElementById('room-width').value = designParameters.roomWidth;
document.getElementById('room-height').value = designParameters.roomHeight;
document.getElementById('main-wall-thickness').value = designParameters.wallThickness;
document.getElementById('door-thickness').value = designParameters.doorThickness;
document.getElementById('maze-length').value = designParameters.mazeLength;
document.getElementById('wall-material').value = designParameters.wallMaterial;
document.getElementById('door-material').value = designParameters.doorMaterial;
document.getElementById('main-wall-value').textContent = designParameters.wallThickness + ' cm';
document.getElementById('door-value').textContent = designParameters.doorThickness + ' cm';
}
function resetToDefaultDesign() {
if (!currentScenario) return;
const defaultDesign = scenarioDatabase[currentScenario].defaultDesign;
designParameters = {
roomLength: defaultDesign.roomDimensions[0],
roomWidth: defaultDesign.roomDimensions[1],
roomHeight: defaultDesign.roomDimensions[2],
wallThickness: defaultDesign.wallThickness,
doorThickness: defaultDesign.doorThickness,
mazeLength: defaultDesign.mazeLength,
wallMaterial: scenarioDatabase[currentScenario].materials[0],
doorMaterial: 'lead'
};
updateUIFromParameters();
if (visualizer) {
visualizer.createFacilityStructure(currentScenario, designParameters);
}
alert('🔄 設計パラメータをデフォルト値にリセットしました。');
}
function updateDocumentTemplate() {
if (!calculationResults || !currentScenario) return;
const docCalcResults = document.getElementById('doc-calc-results');
const docCompliance = document.getElementById('doc-compliance');
if (docCalcResults) {
docCalcResults.textContent = `最大線量率 ${calculationResults.maxDoseRate.toFixed(2)} μSv/h, 遮蔽厚さ ${designParameters.wallThickness || 150} cm`;
}
if (docCompliance) {
const scenario = scenarioDatabase[currentScenario];
const isCompliant = calculationResults.maxDoseRate <= scenario.requirements.doseLimit;
docCompliance.textContent = isCompliant ?
`✓ ${scenario.requirements.regulation}に適合` :
`⚠ 基準値${scenario.requirements.doseLimit}μSv/hを超過、要改善`;
docCompliance.style.color = isCompliant ? 'var(--primary-green)' : 'var(--danger-red)';
}
}
function runBenchmarkTest() {
if (!calculationResults) {
alert('⚠ まず遮蔽計算を実行してください');
return;
}
const userResult = calculationResults.maxDoseRate;
const standardResult = 2.4;
const difference = ((userResult - standardResult) / standardResult * 100);
const userResultElement = document.getElementById('user-result-benchmark');
if (userResultElement) {
userResultElement.textContent = userResult.toFixed(2) + ' μSv/h';
}
const isAcceptable = Math.abs(difference) <= 20;
const message = isAcceptable ?
`✅ ベンチマーク検証合格!\n差異: ${difference.toFixed(1)}% (許容範囲内)` :
`⚠ ベンチマーク検証要確認\n差異: ${difference.toFixed(1)}% (許容範囲±20%を超過)`;
alert(message);
}
});
</script>
</body>
</html>