38 lines
15 KiB
HTML
38 lines
15 KiB
HTML
<!doctype html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="theme-color" content="#111c29">
|
||
<title>R2C · DXF 도면 3D 뷰어</title>
|
||
<script>
|
||
if (location.protocol === 'file:') {
|
||
location.replace('http://localhost:__SERVER_PORT__/' + location.search + location.hash);
|
||
}
|
||
</script>
|
||
<style>
|
||
:root{--bg:#101b28;--panel:#172635;--border:#2b4052;--fg:#edf3f6;--muted:#9caebb;--mint:#8de1cf;--gold:#edc28a}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif}button,a,select{font:inherit}button,select{cursor:pointer}button:focus-visible,a:focus-visible,select:focus-visible{outline:2px solid var(--mint);outline-offset:3px}button{color:inherit}a{color:var(--mint)}.wrap{max-width:1600px;margin:auto;padding:0 28px 30px}header{display:flex;justify-content:space-between;align-items:center;gap:16px;height:82px;border-bottom:1px solid var(--border)}.brand{display:flex;align-items:center;gap:12px}.symbol{width:39px;height:39px;border:1px solid #428379;color:var(--mint);border-radius:11px;display:grid;place-items:center;font-weight:800}.brand strong{display:block;font-size:15px}.brand small{color:var(--muted);font-size:11px}.actions{display:flex;gap:8px;align-items:center}.badge{font-size:10px;color:var(--mint);padding:5px 10px;border:1px solid #345f5b;border-radius:20px}.link,.btn{border:1px solid var(--border);background:var(--panel);padding:8px 12px;border-radius:7px;text-decoration:none;font-size:12px;color:var(--fg);white-space:nowrap}.btn:hover,.link:hover{background:#24394a}.btn.active{background:#204a49;border-color:#4a8b7f;color:#bcf4e6}h1{font-size:25px;font-weight:650;letter-spacing:-.04em;margin:4px 0 6px}.intro{display:flex;justify-content:space-between;align-items:end;margin:25px 0 19px;gap:20px}.eyebrow{color:var(--mint);font-size:10px;font-weight:700;letter-spacing:.15em}.subtitle{color:var(--muted);font-size:12px;margin:0}.intro-note{text-align:right;color:var(--muted);font-size:11px}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:18px}.main{min-width:0}.toolbar{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:10px}.seg{display:flex;gap:5px;flex-wrap:wrap}.seg .btn{padding:6px 10px;font-size:11px}.stage{position:relative;isolation:isolate;height:clamp(420px,61vh,760px);border:1px solid #465662;border-radius:12px;overflow:hidden;background:radial-gradient(ellipse at 45% 38%,#f5f7f6,#dfe7e9)}#scene{display:block;position:absolute;inset:0;width:100%;height:100%;touch-action:none;cursor:grab}#scene:active{cursor:grabbing}.stage-label{position:absolute;left:17px;top:15px;pointer-events:none;font-size:10px;color:#526673;letter-spacing:.12em;z-index:2}.stage-label strong{display:block;font-size:12px;color:#283e49;letter-spacing:.03em;margin-top:3px}.stage-bottom{position:absolute;left:17px;bottom:14px;color:#526673;font-size:10px;pointer-events:none}.stage-status{position:absolute;right:14px;top:14px;border:1px solid #b7c9cc;background:#f9fcfaca;padding:5px 9px;border-radius:7px;color:#35635f;font-size:10px;pointer-events:none}.zoom{position:absolute;right:13px;bottom:13px;display:flex;gap:5px}.zoom button{background:#f5f8f8e8;border:1px solid #b5c6cd;border-radius:6px;color:#334b59;width:31px;height:30px;font-weight:650}.viewbar{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:10px}.help{font-size:10px;color:var(--muted)}.error{position:absolute;inset:55px 15px auto;z-index:5;padding:12px;color:#7b2f22;background:#ffefdf;border-radius:7px;font-size:12px}.error[hidden]{display:none}.cards{display:flex;flex-direction:column;gap:12px}.card{padding:16px;border:1px solid var(--border);border-radius:10px;background:var(--panel)}.card h2{font-size:12px;margin:0 0 13px;display:flex;justify-content:space-between;align-items:center}.small-tag{font-size:9px;font-weight:400;letter-spacing:.05em;color:var(--mint)}.dims{margin:0;display:grid;grid-template-columns:1fr auto;gap:8px;font-size:12px}.dims dt{color:var(--muted)}.dims dd{margin:0;font-variant-numeric:tabular-nums}.dims dd.main-value{color:var(--mint);font-weight:650}.rule{border:0;border-top:1px solid var(--border);margin:13px 0}.micro{font-size:11px;color:var(--muted);margin:0;line-height:1.7}.legend{display:flex;gap:8px;align-items:center;font-size:11px;margin-bottom:7px}.swatch{width:10px;height:10px;border-radius:3px;background:#bcc8c9}.swatch.bottom{background:#81979d}.swatch.inner{background:#6b8189}.card .btn{width:100%;margin-top:12px;text-align:center}.limits{color:var(--muted);font-size:10px;line-height:1.7;margin-top:10px}.drawing-card{border:1px solid var(--border);border-radius:10px;margin-top:22px;background:var(--panel);overflow:hidden}.drawing-card>summary{cursor:pointer;list-style:none;display:flex;justify-content:space-between;gap:12px;padding:16px;font-size:13px;font-weight:600}.drawing-card>summary::after{content:'+';color:var(--mint)}.drawing-card[open]>summary::after{content:'−'}.drawing-controls{display:flex;gap:7px;flex-wrap:wrap;align-items:center;padding:0 16px 12px}.drawing-controls select{background:var(--bg);border:1px solid var(--border);color:var(--fg);border-radius:6px;padding:6px 9px;font-size:11px}.drawing-viewport{height:440px;background:#f7fafb;overflow:hidden}.drawing-viewport svg{display:block;width:100%;height:100%;touch-action:none;cursor:grab}.drawing-note{padding:9px 16px;font-size:10px;color:var(--muted)}.table-scroll{overflow:auto;padding:0 16px 16px}table{border-collapse:collapse;width:100%;font-size:11px}th,td{text-align:right;border-bottom:1px solid var(--border);padding:8px;white-space:nowrap}th{color:var(--muted);font-weight:500}tr.current{background:#20413f;color:#c3f4e8}.foot{font-size:10px;color:#8096a6;margin:17px 0 0}.foot code{font-size:10px}noscript p{position:absolute;bottom:40px;left:17px;color:#755e34;font-size:11px;z-index:3}
|
||
@media(max-width:1050px){.workspace{grid-template-columns:minmax(0,1fr) 270px}.wrap{padding:0 17px 25px}.intro-note{display:none}.viewbar{align-items:start;flex-direction:column}.stage{height:540px}}@media(max-width:740px){header{height:72px}.badge{display:none}.wrap{padding:0 12px 20px}.workspace{grid-template-columns:1fr}h1{font-size:21px}.intro{margin-top:20px}.stage{height:440px}.cards{display:grid;grid-template-columns:1fr 1fr}.card:last-child{grid-column:1/-1}.drawing-viewport{height:350px}.toolbar{align-items:start}.brand small{display:none}}@media(max-width:440px){.cards{grid-template-columns:1fr}.stage{height:390px}.toolbar{flex-direction:column}.actions .link{font-size:10px;padding:7px}.help{display:none}}
|
||
|
||
.upload-zone{border:1px dashed #456477;background:#152735;border-radius:10px;padding:16px;display:flex;gap:14px;align-items:center;justify-content:space-between;margin-bottom:18px}.upload-zone.dragover{border-color:var(--mint);background:#1b403d}.upload-zone strong{font-size:13px}.upload-zone p{margin:3px 0 0;font-size:11px;color:var(--muted)}.upload-controls{display:flex;gap:8px;flex-shrink:0}.upload-message{margin:0 0 12px;color:var(--mint);font-size:12px;overflow-wrap:anywhere}.upload-message.error{position:static;background:#492c29;color:#ffd5c8;padding:10px}.upload-message[hidden]{display:none}.specbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 12px;background:var(--panel);border:1px solid var(--border);border-radius:8px;margin-bottom:12px}.specbar label{font-size:12px;color:var(--muted)}.specbar select{background:#10212b;color:var(--fg);border:1px solid #496879;border-radius:6px;padding:6px 12px;min-width:160px}.specbar select:disabled{opacity:.6}.specnote{font-size:10px;color:var(--muted)}.spec-warning{font-size:10px;line-height:1.65;color:var(--gold);margin:9px 0 0}.spec-cell{border:0;color:var(--mint);background:transparent;padding:2px;cursor:pointer;font-size:11px;text-decoration:underline;text-underline-offset:3px}.btn:disabled{cursor:wait;opacity:.55}.actions{flex-wrap:wrap}
|
||
@media(max-width:740px){.upload-zone{align-items:flex-start;flex-direction:column}.upload-controls{width:100%}.upload-controls .btn{flex:1}.specbar select{min-width:120px}.specnote{flex-basis:100%}}
|
||
</style>
|
||
</head>
|
||
<body><div class="wrap">
|
||
<p id="localFileNotice" class="upload-message" hidden role="status">파일로 연 뷰어입니다. 3D 회전과 규격 선택은 가능하며, DXF 첨부 변환은 서버 뷰어에서 사용할 수 있습니다. <a href="http://localhost:8000/">서버 뷰어 열기 →</a><br>서버가 꺼져 있다면 <code>python3 r2c.py serve</code>를 실행하세요. 다른 포트를 사용한다면 해당 서버 주소로 접속하세요.</p>
|
||
<header><div class="brand"><div class="symbol">R2</div><div><strong>R2C 도면 스튜디오</strong><small>DXF → 단면 재구성 → 웹 3D</small></div></div><div class="actions"><span class="badge">DXF 엔티티 추출</span><a class="link" id="sourceLink" href="__DXF_HREF__" download>원본 DXF ↓</a><button class="btn" id="download">3D 모델 ↓</button></div></header>
|
||
<div class="intro"><div><div class="eyebrow">DRAWING / RECONSTRUCTION</div><h1>사각 입구에서 원형 관로까지</h1><p class="subtitle">R2C관의 전환부와 소켓을 원본 단면으로 확인하세요.</p></div><div class="intro-note">모든 치수는 mm 기준<br>원본 A-A / B-B 단면 및 변화치수표 반영</div></div>
|
||
<section class="upload-zone" id="dropZone" aria-label="DXF 첨부 영역"><div><strong>DXF 파일을 첨부하거나 여기에 놓으세요</strong><p>R2C 일반도의 단면·변화치수표를 읽어 바로 3D로 변환합니다. · 최대 10 MB</p><p>첨부 파일은 변환 후 서버에 저장하지 않습니다.</p></div><div class="upload-controls"><input type="file" id="dxfFile" accept=".dxf" hidden><button class="btn" id="uploadButton">DXF 파일 첨부</button><button class="btn" id="sampleReset">기본 도면으로</button></div></section><p class="upload-message" id="uploadMessage" role="status" aria-live="polite" hidden></p>
|
||
<div class="specbar"><label for="specification">규격 선택</label><select id="specification" aria-label="R2C 관로 내경 규격"></select><span class="small-tag" id="specBasis">원본 단면</span><span class="specnote" id="specNotice">선택한 규격의 변화치수를 적용합니다.</span></div>
|
||
<div class="workspace"><section class="main"><div class="toolbar"><div class="seg" aria-label="표시할 부재"><button class="btn active" data-model="r2c">R2C관</button><button class="btn" data-model="pipe">원심력 콘크리트관</button></div><div class="seg" aria-label="분해 표시"><button class="btn active" data-mode="closed">전체</button><button class="btn" data-mode="exploded">덮개 분리</button><button class="btn" data-mode="cutaway">내부 보기</button></div></div>
|
||
<div class="stage"><div class="stage-label">SECTION-BASED 3D<strong id="modelName">R2C · Ø1,000</strong></div><span class="stage-status" id="status">기본 시점</span><svg id="scene" viewBox="0 0 1000 700" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="DXF에서 재구성한 R2C관 3D 모델" tabindex="0">__INITIAL_SCENE__</svg><div class="error" id="renderError" hidden role="alert"></div><noscript><p>기본 3D 형상입니다. 회전과 분해 기능은 JavaScript를 허용하면 사용할 수 있습니다.</p></noscript><span class="stage-bottom">X 길이 / Y 폭 / Z 높이 · 단위 mm</span><div class="zoom"><button id="zoomOut" aria-label="축소">−</button><button id="zoomIn" aria-label="확대">+</button><button id="reset" aria-label="기본 시점">↺</button></div></div>
|
||
<div class="viewbar"><div class="seg" aria-label="시점"><button class="btn active" data-view="iso">등각</button><button class="btn" data-view="inlet">입구 A-A</button><button class="btn" data-view="outlet">소켓 B-B</button><button class="btn" data-view="side">측면</button><button class="btn" data-view="top">평면</button><button class="btn" id="wire" aria-pressed="false">윤곽선</button></div><span class="help">드래그 회전 · 휠 확대 · 방향키 회전</span></div></section>
|
||
<aside class="cards"><section class="card"><h2>도면에서 읽은 치수 <span class="small-tag">DXF VERIFIED</span></h2><dl class="dims" id="dimensions"></dl><hr class="rule"><p class="micro" id="shapeNote">사각 입구 → Ø1,000 원형 유로 → 단차 소켓</p></section><section class="card"><h2>부재와 내부 형상</h2><div class="legend"><i class="swatch"></i>상부 덮개</div><div class="legend"><i class="swatch bottom"></i>하부 본체</div><div class="legend"><i class="swatch inner"></i>전환부·관로·소켓</div><p class="micro">덮개 분리 또는 내부 보기를 선택하면 사각–원형 전환 면이 드러납니다.</p></section><section class="card"><h2>재구성 기준 <span class="small-tag">A-A / B-B</span></h2><p class="micro">외곽선·원호·치수표에서 단면과 소켓 위치를 추출합니다. 원심력관은 실제 종단 폴리라인을 회전시켜 만듭니다.</p><div class="limits" id="limits"></div><p class="spec-warning" id="specWarning"></p><button class="btn" id="evidence">추출 근거 JSON ↓</button></section></aside></div>
|
||
<details class="drawing-card"><summary>원본 DXF 벡터 도면과 비교</summary><div class="drawing-controls"><select id="drawingView" aria-label="도면 영역"><option value="all">전체 일반도</option><option value="r2c">R2C 평면·측면·단면</option><option value="sections">A-A / B-B 단면</option><option value="pipe">원심력관 종단·단면</option></select><button class="btn" id="drawingReset">영역 맞춤</button><span class="micro">드래그 이동 · 휠 확대</span></div><div class="drawing-viewport">__DRAWING_SVG__</div><div class="drawing-note">DXF의 선·원·원호·타원·스플라인·문자·치수 블록을 표시합니다. 해치 채움과 치수 화살표 SOLID는 생략했습니다.</div></details>
|
||
<details class="drawing-card"><summary>변화치수표 · 규격을 누르면 3D에 반영</summary><div class="table-scroll"><table><thead><tr><th>D</th><th>D1</th><th>D2</th><th>L</th><th>L1</th><th>L2</th><th>L3</th><th>B</th><th>b1</th><th>b2</th><th>b3</th><th>b4</th><th>H</th><th>h1</th><th>h2</th><th>h3</th><th>h4</th><th>h5</th><th>h6</th><th>h7</th></tr></thead><tbody id="sizeTable"></tbody></table></div><div class="drawing-note">내경 D를 누르면 해당 규격의 R2C 모델과 치수가 함께 변경됩니다. 원형 콘크리트관은 실제 종단이 있는 원본 규격으로 표시합니다.</div></details>
|
||
<p class="foot" id="sourceNote">Python 표준 라이브러리로 생성한 독립 실행형 웹 뷰어 · 외부 CDN 및 추가 패키지 불필요</p>
|
||
</div>
|
||
<script id="modelData" type="application/json">__MODEL_JSON__</script>
|
||
<script>
|
||
__VIEWER_JS__
|
||
</script>
|
||
</body></html>
|