Validate DXF geometry and splines and improve viewer maintenance
This commit is contained in:
1 parent
11fd3badf6
commit
9cee9bf791
6 files changed
+560
-77
No files matched your search
+101
-11
@@ -6,25 +6,35 @@
|
||||
<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:#f7fafbLine truncated
|
||||
@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">
|
||||
<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="./drawing/R2C_일반도_20260601.dxf" 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"><polygon points="487.27,400.31 485.50,399.73 536.42,363.38 536.89,364.17" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,399.73 485.50,384.86 532.68,356.63 536.42,363.38" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,384.86 485.50,384.57 532.61,356.48 532.68,356.63" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,384.57 485.50,383.95 532.45,356.18 532.61,356.48" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="502.91,405.44 487.27,400.31 536.89,364.17 541.50,371.62" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,383.95 485.50,369.17 528.68,348.59 532.45,356.18" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,369.17 485.50,355.06 525.12,340.51 528.68,348.59" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="516.62,409.95 502.91,405.44 541.50,371.62 546.42,378.79" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,355.06 485.50,341.98 521.94,332.30 525.12,340.51" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,341.98 485.50,329.72 519.15,323.98 521.94,332.30" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="528.84,413.97 516.62,409.95 546.42,378.79 551.63,385.66" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,329.72 485.50,318.11 516.78,315.59 519.15,323.98" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,318.11 485.50,307.01 514.82,307.17 516.78,315.59" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="539.89,417.60 528.84,413.97 551.63,385.66 557.12,392.20" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,307.01 485.50,296.29 513.29,298.76 514.82,307.17" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,296.29 485.50,285.84 512.19,290.37 513.29,298.76" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="550.02,420.92 539.89,417.60 557.12,392.20 562.84,398.37" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,285.84 485.50,275.57 511.53,282.07 512.19,290.37" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="559.41,424.01 550.02,420.92 562.84,398.37 568.79,404.16" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="485.50,275.57 485.50,265.39 511.31,273.87 511.53,282.07" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="528.68,348.59 525.12,340.51 525.27,340.78 528.69,348.53" fill="rgb(108,131,139)" stroke="rgb(108,131,139)" stroke-width=".45"/><polygon points="532.45,356.18 528.68,348.59 528.69,348.53 532.31,355.83" fill="rgb(108,132,140)" stroke="rgb(108,132,140)" stroke-width=".45"/><polygon points="532.61,356.48 532.45,356.18 532.31,355.83 532.46,356.11" fill="rgb(108,132,140)" stroke="rgb(108,132,140)" stroke-width=".45"/><polygon points="532.68,356.63 532.61,356.48 532.46,356.11 532.53,356.25" fill="rgb(108,132,140)" stroke="rgb(108,132,140)" stroke-width=".45"/><polygon points="525.12,340.51 521.94,332.30 522.21,332.89 525.27,340.78" fill="rgb(107,131,138)" stroke="rgb(107,131,138)" stroke-width=".45"/><polygon points="536.42,363.38 532.68,356.63 532.53,356.25 536.12,362.74" fill="rgb(109,132,140)" stroke="rgb(109,132,140)" stroke-width=".45"/><polygon points="536.89,364.17 536.42,363.38 536.12,362.74 536.57,363.50" fill="rgb(109,133,141)" stroke="rgb(109,133,141)" stroke-width=".45"/><polygon points="521.94,332.30 519.15,323.98 519.54,324.90 522.21,332.89" fill="rgb(106,130,137)" stroke="rgb(106,130,137)" stroke-width=".45"/><polygon points="541.50,371.62 536.89,364.17 536.57,363.50 541.00,370.65" fill="rgb(109,133,141)" stroke="rgb(109,133,141)" stroke-width=".45"/><polygon points="519.15,323.98 516.78,315.59 517.26,316.85 519.54,324.90" fill="rgb(105,128,136)" stroke="rgb(105,128,136)" stroke-width=".45"/><polygon points="546.42,378.79 541.50,371.62 541.00,370.65 545.73,377.54" fill="rgb(109,133,141)" stroke="rgb(109,133,141)" stroke-width=".45"/><polygon points="568.20,426.90 559.41,424.01 568.79,404.16 574.93,409.53" fill="rgb(84,96,99)" stroke="rgb(84,96,99)" stroke-width=".45"/><polygon points="516.78,315.59 514.82,307.17 515.38,308.76 517.26,316.85" fill="rgb(104,127,135)" stroke="rgb(104,127,135)" stroke-width=".45"/><polygon points="485.50,265.39 485.50,255.21 511.53Line truncated
|
||||
<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><button class="btn" id="evidence">추출 근거 JSON ↓</button></section></aside></div>
|
||||
<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"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -5940 16820 5940" aria-label="DXF 원본 벡터 도면"><rect x="0" y="-5940" width="16820" height="5940" fill="#f7fafb"/><g font-family="system-ui,sans-serif"><polyline points="10666.486,-4695.680 10556.486,-4695.680" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="10666.486,-3695.680 10556.486,-3695.680" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="10566.486,-4685.680 10566.486,-3705.680" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><g transform="translate(10566.4858629485,-4695.680351079151) rotate(-90.0) scale(10.0,10.0)"><polyline points="-0.250,-0.000 0.250,-0.000 -0.250,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="-0.500,-0.000 -1.000,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/></g><g transform="translate(10566.4858629485,-3695.680351078918) rotate(-270.0) scale(10.0,10.0)"><polyline points="-0.250,-0.000 0.250,-0.000 -0.250,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="-0.500,-0.000 -1.000,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/></g><text x="10536.47083288838" y="-4195.680351079034" font-size="30.0" fill="#cc6c49" transform="rotate(-0.0 10536.47083288838 -4195.680351079034)">D</text><polyline points="10766.486,-4785.386 10656.486,-4785.386" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="10766.486,-3605.975 10656.486,-3605.975" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="10666.486,-4775.386 10666.486,-3615.975" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><g transform="translate(10666.4858629485,-4785.38574328777) rotate(-90.0) scale(10.0,10.0)"><polyline points="-0.250,-0.000 0.250,-0.000 -0.250,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="-0.500,-0.000 -1.000,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/></g><g transform="translate(10666.4858629485,-3605.97495887056) rotate(-270.0) scale(10.0,10.0)"><polyline points="-0.250,-0.000 0.250,-0.000 -0.250,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="-0.500,-0.000 -1.000,-0.000" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/></g><text x="10636.47083288838" y="-4195.680351079165" font-size="30.0" fill="#cc6c49" transform="rotate(-0.0 10636.47083288838 -4195.680351079165)">D1</text><polyline points="970.038,-4513.028 970.038,-3345.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="874.038,-4515.028 874.038,-3343.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="850.038,-4539.028 850.038,-3319.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><text x="788.3174449396392" y="-5049.357368415149" font-size="100.0" fill="#263c4b" transform="rotate(-0.0 788.3174449396392 -5049.357368415149)">▣ 원심력 철근콘크리트관 Ø1000 일반 B형</text><polyline points="3470.038,-3429.028 970.038,-3429.028 970.038,-3345.028 874.038,-3343.028 850.038,-3319.028 850.038,-3263.028 1015.038,-3263.028 1180.038,-3347.028 3430.038,-3347.028 3430.038,-3354.028 3470.038,-3354.028 3470.038,-3429.028" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="3470.038,-4429.028 970.038,-4429.028 970.038,-4513.028 874.038,-4515.028 850.038,-4539.028 850.038,-4595.028 1015.038,-4595.028 1180.038,-4511.028 3430.038,-4511.028 3430.038,-4504.028 3470.038,-4504.028 3470.038,-4429.028" fill="none" stroke="#263c4b" stroke-width="4" stroke-linejoin="round"/><polyline points="970.038,-4513.028 781.162,-4513.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="756.863,-3929.028 3570.038,-3929.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="850.038,-4695.028 850.038,-4595.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="1015.038,-4695.028 1015.038,-4595.028" fill="none" stroke="#cc6c49" stroke-width="4" stroke-linejoin="round"/><polyline points="1180.038,-4695.028 1180.038,-4511.028" fill="nLine truncated
|
||||
<details class="drawing-card"><summary>변화치수표 · 원본에서 추출한 7개 규격</summary><div class="table-scroll"><table><thead><tr><th>D</th><th>L</th><th>B</th><th>H</th><th>D1</th><th>D2</th><th>L1</th><th>L2</th><th>L3</th></tr></thead><tbody id="sizeTable"></tbody></table></div><div class="drawing-note">3D 모델은 실제 단면이 그려진 Ø1,000 규격입니다. 다른 규격은 치수표 비교용입니다.</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">{"source":"R2C_일반도_20260601.dxf","sha256":"24ecb64f2a9875bed65c0e152b1361463ec11767d687bd337744ed75ef144b32","title":"R2C관 · 사각–원형 전환관","diameter":1000.0,"dimensions":{"D":1000.0,"D1":1180.0,"D2":1183.0,"L":1620.0,"L1":86.0,"B":1540.0,"b1":150.0,"b2":120.0,"b3":1310.0,"b4":115.0,"H":1486.0,"h1":166.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":666.0,"h7":820.0,"L2":120.0,"L3":24.0},"width":1540.0,"height":1486.0,"length":1620.0,"axisHeight":666.0,"profiles":[{"x":0,"kind":"rectangle","halfWidth":655.0000000004202,"top":705.0,"bottom":500.0},{"x":100.0,"kind":"rectangle","halfWidth":650.0000000003911,"top":700.0,"bottom":500.0},{"x":1400.0,"kind":"circle","radius":500.0},{"x":1500.0,"kind":"circle","radius":500.0},{"x":1500.0,"kind":"circle","radius":579.705392},{"x":1509.998477,"kind":"circle","radius":589.879916},{"x":1596.003655,"kind":"circle","radius":591.381142},{"x":1620.0,"kind":"circle","radius":615.8}],"pipeProfile":[[2620.0,500.0],[120.0,500.0],[120.0,584.0],[24.0,586.0],[0.0,610.0],[0.0,666.0],[165.0,666.0],[330.0,582.0],[2580.0,582.0],[2580.0,575.0],[2620.0,575.0]],"pipeDimensions":{"nominalLength":2500.0,"overallLength":2620.0,"outerDiameter":1164.0,"wallThickness":82.0},"table":[{"D":700.0,"D1":880.0,"D2":883.0,"L":1605.0,"L1":75.0,"B":1100.0,"b1":100.0,"b2":100.0,"b3":910.0,"b4":95.0,"H":1068.0,"h1":118.0,"h2":150.0,"h3":100.0,"h4":155.0,"h5":95.0,"h6":468.0,"h7":600.0,"L2":105.0,"L3":30.0},{"D":800.0,"D1":980.0,"D2":983.0,"L":1610.0,"L1":80.0,"B":1300.0,"b1":150.0,"b2":100.0,"b3":1110.0,"b4":95.0,"H":1234.0,"h1":134.0,"h2":200.0,"h3":100.0,"h4":205.0,"h5":95.0,"h6":534.0,"h7":700.0,"L2":110.0,"L3":30.0},{"D":900.0,"D1":1080.0,"D2":1083.0,"L":1615.0,"L1":85.0,"B":1440.0,"b1":150.0,"b2":120.0,"b3":1210.0,"b4":115.0,"H":1372.0,"h1":152.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":602.0,"h7":770.0,"L2":115.0,"L3":30.0},{"D":1000.0,"D1":1180.0,"D2":1183.0,"L":1620.0,"L1":86.0,"B":1540.0,"b1":150.0,"b2":120.0,"b3":1310.0,"b4":115.0,"H":1486.0,"h1":166.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":666.0,"h7":820.0,"L2":120.0,"L3":24.0},{"D":1100.0,"D1":1280.0,"D2":1283.0,"L":1625.0,"L1":100.0,"B":1780.0,"b1":200.0,"b2":140.0,"b3":1510.0,"b4":135.0,"H":1669.0,"h1":179.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":729.0,"h7":940.0,"L2":125.0,"L3":25.0},{"D":1200.0,"D1":1380.0,"D2":1383.0,"L":1630.0,"L1":104.0,"B":1880.0,"b1":200.0,"b2":140.0,"b3":1610.0,"b4":135.0,"H":1783.0,"h1":193.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":793.0,"h7":990.0,"L2":130.0,"L3":26.0},{"D":1350.0,"D1":1530.0,"D2":1533.0,"L":1635.0,"L1":108.0,"B":2030.0,"b1":200.0,"b2":140.0,"b3":1760.0,"b4":135.0,"H":1949.0,"h1":209.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":884.0,"h7":1065.0,"L2":135.0,"L3":27.0}],"counts":{"DIMENSION":76,"HATCH":11,"LINE":521,"TEXT":184,"LWPOLYLINE":12,"CIRCLE":3,"SPLINE":12,"ARC":17,"ELLIPSE":27,"MTEXT":2,"INSERT":3,"SOLID":2},"evidence":{"sectionA":"44F","sectionB":["356","357","35B","35C","35D","36F","370","37F","380","381"],"pipeProfile":"195","stations":[100.0,1400.0,1500.0,1509.998477,1596.003655]},"limitations":["2D 일반도 A-A/B-B와 치수표를 이용한 단면 기반 재구성입니다.","사각–원형 전환 면은 단면 사이를 직선 보간했습니다. 철근과 개스킷은 모델에 포함하지 않았습니다.","상·하부 접합부의 작은 홈은 원본 2D 도면에서 확인할 수 있으며 3D에서는 생략했습니다."],"meshes":{"r2c":{"vertices":[[-810.0,770.0,666.0],[-810.0,770.0,716.468466],[-810.0,770.0,767.372423],[-810.0,770.0,819.162523],[-810.0,770.0,872.320878],[-810.0,770.0,927.379779],[-810.0,770.0,984.944443],[-810.0,770.0,1045.721978],[-810.0,770.0,1110.559707],[-810.0,770.0,1180.497551],[-810.0,770.0,1256.841781],[-810.0,770.0,1341.271877],[-810.0,770.0,1436.0],[-810.0,770.0,1486.0],[-810.0,761.843972,1486.0],[-810.0,761.428571,1486.0],[-810.0,719.1207,1486.0],[-810.0,629.20813,1486.0],[-810.0,547.906483,1486.0],[-810.0,473.427221,1486.0],[-810.0,404.379249,1486.0],[-810.0,339.655121,1486.0],[-810.0,278.352492,1486.0],[-810.0,219.718338,1486.0],[-810.0,163.108141,1486.0],[-810.0,107.955048,1486.0],[-810.0,53.74564,1486.0],[-810.0,-0.0,1486.0],[-810.0,-53.74564,1486.0],[-810.0,-107.955048,1486.0],[-810.0,-163.108141,1486.0],[-810.0,-219.718338,1486.0],[-810.0,-278.352492,1486.0],[-810.0,-339.655121,1486.0],[-810.0,-404.379249,1486.0],[-810.0,-473.427221,1486.0],[-810.0,-547.906483,1486.0],[-810.0,-629.20813,1486.0],[-810.0,-719.1207,1486.0],[-810.0,-761.428571,1486.0],[-810.0,-761.843972,1486.0],[-810.0,-770.0,1486.0],[-810.0,-770.0,1436.0],[-810.0,-770.0,1341.271877],[-810.0,-770.0,1256.841781],[-810.0,-770.0,1180.497551],[-810.0,-770.0,1110.559707],[-810.0,-770.0,1045.721978],[-810.0,-770.0,984.944443],[-810.0,-770.0,927.379779],[-810.0,-770.0,872.320878],[-810.0,-770.0,819.162523],[-810.0,-770.0,767.372423],[-810.0,-770.0,716.468466],[-810.0,Line truncated
|
||||
<script id="modelData" type="application/json">{"source":"R2C_일반도_20260601.dxf","sha256":"24ecb64f2a9875bed65c0e152b1361463ec11767d687bd337744ed75ef144b32","title":"R2C관 · 사각–원형 전환관","diameter":1000.0,"dimensions":{"D":1000.0,"D1":1180.0,"D2":1183.0,"L":1620.0,"L1":86.0,"B":1540.0,"b1":150.0,"b2":120.0,"b3":1310.0,"b4":115.0,"H":1486.0,"h1":166.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":666.0,"h7":820.0,"L2":120.0,"L3":24.0},"width":1540.0,"height":1486.0,"length":1620.0,"axisHeight":666.0,"profiles":[{"x":0,"kind":"rectangle","halfWidth":655.0000000004202,"top":705.0,"bottom":500.0},{"x":100.0,"kind":"rectangle","halfWidth":650.0000000003911,"top":700.0,"bottom":500.0},{"x":1400.0,"kind":"circle","radius":500.0},{"x":1500.0,"kind":"circle","radius":500.0},{"x":1500.0,"kind":"circle","radius":579.705392},{"x":1509.998477,"kind":"circle","radius":589.879916},{"x":1596.003655,"kind":"circle","radius":591.381142},{"x":1620.0,"kind":"circle","radius":615.8}],"pipeProfile":[[2620.0,500.0],[120.0,500.0],[120.0,584.0],[24.0,586.0],[0.0,610.0],[0.0,666.0],[165.0,666.0],[330.0,582.0],[2580.0,582.0],[2580.0,575.0],[2620.0,575.0]],"pipeDimensions":{"nominalLength":2500.0,"overallLength":2620.0,"outerDiameter":1164.0,"wallThickness":82.0},"table":[{"D":700.0,"D1":880.0,"D2":883.0,"L":1605.0,"L1":75.0,"B":1100.0,"b1":100.0,"b2":100.0,"b3":910.0,"b4":95.0,"H":1068.0,"h1":118.0,"h2":150.0,"h3":100.0,"h4":155.0,"h5":95.0,"h6":468.0,"h7":600.0,"L2":105.0,"L3":30.0},{"D":800.0,"D1":980.0,"D2":983.0,"L":1610.0,"L1":80.0,"B":1300.0,"b1":150.0,"b2":100.0,"b3":1110.0,"b4":95.0,"H":1234.0,"h1":134.0,"h2":200.0,"h3":100.0,"h4":205.0,"h5":95.0,"h6":534.0,"h7":700.0,"L2":110.0,"L3":30.0},{"D":900.0,"D1":1080.0,"D2":1083.0,"L":1615.0,"L1":85.0,"B":1440.0,"b1":150.0,"b2":120.0,"b3":1210.0,"b4":115.0,"H":1372.0,"h1":152.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":602.0,"h7":770.0,"L2":115.0,"L3":30.0},{"D":1000.0,"D1":1180.0,"D2":1183.0,"L":1620.0,"L1":86.0,"B":1540.0,"b1":150.0,"b2":120.0,"b3":1310.0,"b4":115.0,"H":1486.0,"h1":166.0,"h2":200.0,"h3":120.0,"h4":205.0,"h5":115.0,"h6":666.0,"h7":820.0,"L2":120.0,"L3":24.0},{"D":1100.0,"D1":1280.0,"D2":1283.0,"L":1625.0,"L1":100.0,"B":1780.0,"b1":200.0,"b2":140.0,"b3":1510.0,"b4":135.0,"H":1669.0,"h1":179.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":729.0,"h7":940.0,"L2":125.0,"L3":25.0},{"D":1200.0,"D1":1380.0,"D2":1383.0,"L":1630.0,"L1":104.0,"B":1880.0,"b1":200.0,"b2":140.0,"b3":1610.0,"b4":135.0,"H":1783.0,"h1":193.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":793.0,"h7":990.0,"L2":130.0,"L3":26.0},{"D":1350.0,"D1":1530.0,"D2":1533.0,"L":1635.0,"L1":108.0,"B":2030.0,"b1":200.0,"b2":140.0,"b3":1760.0,"b4":135.0,"H":1949.0,"h1":209.0,"h2":250.0,"h3":140.0,"h4":255.0,"h5":135.0,"h6":884.0,"h7":1065.0,"L2":135.0,"L3":27.0}],"counts":{"DIMENSION":76,"HATCH":11,"LINE":521,"TEXT":184,"LWPOLYLINE":12,"CIRCLE":3,"SPLINE":12,"ARC":17,"ELLIPSE":27,"MTEXT":2,"INSERT":3,"SOLID":2},"evidence":{"sectionA":"44F","sectionB":["356","357","35B","35C","35D","36F","370","37F","380","381"],"pipeProfile":"195","stations":[100.0,1400.0,1500.0,1509.998477,1596.003655]},"limitations":["2D 일반도 A-A/B-B와 치수표를 이용한 단면 기반 재구성입니다.","사각–원형 전환 면은 단면 사이를 직선 보간했습니다. 철근과 개스킷은 모델에 포함하지 않았습니다.","상·하부 접합부의 작은 홈은 원본 2D 도면에서 확인할 수 있으며 3D에서는 생략했습니다."],"meshes":{"r2c":{"vertices":[[-810.0,770.0,666.0],[-810.0,770.0,716.468466],[-810.0,770.0,767.372423],[-810.0,770.0,819.162523],[-810.0,770.0,872.320878],[-810.0,770.0,927.379779],[-810.0,770.0,984.944443],[-810.0,770.0,1045.721978],[-810.0,770.0,1110.559707],[-810.0,770.0,1180.497551],[-810.0,770.0,1256.841781],[-810.0,770.0,1341.271877],[-810.0,770.0,1436.0],[-810.0,770.0,1486.0],[-810.0,761.843972,1486.0],[-810.0,761.428571,1486.0],[-810.0,719.1207,1486.0],[-810.0,629.20813,1486.0],[-810.0,547.906483,1486.0],[-810.0,473.427221,1486.0],[-810.0,404.379249,1486.0],[-810.0,339.655121,1486.0],[-810.0,278.352492,1486.0],[-810.0,219.718338,1486.0],[-810.0,163.108141,1486.0],[-810.0,107.955048,1486.0],[-810.0,53.74564,1486.0],[-810.0,-0.0,1486.0],[-810.0,-53.74564,1486.0],[-810.0,-107.955048,1486.0],[-810.0,-163.108141,1486.0],[-810.0,-219.718338,1486.0],[-810.0,-278.352492,1486.0],[-810.0,-339.655121,1486.0],[-810.0,-404.379249,1486.0],[-810.0,-473.427221,1486.0],[-810.0,-547.906483,1486.0],[-810.0,-629.20813,1486.0],[-810.0,-719.1207,1486.0],[-810.0,-761.428571,1486.0],[-810.0,-761.843972,1486.0],[-810.0,-770.0,1486.0],[-810.0,-770.0,1436.0],[-810.0,-770.0,1341.271877],[-810.0,-770.0,1256.841781],[-810.0,-770.0,1180.497551],[-810.0,-770.0,1110.559707],[-810.0,-770.0,1045.721978],[-810.0,-770.0,984.944443],[-810.0,-770.0,927.379779],[-810.0,-770.0,872.320878],[-810.0,-770.0,819.162523],[-810.0,-770.0,767.372423],[-810.0,-770.0,716.468466],[-810.0,Line truncated
|
||||
<script>
|
||||
window.addEventListener('error',function(event){const box=document.getElementById('renderError');box.hidden=false;box.textContent='화면 실행 오류: '+event.message;});
|
||||
(()=>{
|
||||
'use strict';
|
||||
const data=JSON.parse(document.getElementById('modelData').textContent),scene=document.getElementById('scene');
|
||||
const initialDocument=JSON.parse(document.getElementById('modelData').textContent),scene=document.getElementById('scene');
|
||||
let sourceDocument=initialDocument,data=initialDocument,selectedDiameter=initialDocument.diameter;
|
||||
const initialDrawing=document.querySelector('.drawing-viewport').innerHTML,initialSource=document.getElementById('sourceLink').getAttribute('href');
|
||||
let sourceUrl=null,uploadBusy=false;
|
||||
const specification=document.getElementById('specification');
|
||||
const tableKeys=['D','D1','D2','L','L1','L2','L3','B','b1','b2','b3','b4','H','h1','h2','h3','h4','h5','h6','h7'];
|
||||
let model='r2c',mode='closed',yaw=.87,pitch=.40,zoom=1,wire=false,drag=null,pending=false;
|
||||
const fmt=n=>Math.round(n).toLocaleString('ko-KR');
|
||||
const presets={iso:[.87,.40],inlet:[Math.PI/2,0],outlet:[-Math.PI/2,0],side:[0,0],top:[0,Math.PI/2]};
|
||||
@@ -54,20 +64,100 @@ function render(){
|
||||
scene.innerHTML=markup;document.getElementById('status').textContent=(mode==='exploded'?'덮개 분리':mode==='cutaway'?'내부 보기':'전체 형상')+' · '+Math.round(zoom*100)+'%';
|
||||
}
|
||||
function schedule(){if(!pending){pending=true;requestAnimationFrame(render);}}
|
||||
function info(){const d=data.dimensions,values=model==='r2c'?[['관로 내경 D','Ø'+fmt(data.diameter)],['부재 길이 L',fmt(data.length)],['외형 폭 B',fmt(data.width)],['외형 높이 H',fmt(data.height)],['관 중심 높이 h6',fmt(data.axisHeight)],['입구 폭 b3',fmt(d.b3)],['소켓 길이 L2',fmt(d.L2)]]:[['관로 내경','Ø'+fmt(data.diameter)],['유효 길이',fmt(data.pipeDimensions.nominalLength)],['전체 길이',fmt(Math.max(...data.pipeProfile.map(p=>p[0])))],['본체 외경','Ø'+fmt(data.pipeDimensions.outerDiameter)],['본체 두께',fmt(data.pipeDimensions.wallThickness)]];document.getElementById('dimensions').innerHTML=values.map((v,i)=>'<dt>'+v[0]+'</dt><dd class="'+(i===0?'main-value':'')+'">'+v[1]+' <small>mm</small></dd>').join('');document.getElementById('modelName').textContent=(model==='r2c'?'R2C관':'원심력 콘크리트관')+' · Ø'+fmt(data.diameter);document.getElementById('shapeNote').textContent=model==='r2c'?'사각 입구 → Ø1,000 원형 유로 → 단차 소켓':'종단 폴리라인 195를 관 중심축으로 회전한 실제 단면 형상';}
|
||||
function info(){const d=data.dimensions,values=model==='r2c'?[['관로 내경 D','Ø'+fmt(data.diameter)],['부재 길이 L',fmt(data.length)],['외형 폭 B',fmt(data.width)],['외형 높이 H',fmt(data.height)],['관 중심 높이 h6',fmt(data.axisHeight)],['입구 폭 b3',fmt(d.b3)],['소켓 길이 L2',fmt(d.L2)]]:[['관로 내경','Ø'+fmt(data.diameter)],['유효 길이',fmt(data.pipeDimensions.nominalLength)],['전체 길이',fmt(Math.max(...data.pipeProfile.map(p=>p[0])))],['본체 외경','Ø'+fmt(data.pipeDimensions.outerDiameter)],['본체 두께',fmt(data.pipeDimensions.wallThickness)]];document.getElementById('dimensions').innerHTML=values.map((v,i)=>'<dt>'+v[0]+'</dt><dd class="'+(i===0?'main-value':'')+'">'+v[1]+' <small>mm</small></dd>').join('');document.getElementById('modelName').textContent=(model==='r2c'?'R2C관':'원심력 콘크리트관')+' · Ø'+fmt(data.diameter);document.getElementById('shapeNote').textContent=model==='r2c'?'사각 입구 → Ø'+fmt(data.diameter)+' 원형 유로 → 단차 소켓':'원본 DXF 종단 폴리라인을 회전한 실제 단면 형상';}
|
||||
function reset(){[yaw,pitch]=presets.iso;zoom=1;active('[data-view]','view','iso');schedule();}
|
||||
document.querySelectorAll('[data-model]').forEach(b=>b.addEventListener('click',()=>{model=b.dataset.model;mode='closed';active('[data-model]','model',model);active('[data-mode]','mode',mode);document.querySelectorAll('[data-mode]').forEach(x=>x.disabled=model==='pipe'&&x.dataset.mode==='exploded');info();reset();}));
|
||||
function updateTable(){document.getElementById('sizeTable').innerHTML=sourceDocument.table.map(row=>'<tr class="'+(row.D===selectedDiameter?'current':'')+'">'+tableKeys.map(key=>key==='D'?'<td><button class="spec-cell" data-spec="'+row.D+'" aria-label="내경 '+row.D+' 규격 보기">'+fmt(row.D)+'</button></td>':'<td>'+fmt(row[key])+'</td>').join('')+'</tr>').join('');}
|
||||
function applySelection(){
|
||||
const variant=sourceDocument.variants.find(v=>v.diameter===selectedDiameter);
|
||||
if(!variant)throw Error('선택한 규격의 모델을 찾지 못했습니다.');
|
||||
data=model==='pipe'?sourceDocument:{...sourceDocument,...variant};
|
||||
specification.value=String(selectedDiameter);specification.disabled=model==='pipe';
|
||||
document.querySelectorAll('[data-mode]').forEach(b=>b.disabled=model==='pipe'&&b.dataset.mode==='exploded');
|
||||
document.getElementById('specBasis').textContent=model==='pipe'?'원본 종단':data.basis==='drawn'?'원본 단면':'치수표 재구성';
|
||||
document.getElementById('specNotice').textContent=model==='pipe'?'원심력관은 원본 Ø'+fmt(sourceDocument.diameter)+' 규격만 제공합니다.':data.reconstructionNote;
|
||||
document.getElementById('specWarning').textContent=model==='pipe'?'':(data.warnings||[]).join(' ');
|
||||
document.getElementById('limits').textContent=sourceDocument.limitations.join(' ');
|
||||
info();updateTable();schedule();
|
||||
}
|
||||
function selectSpecification(value){selectedDiameter=Number(value);model='r2c';active('[data-model]','model',model);applySelection();}
|
||||
specification.addEventListener('change',e=>selectSpecification(e.target.value));
|
||||
document.getElementById('sizeTable').addEventListener('click',e=>{const button=e.target.closest('[data-spec]');if(button)selectSpecification(button.dataset.spec);});
|
||||
document.querySelectorAll('[data-model]').forEach(b=>b.addEventListener('click',()=>{model=b.dataset.model;mode='closed';active('[data-model]','model',model);active('[data-mode]','mode',mode);applySelection();reset();}));
|
||||
document.querySelectorAll('[data-mode]').forEach(b=>b.addEventListener('click',()=>{mode=b.dataset.mode;active('[data-mode]','mode',mode);schedule();}));
|
||||
document.querySelectorAll('[data-view]').forEach(b=>b.addEventListener('click',()=>{[yaw,pitch]=presets[b.dataset.view];active('[data-view]','view',b.dataset.view);schedule();}));
|
||||
document.getElementById('wire').addEventListener('click',e=>{wire=!wire;e.currentTarget.classList.toggle('active',wire);e.currentTarget.setAttribute('aria-pressed',String(wire));schedule();});
|
||||
document.getElementById('zoomIn').addEventListener('click',()=>{zoom=Math.min(2.5,zoom*1.2);schedule();});document.getElementById('zoomOut').addEventListener('click',()=>{zoom=Math.max(.5,zoom/1.2);schedule();});document.getElementById('reset').addEventListener('click',reset);
|
||||
scene.addEventListener('pointerdown',e=>{drag={x:e.clientX,y:e.clientY,yaw,pitch};scene.setPointerCapture(e.pointerId);});scene.addEventListener('pointermove',e=>{if(!drag)return;yaw=drag.yaw+(e.clientX-drag.x)*.008;pitch=Math.max(-1.5,Math.min(1.5,drag.pitch+(e.clientY-drag.y)*.006));active('[data-view]','view','');schedule();});scene.addEventListener('pointerup',()=>drag=null);scene.addEventListener('pointercancel',()=>drag=null);scene.addEventListener('wheel',e=>{e.preventDefault();zoom=Math.max(.5,Math.min(2.5,zoom*Math.exp(-e.deltaY*.001)));schedule();},{passive:false});scene.addEventListener('keydown',e=>{if(!['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(e.key))return;e.preventDefault();yaw+=e.key==='ArrowLeft'?-.1:e.key==='ArrowRight'?.1:0;pitch=Math.max(-1.5,Math.min(1.5,pitch+(e.key==='ArrowUp'?.1:e.key==='ArrowDown'?-.1:0)));schedule();});
|
||||
function save(name,text,type){const url=URL.createObjectURL(new Blob([text],{type})),a=document.createElement('a');a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}
|
||||
document.getElementById('download').addEventListener('click',()=>{const mesh=data.meshes[model],lines=['# DXF reconstructed model; units: mm'];for(const p of mesh.vertices)lines.push('v '+p.join(' '));for(const f of mesh.faces)lines.push('f '+f.map(i=>i+1).join(' '));save(model+'.obj',lines.join('\n')+'\n','text/plain');});document.getElementById('evidence').addEventListener('click',()=>{const report={...data};delete report.meshes;save('dxf-evidence.json',JSON.stringify(report,null,2),'application/json');});
|
||||
document.getElementById('sizeTable').innerHTML=data.table.map(row=>'<tr class="'+(row.D===data.diameter?'current':'')+'">'+['D','L','B','H','D1','D2','L1','L2','L3'].map(key=>'<td>'+fmt(row[key])+'</td>').join('')+'</tr>').join('');document.getElementById('limits').textContent=data.limitations.join(' ');document.getElementById('sourceNote').textContent=data.source+' · SHA-256 '+data.sha256.slice(0,16)+'… · 외부 라이브러리 없이 생성';
|
||||
const drawing=document.querySelector('.drawing-viewport svg'),regions={all:[0,-5940,16820,5940],r2c:[8750,-5500,7900,5000],sections:[11350,-2800,4200,1950],pipe:[550,-4900,4800,1900]};let vb=regions.all.slice(),drawingDrag=null;
|
||||
function applyView(){drawing.setAttribute('viewBox',vb.join(' '));}document.getElementById('drawingView').addEventListener('change',e=>{vb=regions[e.target.value].slice();applyView();});document.getElementById('drawingReset').addEventListener('click',()=>{vb=regions[document.getElementById('drawingView').value].slice();applyView();});drawing.addEventListener('pointerdown',e=>{drawingDrag={x:e.clientX,y:e.clientY,v:vb.slice()};drawing.setPointerCapture(e.pointerId);});drawing.addEventListener('pointermove',e=>{if(!drawingDrag)return;const r=drawing.getBoundingClientRect(),s=Math.max(drawingDrag.v[2]/r.width,drawingDrag.v[3]/r.height);vb=[drawingDrag.v[0]-(e.clientX-drawingDrag.x)*s,drawingDrag.v[1]-(e.clientY-drawingDrag.y)*s,drawingDrag.v[2],drawingDrag.v[3]];applyView();});drawing.addEventListener('pointerup',()=>drawingDrag=null);drawing.addEventListener('pointercancel',()=>drawingDrag=null);drawing.addEventListener('wheel',e=>{e.preventDefault();const f=Math.exp(Math.max(-.35,Math.min(.35,e.deltaY*.001))),width=Math.max(200,Math.min(40000,vb[2]*f)),ratio=width/vb[2];vb=[vb[0]+vb[2]*(1-ratio)/2,vb[1]+vb[3]*(1-ratio)/2,width,vb[3]*ratio];applyView();},{passive:false});
|
||||
info();render();
|
||||
document.getElementById('download').addEventListener('click',()=>{const mesh=data.meshes[model],lines=['# DXF reconstructed model; units: mm'];for(const p of mesh.vertices)lines.push('v '+p.join(' '));for(const f of mesh.faces)lines.push('f '+f.map(i=>i+1).join(' '));save(model+'-D'+data.diameter+'.obj',lines.join('\n')+'\n','text/plain');});document.getElementById('evidence').addEventListener('click',()=>{const report={...data};delete report.meshes;delete report.variants;save('dxf-evidence-D'+data.diameter+'.json',JSON.stringify(report,null,2),'application/json');});
|
||||
let drawing=document.querySelector('.drawing-viewport svg');
|
||||
let regions={},vb=[],drawingDrag=null;
|
||||
function drawingRegions(){
|
||||
// Measure rendered vectors, including block transforms and text.
|
||||
const bounds=drawing.querySelector('g').getBBox();
|
||||
const pad=100;
|
||||
const all=[bounds.x-pad,bounds.y-pad,bounds.width+2*pad,bounds.height+2*pad];
|
||||
const labels=Array.from(drawing.querySelectorAll('text'));
|
||||
const find=text=>labels.find(label=>label.textContent.includes(text));
|
||||
const around=(label,width,height)=>{
|
||||
if(!label)return all.slice();
|
||||
const box=label.getBBox();
|
||||
return [box.x-200,box.y-100,width+400,height+500];
|
||||
};
|
||||
const aa=find('단면 A-A'),bb=find('단면 B-B');
|
||||
let sections=all.slice();
|
||||
if(aa&&bb){
|
||||
const a=around(aa,sourceDocument.width,sourceDocument.height);
|
||||
const b=around(bb,sourceDocument.width,sourceDocument.height);
|
||||
const x=Math.min(a[0],b[0]),y=Math.min(a[1],b[1]);
|
||||
sections=[x,y,Math.max(a[0]+a[2],b[0]+b[2])-x,Math.max(a[1]+a[3],b[1]+b[3])-y];
|
||||
}
|
||||
regions={all,r2c:around(find('측면도'),sourceDocument.length,sourceDocument.height),
|
||||
sections,pipe:around(find('일반 B형'),sourceDocument.pipeDimensions.overallLength,sourceDocument.pipeDimensions.outerDiameter)};
|
||||
vb=regions[document.getElementById('drawingView').value].slice();
|
||||
applyView();
|
||||
}
|
||||
function applyView(){drawing.setAttribute('viewBox',vb.join(' '));}document.getElementById('drawingView').addEventListener('change',e=>{vb=regions[e.target.value].slice();applyView();});document.getElementById('drawingReset').addEventListener('click',()=>{vb=regions[document.getElementById('drawingView').value].slice();applyView();});function bindDrawing(){drawing.addEventListener('pointerdown',e=>{drawingDrag={x:e.clientX,y:e.clientY,v:vb.slice()};drawing.setPointerCapture(e.pointerId);});drawing.addEventListener('pointermove',e=>{if(!drawingDrag)return;const r=drawing.getBoundingClientRect(),s=Math.max(drawingDrag.v[2]/r.width,drawingDrag.v[3]/r.height);vb=[drawingDrag.v[0]-(e.clientX-drawingDrag.x)*s,drawingDrag.v[1]-(e.clientY-drawingDrag.y)*s,drawingDrag.v[2],drawingDrag.v[3]];applyView();});drawing.addEventListener('pointerup',()=>drawingDrag=null);drawing.addEventListener('pointercancel',()=>drawingDrag=null);drawing.addEventListener('wheel',e=>{e.preventDefault();const f=Math.exp(Math.max(-.35,Math.min(.35,e.deltaY*.001))),width=Math.max(200,Math.min(40000,vb[2]*f)),ratio=width/vb[2];vb=[vb[0]+vb[2]*(1-ratio)/2,vb[1]+vb[3]*(1-ratio)/2,width,vb[3]*ratio];applyView();},{passive:false});}
|
||||
|
||||
function documentInfo(){
|
||||
specification.innerHTML=sourceDocument.variants.map(v=>'<option value="'+v.diameter+'">Ø '+fmt(v.diameter)+' mm</option>').join('');
|
||||
document.getElementById('sourceNote').textContent=sourceDocument.source+' · SHA-256 '+sourceDocument.sha256.slice(0,16)+'… · 첨부 변환은 Python 서버에서 실행';
|
||||
applySelection();
|
||||
}
|
||||
function uploadMessage(text,error=false){const el=document.getElementById('uploadMessage');el.textContent=text;el.hidden=!text;el.classList.toggle('error',error);}
|
||||
function replaceDocument(documentData,svg,file){
|
||||
sourceDocument=documentData;selectedDiameter=documentData.diameter;model='r2c';mode='closed';
|
||||
if(sourceUrl){URL.revokeObjectURL(sourceUrl);sourceUrl=null;}
|
||||
if(file){sourceUrl=URL.createObjectURL(file);document.getElementById('sourceLink').href=sourceUrl;document.getElementById('sourceLink').download=file.name;}else{document.getElementById('sourceLink').href=initialSource;document.getElementById('sourceLink').download=initialDocument.source;}
|
||||
document.querySelector('.drawing-viewport').innerHTML=svg;drawing=document.querySelector('.drawing-viewport svg');drawingDrag=null;bindDrawing();
|
||||
active('[data-model]','model',model);active('[data-mode]','mode',mode);documentInfo();if(drawing.closest('details').open)drawingRegions();reset();
|
||||
}
|
||||
async function upload(file){
|
||||
if(!file||uploadBusy)return;
|
||||
if(!/\.dxf$/i.test(file.name)){uploadMessage('.dxf 파일을 선택해 주세요.',true);return;}
|
||||
if(file.size>10*1024*1024){uploadMessage('파일은 10 MB 이하로 첨부해 주세요.',true);return;}
|
||||
if(location.protocol==='file:'){uploadMessage('파일 첨부 변환은 웹 서버에서 사용할 수 있습니다. python3 r2c.py serve 실행 후 http://localhost:8000 에 접속해 주세요.',true);return;}
|
||||
uploadBusy=true;document.getElementById('uploadButton').disabled=true;document.getElementById('sampleReset').disabled=true;uploadMessage(file.name+' · 도면과 규격을 분석하고 있습니다…');
|
||||
const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),30000);
|
||||
try{
|
||||
const response=await fetch('/api/convert',{method:'POST',headers:{'Content-Type':'application/octet-stream','X-Filename':encodeURIComponent(file.name)},body:file,signal:controller.signal});
|
||||
if(!(response.headers.get('Content-Type')||'').includes('application/json'))throw Error('첨부 변환 API가 없습니다. python3 r2c.py serve로 실행한 사이트에서 첨부해 주세요.');
|
||||
const payload=await response.json();if(!response.ok)throw Error(payload.error||'도면 변환에 실패했습니다.');
|
||||
if(!payload.model?.variants?.length||!payload.drawingSvg?.startsWith('<svg'))throw Error('변환 결과가 올바르지 않습니다.');
|
||||
replaceDocument(payload.model,payload.drawingSvg,file);uploadMessage(file.name+' · '+payload.model.variants.length+'개 규격을 불러왔습니다.');
|
||||
}catch(error){uploadMessage(error.name==='AbortError'?'변환 시간이 초과되었습니다. 파일 크기와 도면 형식을 확인해 주세요.':error.message,true);}
|
||||
finally{clearTimeout(timer);uploadBusy=false;document.getElementById('uploadButton').disabled=false;document.getElementById('sampleReset').disabled=false;document.getElementById('dxfFile').value='';}
|
||||
}
|
||||
const fileInput=document.getElementById('dxfFile'),dropZone=document.getElementById('dropZone');
|
||||
document.getElementById('uploadButton').addEventListener('click',()=>fileInput.click());fileInput.addEventListener('change',e=>upload(e.target.files[0]));
|
||||
dropZone.addEventListener('dragover',e=>{e.preventDefault();dropZone.classList.add('dragover');});dropZone.addEventListener('dragleave',()=>dropZone.classList.remove('dragover'));dropZone.addEventListener('drop',e=>{e.preventDefault();dropZone.classList.remove('dragover');upload(e.dataTransfer.files[0]);});
|
||||
document.getElementById('sampleReset').addEventListener('click',()=>{replaceDocument(initialDocument,initialDrawing,null);uploadMessage('기본 도면으로 돌아왔습니다.');});
|
||||
bindDrawing();documentInfo();render();
|
||||
// Hidden SVGs have no measurable bounds until their details panel opens.
|
||||
document.querySelector('.drawing-card').addEventListener('toggle',event=>{
|
||||
if(event.currentTarget.open)drawingRegions();
|
||||
});
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body></html>
|
||||
Reference in new issue
Block a user