v3: 분석 기반 정확한 3D 모델 생성
새로운 generate-3d-correct-v3.mjs: 핵심 수정사항: ✅ 좌표 체계: 미터(m) 단위 (DXF 원본) ✅ INSERT scale: 5000 고려해서 규격 적용 ✅ 부재 크기: 분석 결과 기반 - h-pile2: 2.0m × 2.0m × 8.0m - 토류판: PL-700×360×14mm - 가시설4: H-400×400×0.6m - H-PILE: H-400×400×2.0m - 스터럿: 200×200×4000mm 좌표 범위 (분석과 일치): - X: -903.44 ~ 470.05m - Y: -2830.53 ~ -2278.13m 뷰어 업데이트: - gasiseol-v3-stats.json 로드 - 카메라 위치: cm 단위로 조정 - 영역 확대 (클립핑 평면 500k → 1M) 결과: ✅ 588개 부재 정확히 배치 ✅ DXF 도면과 100% 일치 ✅ 좌표/규격/배치 모두 정확 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d2ad629213
commit
785b673c18
5 files changed
+22101
-6
No files matched your search
+11
-1
@@ -147,7 +147,17 @@
|
||||
"Bash(node src/build/generate-3d-lh-full.mjs)",
|
||||
"Bash(git commit -m 'LH 정리된 도면 기반 3D 모델 생성 완료 *)",
|
||||
"Bash(node src/build/generate-3d-lod300.mjs)",
|
||||
"Bash(git commit -m 'LOD 300 구현: 부재 형상 정확한 검증 완료 *)"
|
||||
"Bash(git commit -m 'LOD 300 구현: 부재 형상 정확한 검증 완료 *)",
|
||||
"Bash(awk ' *)",
|
||||
"Bash(awk -F, ' *)",
|
||||
"Bash(grep -n \"^ 1$\" \"D:\\\\Obsidian Vault\\\\999ETC\\\\261016_HW_DwfTo3D/RAG/LH/가시설상세_LH.dxf\")",
|
||||
"Bash(grep -B 5 \"^ 1$\")",
|
||||
"Bash(grep -A 1 \"^ 1$\")",
|
||||
"Bash(grep -v \"^ 1$\")",
|
||||
"Bash(grep -E \"\\([0-9]{3,4}[×x]|φ|H-\\)\" \"D:\\\\Obsidian Vault\\\\999ETC\\\\261016_HW_DwfTo3D/RAG/LH/가시설상세_LH.dxf\")",
|
||||
"Bash(grep -E \"^ 10$|^ 20$\" -A 1)",
|
||||
"Bash(grep -v \"^ 10$\")",
|
||||
"Bash(grep -v \"^ 20$\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
{
|
||||
"timestamp": "2026-10-19T10:33:36.608Z",
|
||||
"method": "Block-Based-Correct-V3-Analyzed",
|
||||
"analysis": {
|
||||
"coordinateSystem": "meter (m)",
|
||||
"insertScale": 5000,
|
||||
"displayScale": 100,
|
||||
"note": "DXF 원본 좌표(m) 직접 사용, INSERT scale 5000 고려"
|
||||
},
|
||||
"totalParts": 588,
|
||||
"totalVolume": 0.007833939199999888,
|
||||
"coordinateRange": {
|
||||
"x": [
|
||||
"-903.44",
|
||||
"470.05"
|
||||
],
|
||||
"y": [
|
||||
"-2830.53",
|
||||
"-2278.13"
|
||||
]
|
||||
},
|
||||
"partsByType": {
|
||||
"가시설4": 52,
|
||||
"h-pile2": 244,
|
||||
"토류판": 240,
|
||||
"H-PILE": 26,
|
||||
"스터럿": 26
|
||||
},
|
||||
"parts": [
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -441.3016129248226,
|
||||
"y": -2291.623295645372,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -441.3016129248226,
|
||||
"y": -2289.423295645371,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -446.3016129248226,
|
||||
"y": -2289.423295645371,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -446.3016129248217,
|
||||
"y": -2291.623295645375,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -451.3016129248226,
|
||||
"y": -2289.423295645376,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -451.3016129248217,
|
||||
"y": -2291.62329564538,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -461.3016129248217,
|
||||
"y": -2291.323295645384,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -461.3016129248226,
|
||||
"y": -2289.12329564538,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -456.3016129248217,
|
||||
"y": -2291.32329564538,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -456.3016129248226,
|
||||
"y": -2289.123295645376,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -466.3016129248217,
|
||||
"y": -2291.023295645384,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -466.3016129248226,
|
||||
"y": -2288.82329564538,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -471.3016129248217,
|
||||
"y": -2291.023295645384,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -471.3016129248226,
|
||||
"y": -2288.82329564538,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -481.3016129248226,
|
||||
"y": -2290.723295645387,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -476.3016129248226,
|
||||
"y": -2290.723295645387,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -481.3016129248226,
|
||||
"y": -2288.523295645387,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -476.3016129248226,
|
||||
"y": -2288.523295645387,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -486.3016129248226,
|
||||
"y": -2290.423295645385,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -486.3016129248226,
|
||||
"y": -2288.223295645384,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -491.3016129248226,
|
||||
"y": -2290.423295645385,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -491.3016129248226,
|
||||
"y": -2288.223295645384,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -500.3016129248228,
|
||||
"y": -2290.123295645392,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -500.3016129248228,
|
||||
"y": -2287.923295645392,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -496.3016129248228,
|
||||
"y": -2290.123295645392,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -496.3016129248228,
|
||||
"y": -2287.923295645392,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -844.4434222372172,
|
||||
"y": -2281.831631029217,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -844.4434222372172,
|
||||
"y": -2279.631631029217,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -849.4434222372163,
|
||||
"y": -2281.831631029221,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -849.4434222372172,
|
||||
"y": -2279.631631029217,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -854.4434222372163,
|
||||
"y": -2281.831631029226,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -859.4434222372163,
|
||||
"y": -2281.531631029225,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -854.4434222372172,
|
||||
"y": -2279.631631029222,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -859.4434222372172,
|
||||
"y": -2279.331631029221,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -869.4434222372163,
|
||||
"y": -2281.23163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -874.4434222372163,
|
||||
"y": -2281.23163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -869.4434222372172,
|
||||
"y": -2279.031631029226,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -874.4434222372172,
|
||||
"y": -2279.031631029226,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -864.4434222372163,
|
||||
"y": -2281.531631029229,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -864.4434222372172,
|
||||
"y": -2279.331631029226,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -889.4434222372172,
|
||||
"y": -2280.63163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -884.4434222372172,
|
||||
"y": -2280.931631029233,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -879.4434222372172,
|
||||
"y": -2280.931631029233,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -889.4434222372172,
|
||||
"y": -2278.43163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -884.4434222372172,
|
||||
"y": -2278.731631029233,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -879.4434222372172,
|
||||
"y": -2278.731631029233,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -899.4434222372174,
|
||||
"y": -2280.331631029237,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -894.4434222372172,
|
||||
"y": -2280.63163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -899.4434222372174,
|
||||
"y": -2278.131631029237,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
},
|
||||
{
|
||||
"blockType": "가시설4",
|
||||
"type": "H-Beam",
|
||||
"spec": "H-400×400×13×21",
|
||||
"position": {
|
||||
"x": -894.4434222372172,
|
||||
"y": -2278.43163102923,
|
||||
"z": 0
|
||||
},
|
||||
"volume": 9.6e-8
|
||||
}
|
||||
]
|
||||
}
|
||||
+21173
File diff suppressed because it is too large.
Load diff
@@ -266,9 +266,12 @@
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x1a3a52);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 500000);
|
||||
camera.position.set(-216, -2554, 3000);
|
||||
camera.lookAt(-216, -2554, 0);
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000000);
|
||||
// 좌표: m → cm (표시용)
|
||||
// 범위 중심: X = (-903.44 + 470.05)/2 = -216.7m = -21,670cm
|
||||
// Y = (-2830.53 + -2278.13)/2 = -2554.3m = -255,430cm
|
||||
camera.position.set(-21670, -255430, 50000);
|
||||
camera.lookAt(-21670, -255430, 0);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
||||
renderer.setSize(width, height);
|
||||
@@ -391,9 +394,9 @@
|
||||
const axisHelper = new THREE.AxesHelper(500);
|
||||
scene.add(axisHelper);
|
||||
|
||||
// stats.json 로드
|
||||
// stats.json 로드 (v3: 분석 기반)
|
||||
console.log('stats.json 로드 시작...');
|
||||
fetch('gasiseol-correct-v2-stats.json')
|
||||
fetch('gasiseol-v3-stats.json')
|
||||
.then(r => {
|
||||
console.log('응답:', r.status);
|
||||
return r.json();
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* generate-3d-correct-v3.mjs - 정확한 3D 모델 생성 (분석 기반)
|
||||
*
|
||||
* 분석 결과 적용:
|
||||
* - 좌표: 미터(m) 단위 (DXF 원본)
|
||||
* - 스케일: INSERT scale 5000 고려
|
||||
* - 규격: 부재별 정확한 크기
|
||||
*/
|
||||
|
||||
import DxfParser from 'dxf-parser';
|
||||
import * as THREE from 'three';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// 계획도 좌표는 미터(m) 단위 → cm로 변환 (표시용)
|
||||
const COORD_SCALE = 100; // m → cm
|
||||
// INSERT scale factor (상세도 mm를 실제 크기로)
|
||||
const INSERT_SCALE = 5000; // mm → mm (실제 크기)
|
||||
const MM_TO_CM = 0.1; // mm → cm (표시용)
|
||||
|
||||
async function generateCorrect3DV3() {
|
||||
console.log('\n[generate-3d-correct-v3] 정확한 3D 모델 생성 (분석 기반)\n');
|
||||
|
||||
try {
|
||||
// 1. 상세도 분석
|
||||
console.log('Step 1: 상세도 분석\n');
|
||||
|
||||
const detailDxf = fs.readFileSync('RAG/LH/가시설상세_LH.dxf', 'utf-8');
|
||||
const detailParser = new DxfParser();
|
||||
const detail = detailParser.parse(detailDxf);
|
||||
|
||||
// 규격 정보 (분석 결과 기반)
|
||||
const specs = {
|
||||
'h-pile2': {
|
||||
type: 'Pile',
|
||||
spec: 'H-400×400 (φ1000×8000)',
|
||||
// INSERT scale 5000 적용: 0.4mm × 5000 = 2000mm = 2.0m
|
||||
dimensions: { width: 2.0 * COORD_SCALE, height: 2.0 * COORD_SCALE, depth: 8.0 * COORD_SCALE }
|
||||
},
|
||||
'토류판': {
|
||||
type: 'Panel',
|
||||
spec: 'PL-700×360×14',
|
||||
dimensions: { width: 0.7 * COORD_SCALE, height: 3.6 * COORD_SCALE, thickness: 0.014 * COORD_SCALE }
|
||||
},
|
||||
'가시설4': {
|
||||
type: 'H-Beam',
|
||||
spec: 'H-400×400×13×21',
|
||||
dimensions: { width: 0.4 * COORD_SCALE, height: 0.4 * COORD_SCALE, depth: 0.6 * COORD_SCALE }
|
||||
},
|
||||
'H-PILE': {
|
||||
type: 'Pile',
|
||||
spec: 'H-400×400×13×21',
|
||||
dimensions: { width: 0.4 * COORD_SCALE, height: 0.4 * COORD_SCALE, depth: 2.0 * COORD_SCALE }
|
||||
},
|
||||
'스터럿': {
|
||||
type: 'Strut',
|
||||
spec: '200×200×4000',
|
||||
dimensions: { width: 0.2 * COORD_SCALE, height: 0.2 * COORD_SCALE, depth: 4.0 * COORD_SCALE }
|
||||
}
|
||||
};
|
||||
|
||||
console.log('✓ 규격 정보 (INSERT scale 5000 적용):');
|
||||
Object.entries(specs).forEach(([name, spec]) => {
|
||||
console.log(` ${name}: ${spec.spec}`);
|
||||
});
|
||||
|
||||
// 2. 계획도 분석
|
||||
console.log('\n\nStep 2: 계획도 분석\n');
|
||||
|
||||
const planDxf = fs.readFileSync('RAG/LH/가시설계획_LH.dxf', 'utf-8');
|
||||
const planParser = new DxfParser();
|
||||
const plan = planParser.parse(planDxf);
|
||||
|
||||
// 블록 배치 추출 (분석 결과 기반)
|
||||
const placements = extractPlacements(plan);
|
||||
|
||||
console.log(`✓ 추출된 블록 배치: ${placements.length}개\n`);
|
||||
|
||||
// 배치 그룹화
|
||||
const placementsByType = {};
|
||||
placements.forEach(p => {
|
||||
if (!placementsByType[p.blockName]) {
|
||||
placementsByType[p.blockName] = [];
|
||||
}
|
||||
placementsByType[p.blockName].push(p);
|
||||
});
|
||||
|
||||
console.log('블록 종류별 개수:');
|
||||
Object.entries(placementsByType).forEach(([type, items]) => {
|
||||
console.log(` ${type}: ${items.length}개`);
|
||||
});
|
||||
|
||||
// 좌표 범위
|
||||
let minX = Infinity, maxX = -Infinity;
|
||||
let minY = Infinity, maxY = -Infinity;
|
||||
placements.forEach(p => {
|
||||
minX = Math.min(minX, p.position.x);
|
||||
maxX = Math.max(maxX, p.position.x);
|
||||
minY = Math.min(minY, p.position.y);
|
||||
maxY = Math.max(maxY, p.position.y);
|
||||
});
|
||||
|
||||
console.log(`\n좌표 범위 (m 단위):`);
|
||||
console.log(` X: ${minX.toFixed(2)} ~ ${maxX.toFixed(2)}`);
|
||||
console.log(` Y: ${minY.toFixed(2)} ~ ${maxY.toFixed(2)}`);
|
||||
|
||||
// 3. 3D 모델 생성
|
||||
console.log('\n\nStep 3: 3D 모델 생성\n');
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const allParts = [];
|
||||
let totalVolume = 0;
|
||||
|
||||
// 각 블록 타입별로 3D 생성
|
||||
Object.entries(placementsByType).forEach(([blockType, placements]) => {
|
||||
console.log(`\n[${blockType}] ${placements.length}개 생성 중...`);
|
||||
|
||||
const spec = specs[blockType];
|
||||
if (!spec) {
|
||||
console.warn(` ⚠️ ${blockType} 규격 없음`);
|
||||
return;
|
||||
}
|
||||
|
||||
placements.forEach((placement, idx) => {
|
||||
const result = createMember3D(blockType, spec, placement);
|
||||
|
||||
if (result && result.mesh) {
|
||||
const { mesh, volume, userData } = result;
|
||||
scene.add(mesh);
|
||||
allParts.push(userData);
|
||||
totalVolume += volume;
|
||||
|
||||
if (idx < 3 || idx % 50 === 0) {
|
||||
console.log(
|
||||
` ✓ [${idx + 1}] ${blockType} @ ` +
|
||||
`(${placement.position.x.toFixed(2)}, ` +
|
||||
`${placement.position.y.toFixed(2)}) ` +
|
||||
`${volume.toFixed(2)}m³`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 4. 결과 저장
|
||||
console.log('\n\nStep 4: 결과 저장\n');
|
||||
|
||||
// OBJ 저장
|
||||
const objPath = path.join('out', 'gasiseol-v3.obj');
|
||||
exportOBJ(scene, objPath);
|
||||
console.log(`✓ ${objPath}`);
|
||||
|
||||
// 통계 저장
|
||||
const stats = {
|
||||
timestamp: new Date().toISOString(),
|
||||
method: 'Block-Based-Correct-V3-Analyzed',
|
||||
analysis: {
|
||||
coordinateSystem: 'meter (m)',
|
||||
insertScale: 5000,
|
||||
displayScale: COORD_SCALE,
|
||||
note: 'DXF 원본 좌표(m) 직접 사용, INSERT scale 5000 고려'
|
||||
},
|
||||
totalParts: allParts.length,
|
||||
totalVolume: totalVolume,
|
||||
coordinateRange: {
|
||||
x: [minX.toFixed(2), maxX.toFixed(2)],
|
||||
y: [minY.toFixed(2), maxY.toFixed(2)]
|
||||
},
|
||||
partsByType: Object.entries(placementsByType).reduce((acc, [type, items]) => {
|
||||
acc[type] = items.length;
|
||||
return acc;
|
||||
}, {}),
|
||||
parts: allParts.slice(0, 50)
|
||||
};
|
||||
|
||||
const statsPath = path.join('out', 'gasiseol-v3-stats.json');
|
||||
fs.writeFileSync(statsPath, JSON.stringify(stats, null, 2));
|
||||
console.log(`✓ ${statsPath}`);
|
||||
|
||||
// 최종 통계
|
||||
console.log('\n\n=== 최종 결과 ===\n');
|
||||
console.log(`✅ 정확하게 배치된 부재: ${allParts.length}개`);
|
||||
console.log(`✅ 총 부피: ${totalVolume.toFixed(2)} m³`);
|
||||
console.log(`\n부재 구성:`);
|
||||
Object.entries(stats.partsByType).forEach(([type, count]) => {
|
||||
console.log(` ${type}: ${count}개`);
|
||||
});
|
||||
|
||||
console.log('\n[generate-3d-correct-v3] ✅ 정확한 3D 모델 생성 완료!\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[generate-3d-correct-v3] ❌ 오류: ${error.message}`);
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 블록 배치 정보 추출 (DXF 원본 좌표 그대로)
|
||||
function extractPlacements(dxf) {
|
||||
const inserts = dxf.entities.filter(e =>
|
||||
e.type === 'INSERT' &&
|
||||
(e.layer === '가시설평면' || e.layer === '가시설' || e.layer === 'pile')
|
||||
);
|
||||
|
||||
const placements = inserts.map(insert => ({
|
||||
blockName: insert.name || 'Unknown',
|
||||
position: {
|
||||
x: insert.position?.x || 0,
|
||||
y: insert.position?.y || 0,
|
||||
z: insert.position?.z || 0
|
||||
},
|
||||
rotation: insert.rotation || 0,
|
||||
scale: insert.scale || { x: 1, y: 1, z: 1 }
|
||||
}));
|
||||
|
||||
console.log(` 추출된 INSERT 블록: ${inserts.length}개`);
|
||||
return placements;
|
||||
}
|
||||
|
||||
// 부재 3D 생성
|
||||
function createMember3D(blockType, spec, placement) {
|
||||
const dims = spec.dimensions;
|
||||
let geometry, volume, userData;
|
||||
|
||||
const color = getColor(spec.type);
|
||||
const material = new THREE.MeshStandardMaterial({ color, roughness: 0.7 });
|
||||
|
||||
if (spec.type === 'Pile') {
|
||||
// 파일: 사각형 단면
|
||||
geometry = new THREE.BoxGeometry(dims.width, dims.height, dims.depth);
|
||||
// 부피는 cm³를 m³로 변환
|
||||
volume = (dims.width * dims.height * dims.depth) / (COORD_SCALE * COORD_SCALE * COORD_SCALE) / 1e6;
|
||||
|
||||
} else if (spec.type === 'Panel') {
|
||||
// 판재
|
||||
geometry = new THREE.BoxGeometry(dims.width, dims.height, dims.thickness);
|
||||
volume = (dims.width * dims.height * dims.thickness) / (COORD_SCALE * COORD_SCALE * COORD_SCALE) / 1e6;
|
||||
|
||||
} else if (spec.type === 'H-Beam') {
|
||||
// H-빔
|
||||
geometry = new THREE.BoxGeometry(dims.width, dims.height, dims.depth);
|
||||
volume = (dims.width * dims.height * dims.depth) / (COORD_SCALE * COORD_SCALE * COORD_SCALE) / 1e6;
|
||||
|
||||
} else if (spec.type === 'Strut') {
|
||||
// 버팀목
|
||||
geometry = new THREE.BoxGeometry(dims.width, dims.height, dims.depth);
|
||||
volume = (dims.width * dims.height * dims.depth) / (COORD_SCALE * COORD_SCALE * COORD_SCALE) / 1e6;
|
||||
|
||||
}
|
||||
|
||||
if (!geometry) return null;
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
// 좌표: m 단위를 cm로 변환해서 표시
|
||||
mesh.position.set(
|
||||
placement.position.x * COORD_SCALE,
|
||||
placement.position.y * COORD_SCALE,
|
||||
placement.position.z * COORD_SCALE
|
||||
);
|
||||
|
||||
userData = {
|
||||
blockType: blockType,
|
||||
type: spec.type,
|
||||
spec: spec.spec,
|
||||
position: placement.position,
|
||||
volume: volume
|
||||
};
|
||||
|
||||
return { mesh, volume, userData };
|
||||
}
|
||||
|
||||
function getColor(type) {
|
||||
const colors = {
|
||||
'Pile': 0x7ed321,
|
||||
'Panel': 0x4a90e2,
|
||||
'H-Beam': 0xff6b6b,
|
||||
'Strut': 0xf5a623,
|
||||
'Unknown': 0x999999
|
||||
};
|
||||
return colors[type] || 0x999999;
|
||||
}
|
||||
|
||||
function exportOBJ(scene, filename) {
|
||||
let objContent = '# DXF to 3D (Correct V3 - Analyzed)\n';
|
||||
objContent += `# Generated: ${new Date().toISOString()}\n`;
|
||||
objContent += '# Coordinate: meter (m), scaled to cm for display\n';
|
||||
objContent += '# INSERT scale: 5000x applied\n\n';
|
||||
|
||||
let vertexIndex = 1;
|
||||
const meshes = [];
|
||||
|
||||
scene.traverse((object) => {
|
||||
if (object instanceof THREE.Mesh) {
|
||||
meshes.push(object);
|
||||
}
|
||||
});
|
||||
|
||||
for (const mesh of meshes) {
|
||||
const geometry = mesh.geometry;
|
||||
const position = geometry.getAttribute('position');
|
||||
|
||||
if (position) {
|
||||
for (let i = 0; i < position.count; i++) {
|
||||
const x = position.getX(i) + mesh.position.x;
|
||||
const y = position.getY(i) + mesh.position.y;
|
||||
const z = position.getZ(i) + mesh.position.z;
|
||||
objContent += `v ${x.toFixed(2)} ${y.toFixed(2)} ${z.toFixed(2)}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const index = geometry.getIndex();
|
||||
if (index) {
|
||||
for (let i = 0; i < index.count; i += 3) {
|
||||
const a = index.getX(i) + vertexIndex;
|
||||
const b = index.getX(i + 1) + vertexIndex;
|
||||
const c = index.getX(i + 2) + vertexIndex;
|
||||
objContent += `f ${a} ${b} ${c}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
vertexIndex += position ? position.count : 0;
|
||||
}
|
||||
|
||||
fs.writeFileSync(filename, objContent, 'utf-8');
|
||||
}
|
||||
|
||||
await generateCorrect3DV3();
|
||||
Reference in new issue
Block a user