260 lines
14 KiB
HTML
260 lines
14 KiB
HTML
<!doctype html>
|
||
<!--
|
||
============================================================================
|
||
프로젝트: 터널 단면 3D 파라메트릭 모델러 (Tunnel Modeler 3D)
|
||
개발자: m12205.tdc.hanmacgroup@gmail.com
|
||
저작권: Copyright (c) 2026 m12205.tdc.hanmacgroup@gmail.com. All rights reserved.
|
||
설명: DWG/DXF 표준단면도 기반 지능형 형상 인식 및 3D 솔리드 모델 자동 생성 웹 애플리케이션
|
||
============================================================================
|
||
-->
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>터널 단면 모델러 — DWG/DXF → 3D 모델</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
||
<script src="dxf-parser.js"></script>
|
||
<script src="earcut.min.js"></script>
|
||
<script src="rhino3dm.min.js"></script>
|
||
<script defer src="app.js"></script>
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<div class="brand-mark">T</div>
|
||
<div>
|
||
<strong>터널 단면 모델러</strong>
|
||
<small>DWG 표준단면에서 10m 3D 모델 생성</small>
|
||
</div>
|
||
<div class="top-status">
|
||
<span class="status-dot" id="statusDot"></span>
|
||
<span id="engineStatus">엔진 확인 중</span>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="layout">
|
||
<aside class="sidebar">
|
||
<section class="panel import-panel">
|
||
<div class="section-kicker">01 / 도면 입력</div>
|
||
<h1>표준단면 불러오기</h1>
|
||
<label id="dropzone" class="dropzone">
|
||
<input id="fileInput" type="file" accept=".dwg,.dxf">
|
||
<span class="upload-icon">↑</span>
|
||
<b>DWG 또는 DXF 파일 선택</b>
|
||
<small>파일은 이 PC 안에서 처리합니다</small>
|
||
</label>
|
||
<div id="fileMeta" class="file-meta hidden"></div>
|
||
<div class="sample-actions">
|
||
<button id="loadSample" class="quiet-button">예제 DXF 불러오기</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel settings-panel">
|
||
<div class="section-kicker">02 / 모델 길이</div>
|
||
<label class="length-label" for="lengthInput">터널 모델 길이 <span>m</span></label>
|
||
<div class="length-input">
|
||
<input id="lengthInput" type="number" min="1" max="500" step="1" value="10">
|
||
<span>m</span>
|
||
</div>
|
||
<div class="length-presets">
|
||
<button type="button" class="preset-len-btn active" data-len="10">10m</button>
|
||
<button type="button" class="preset-len-btn" data-len="20">20m</button>
|
||
<button type="button" class="preset-len-btn" data-len="50">50m</button>
|
||
<button type="button" class="preset-len-btn" data-len="100">100m</button>
|
||
</div>
|
||
<p class="help">요청하신 기본값 10m로 설정되어 있습니다.</p>
|
||
<button id="buildButton" class="primary-button" disabled>단면 분석 및 3D 생성</button>
|
||
</section>
|
||
|
||
<section class="panel detection-panel">
|
||
<div class="section-kicker">03 / 구조물 인식</div>
|
||
<div class="detection-header">
|
||
<h2>인식 결과</h2>
|
||
<button type="button" id="toggleParamsBtn" class="quiet-button hidden" style="padding:0;">상세 제원 편집 ▾</button>
|
||
</div>
|
||
<div id="detectionList" class="detection-list">
|
||
<div class="empty-state">도면을 불러오면 자동 분석합니다.</div>
|
||
</div>
|
||
|
||
<!-- 추출된 파라미터 미세 조정 패널 (접기/펼치기 지원) -->
|
||
<div id="paramEditorPanel" class="param-editor hidden">
|
||
<div class="param-grid">
|
||
<label><span>천단반경 R1(mm)</span><input type="number" id="p_r1" value="5500"></label>
|
||
<label><span>중심각 θ1(°)</span><input type="number" id="p_theta1" value="120"></label>
|
||
<label><span>내공좌측 R2(mm)</span><input type="number" id="p_r2" value="7500"></label>
|
||
<label><span>내공우측 R3(mm)</span><input type="number" id="p_r3" value="7500"></label>
|
||
<label><span>외면좌측 R4(mm)</span><input type="number" id="p_r4" value="9200"></label>
|
||
<label><span>외면우측 R5(mm)</span><input type="number" id="p_r5" value="9200"></label>
|
||
<label><span>중심높이 H1(mm)</span><input type="number" id="p_h1" value="500"></label>
|
||
<label><span>이격거리 E(mm)</span><input type="number" id="p_offset" value="3500"></label>
|
||
<label><span>라이닝 두께(mm)</span><input type="number" id="p_lining_t" value="300"></label>
|
||
<label><span>숏크리트 두께(mm)</span><input type="number" id="p_shotcrete_t" value="150"></label>
|
||
<label><span>편경사(%)</span><input type="number" id="p_se" step="0.1" value="-2.0"></label>
|
||
<label><span>포장 표층 t1(mm)</span><input type="number" id="p_pave_t1" value="20"></label>
|
||
<label><span>포장 콘크리트 t2</span><input type="number" id="p_pave_t2" value="260"></label>
|
||
<label><span>포장 필터층 t3</span><input type="number" id="p_pave_t3" value="150"></label>
|
||
</div>
|
||
<button type="button" id="applyParamsBtn" class="secondary-button">변경 제원으로 3D 갱신</button>
|
||
</div>
|
||
|
||
<p class="note">
|
||
레이어 이름에 의존하지 않고 아치의 동심 관계, 폐합 윤곽, 객체의 위치와 치수로 단면 요소를 찾습니다.
|
||
라이닝과 하부 배수콘크리트는 경계면 없는 단일 일체형 솔리드로 생성되며, 공동구는 내부를 비우고 받침턱 위에 길이 방향 뚜껑을 설치합니다.
|
||
</p>
|
||
</section>
|
||
|
||
<div class="sidebar-foot">공동구 내부는 비우고 도면의 받침턱 위에 뚜껑을 얹습니다.</div>
|
||
</aside>
|
||
|
||
<section class="workspace">
|
||
<div class="view-tabs">
|
||
<button class="view-tab active" data-view="3d">3D 모델</button>
|
||
<button class="view-tab" data-view="2d">2D 단면</button>
|
||
<div class="view-spacer"></div>
|
||
|
||
<div class="viewer-toolbar">
|
||
<div id="opacitySliderBox" class="opacity-slider-box" title="3D 모델 객체 투명도 조절: 100% 완전 솔리드 ~ 10% 내부 투시">
|
||
<span class="slider-label">객체 투명도</span>
|
||
<input type="range" id="opacitySlider" min="10" max="100" value="100" step="5" class="slider-range">
|
||
<span id="opacityVal" class="slider-val">100%</span>
|
||
</div>
|
||
<button type="button" id="btnViewInside" class="tool-button" title="터널 내부 주행 시점">👁️ 내부 시점</button>
|
||
<button type="button" id="btnViewBird" class="tool-button" title="전체 조감도 시점">조감도 시점</button>
|
||
<div class="theme-switch">
|
||
<button type="button" id="btnThemeDark" class="theme-btn active">🌙 다크</button>
|
||
<button type="button" id="btnThemeLight" class="theme-btn">☀️ 라이트</button>
|
||
</div>
|
||
<button id="resetView" class="icon-button" title="화면 초기화">⟲</button>
|
||
<button id="exportObj" class="export-button" disabled>OBJ 내보내기</button>
|
||
<button id="export3dm" class="export-button" disabled title="Rhino 3D 모델 파일(.3dm)로 내보내기">3DM 내보내기</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="viewport" class="viewport">
|
||
<!-- 3D Plotly 뷰어 -->
|
||
<div id="placement_3d_plot" class="plot-3d"></div>
|
||
|
||
<!-- 2D 단면 캔버스 -->
|
||
<canvas id="modelCanvas" class="canvas-2d hidden"></canvas>
|
||
|
||
<!-- 초기 안내 화면 -->
|
||
<div id="welcome" class="welcome">
|
||
<div class="welcome-glyph">◈</div>
|
||
<h2>단면 도면을 불러오세요</h2>
|
||
<p>인식된 표준단면을 길이 10m로 생성해 3D로 확인합니다.</p>
|
||
</div>
|
||
|
||
<!-- 캔버스 내부 우측 상단 플로팅 오버레이: 전구 표시 세로 리스트 -->
|
||
<div id="placement_3d_layer_panel" class="layer-panel">
|
||
<!-- [전체 마스터 토글 행] -->
|
||
<div class="layer-master-row">
|
||
<div onclick="toggleAll3DObjects()" title="전체 모델 한번에 켜기/끄기" class="layer-master-left">
|
||
<span id="bulb_icon_all" class="bulb-icon master-bulb">💡</span>
|
||
<span id="label_layer_all" class="master-label">전체 모델</span>
|
||
</div>
|
||
<div class="layer-master-right">
|
||
<span onclick="toggleAll3DObjects()" id="badge_status_all" class="status-badge is-on">ON</span>
|
||
<button onclick="toggle3DLayerPanelMinimize()" id="btn_toggle_layer_panel" title="목록 접기/펼치기" class="minimize-btn">−</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 모델별 세로 리스트 영역 (접기 대상) -->
|
||
<div id="placement_3d_layer_items_wrapper" class="layer-items-wrapper">
|
||
<div class="layer-divider"></div>
|
||
|
||
<!-- 1. 숏크리트 -->
|
||
<div onclick="toggle3DObject('shotcrete')" id="item_layer_shotcrete" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_shotcrete" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#475569; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_shotcrete" class="layer-label">숏크리트</span>
|
||
</div>
|
||
<span id="badge_status_shotcrete" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 2. 라이닝·공동구 -->
|
||
<div onclick="toggle3DObject('lining')" id="item_layer_lining" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_lining" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#8da0b0; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_lining" class="layer-label">라이닝·공동구</span>
|
||
</div>
|
||
<span id="badge_status_lining" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 3. 도로포장 -->
|
||
<div onclick="toggle3DObject('pavement')" id="item_layer_pavement" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_pavement" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#1e293b; border-color:#475569;"></span>
|
||
<span id="label_layer_pavement" class="layer-label">도로포장</span>
|
||
</div>
|
||
<span id="badge_status_pavement" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 4. 공동구 뚜껑 -->
|
||
<div onclick="toggle3DObject('lids')" id="item_layer_lids" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_lids" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#cbd5e1; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_lids" class="layer-label">공동구 뚜껑</span>
|
||
</div>
|
||
<span id="badge_status_lids" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 5. 맹암거 -->
|
||
<div onclick="toggle3DObject('blindDitch')" id="item_layer_blindDitch" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_blindDitch" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#d97706; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_blindDitch" class="layer-label">맹암거</span>
|
||
</div>
|
||
<span id="badge_status_blindDitch" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 6. 종배수관 -->
|
||
<div onclick="toggle3DObject('drainPipe')" id="item_layer_drainPipe" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_drainPipe" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#ea580c; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_drainPipe" class="layer-label">종배수관</span>
|
||
</div>
|
||
<span id="badge_status_drainPipe" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 7. 차선 -->
|
||
<div onclick="toggle3DObject('lanes')" id="item_layer_lanes" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_lanes" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#ffffff; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_lanes" class="layer-label">차선</span>
|
||
</div>
|
||
<span id="badge_status_lanes" class="status-badge is-on">ON</span>
|
||
</div>
|
||
|
||
<!-- 8. 단면 테두리 -->
|
||
<div onclick="toggle3DObject('rims')" id="item_layer_rims" class="layer-item">
|
||
<div class="layer-item-left">
|
||
<span id="bulb_icon_rims" class="bulb-icon">💡</span>
|
||
<span class="color-dot" style="background:#10b981; border-color:rgba(255,255,255,0.4);"></span>
|
||
<span id="label_layer_rims" class="layer-label">단면 테두리</span>
|
||
</div>
|
||
<span id="badge_status_rims" class="status-badge is-on">ON</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="hint" class="view-hint hidden">왼쪽 드래그: 회전 · 오른쪽 드래그: 이동 · 휠: 확대/축소</div>
|
||
<div class="canvas-credit" id="canvasCredit">Developed by m12205.tdc.hanmacgroup@gmail.com · © 2026</div>
|
||
</div>
|
||
|
||
<div class="bottom-bar">
|
||
<span id="modelSummary">도면 대기 중</span>
|
||
<span class="developer-credit">Developed by m12205.tdc.hanmacgroup@gmail.com · © 2026</span>
|
||
<span id="cursorInfo">단위: 도면 mm / 3D 모델 m</span>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</body>
|
||
</html>
|