단면 윤곽의 OBJ 파일 및 근거 JSON 내보내기 추가

This commit is contained in:
김지영 committed 2026-10-20 10:57:56 +09:00
1 parent 093338256c
commit 02eb05bda4
2 files changed
+19 -3

No files matched your search

+4 -3
View File
@@ -1,6 +1,6 @@
import { useMemo, useState } from 'react'
import Viewer from './Viewer'
import { model, groups, palette, partDimensions, exportModel } from './model/geometry'
import { model, groups, palette, partDimensions, exportModel, exportOBJ } from './model/geometry'
function download(data, filename, type = 'application/json') {
const url = URL.createObjectURL(new Blob([data], { type }))
@@ -21,7 +21,7 @@ export default function App() {
<a className="brand" href="#top"><span className="brand-icon">G</span>도면 모델 작업실</a>
<span className="project-label">구조 검토 <b>/</b> 일반도 13 · 14</span>
<a className="drawing-link" href="./drawings/광터지하차도_일반도(BLOCK7).dxf" download>원본 DXF ↓</a>
<button className="export" onClick={() => download(JSON.stringify(exportModel(hidden), null, 2), '광터_BLOCK7_단면모델.json')}>모델 데이터 ↓</button>
<button className="export" onClick={() => download(exportOBJ(hidden), '광터_BLOCK7_단면윤곽.obj', 'text/plain')}>3D OBJ ↓</button>
</header>
<section className="intro" id="top">
<div><span className="kicker">도면에서 읽은 구조, 근거가 보이는 모델</span><h1>광터지하차도 <em>BLOCK7</em></h1><p>원본 단면 윤곽을 보존한 3D 검토 모델</p></div>
@@ -49,7 +49,7 @@ export default function App() {
if (!ps.length) return null
const allHidden = ps.every(p => hidden.has(p.id))
return <details key={g} open={search ? true : undefined}><summary><span className="swatch" style={{ background: palette[i % palette.length] }} /><strong>{g}</strong><small>{ps.length}</small></summary><div className="group-actions"><button onClick={() => toggle(ps.map(p => p.id))}>{allHidden ? '그룹 표시' : '그룹 숨김'}</button></div>{ps.map(p => <div className={`part-row ${selected === p.id ? 'chosen' : ''}`} key={p.id}><button onClick={() => setSelected(p.id)} title={p.name}>{p.name}</button><input aria-label={`${p.name} 표시`} type="checkbox" checked={!hidden.has(p.id)} onChange={() => toggle([p.id])} /></div>)}</details>
})}{!filtered.length && <p className="empty">검색 결과가 없습니다.</p>}</div><p className="panel-note">선 개수는 실제 부재 개수와 다릅니다. 반복선은 기본적으로 숨겨져 있습니다.</p></>}
})}{!filtered.length && <p className="empty">검색 결과가 없습니다.</p>}</div><button className="subtle" onClick={() => download(JSON.stringify(exportModel(hidden), null, 2), '광터_BLOCK7_단면모델.json')}>좌표·근거 JSON ↓</button><p className="panel-note">선 개수는 실제 부재 개수와 다릅니다. 반복선은 기본적으로 숨겨져 있습니다.</p></>}
{tab === '도면' && <><h2>원본 좌표 검토</h2><p className="panel-note">주요 LINE·TEXT와 공동구 선을 발췌한 검토 자료입니다. 전체 도면은 원본 DXF를 확인하세요.</p><div className="source-tabs">{['단면A', '단면B', '평면검토', '종단검토', '측구상세'].map(s => <button key={s} className={s === source ? 'active' : ''} onClick={() => setSource(s)}>{s}</button>)}</div><a href={`./analysis/${source}.svg`} target="_blank" rel="noreferrer"><img className="drawing-preview" src={`./analysis/${source}.svg`} alt={`${source} 원본 좌표 발췌`} /><span className="open-drawing">크게 보기 ↗</span></a><div className="source-note"><strong>그림 읽는 법</strong><p>녹색은 두 콘크리트 레이어, 황토색은 공동구 삽입 블록, 회색은 보조선입니다. 도면 기호·일부 블록은 생략되어 있습니다.</p></div></>}
{tab === '검증' && <><h2>확인한 것과 남은 것</h2><ul className="checks"><li><b>확인</b><span>A/B 벽체 상단 EL<br /><small>종단면과 단면 각각 일치</small></span></li><li><b>확인</b><span>단면별 구조선 114개<br /><small>MAJR 108 + MINR 6, 원본 번호 보존</small></span></li><li><b>확인</b><span>공동구 실제 직선·원호<br /><small>좌우 반전과 단면별 높이 반영</small></span></li><li className="pending"><b>미확정</b><span>평면 중심선·절단 위치<br /><small>중심 EL·중간 곡률 미검증</small></span></li><li className="pending"><b>미확정</b><span>단면·종단 측점 차이<br /><small>각 끝 10mm 차이, 경계 대응 필요</small></span></li><li className="pending"><b>미확정</b><span>연결 표면·체적·재료<br /><small>실제 중간 형상과 상세 연장 미검증</small></span></li></ul><div className="source-note"><strong>현재 표현 범위</strong><p>두 단면의 구조선과 공동구 윤곽을 공간에 배치했습니다. 표면·그림자를 적용한 렌더링은 연결 형상 확인 후 추가할 항목입니다. 현재 윤곽 명암은 실사 렌더링이 아닙니다.</p></div></>}
</aside>
@@ -57,3 +57,4 @@ export default function App() {
<footer><span>광터지하차도 BLOCK7 · 도면 검토용</span><span>기준 단위 m · 표고 EL · 미확정 형상은 분석 기록에서 관리</span></footer>
</main>
}
+15
View File
@@ -17,3 +17,18 @@ export function exportModel(hidden) {
const visibleParts = data.parts.filter(p => !hidden.has(p.id))
return { ...data, parts: visibleParts, description: '단면 윤곽 3D 배치. 평면 위치·단면간 연결 미확정. 부재 체적 아님.', coordinateSystem: 'X=단면 횡방향(m), Y=EL(m), Z=단면 STA 차(m); 실제 평면 좌표 미확정' }
}
export function exportOBJ(hidden = new Set()) {
const rows = ['# 광터지하차도 BLOCK7 단면 윤곽 모델', '# 단위 m. X 횡방향 / Y EL / Z 단면 STA 차. 평면 배치와 연결 표면 미확정.']
let index = 1
for (const part of data.parts.filter(p => !hidden.has(p.id))) {
for (const section of [0, 1]) {
rows.push(`# ${part.name} / ${part.source}`, `o ${part.id.replace(/[^a-zA-Z0-9-]/g, '_')}-${section ? 'B' : 'A'}`)
const points = profileVertices(part, section)
points.forEach(([x, y, z]) => rows.push(`v ${x} ${y + 100} ${z + 10}`))
rows.push(`l ${points.map((_, i) => index + i).join(' ')}`)
index += points.length
}
}
return rows.join('\n') + '\n'
}