평면도와 상부평면도 모델을 하나로 통합

This commit is contained in:
김지영 committed 2026-10-20 13:20:43 +09:00
1 parent 9d11b4ed62
commit e473a7caff
10 files changed
+54 -8799

No files matched your search

File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 537 KiB

After

Width:  |  Height:  |  Size: 270 KiB

+8 -8
View File
@@ -4,7 +4,7 @@
| 도면 | 자료 위치 | 읽은 내용 | 상태 |
|---|---|---|---|
| 평면도 | `public/drawings/광터지하차도_BLOCK8.dxf`, `CS-CNTL` 원호와 `CS-CONC-MAJR` 끝 구조선 | BLOCK8 표기 평면 중심선 2개, 반경 1,700m 원호, 원호와 끝 구조선의 교점 | 직접 좌표·레이어 대조 |
| 평면도 | `public/drawings/광터지하차도_BLOCK8.dxf`, `CS-CNTL` 원호와 `CS-CONC-MAJR` 끝 구조선 | 평면도와 상부평면도에 같은 형상으로 반복 표기된 중심선. 단일 모델 기준 원호 1개, 반경 1,700m, 원호와 끝 구조선의 교점 | 직접 좌표·레이어 대조 |
| 상부 평면 및 종단 검토 영역 | 같은 DXF, BLOCK8 주변 측점·EL 주석 | 모델 시종점 측점 319.010/338.990, 길이 19.980m | 측점은 도면값, 길이는 교점 간 계산값 |
| 단면 A-A | 같은 DXF, `STA. 0K+319.000` 주석이 있는 단면 영역 | 단면 구조선 114개, EL 표기와 폭 분할 | 직접 추출 및 대조 |
| 단면 B-B | 같은 DXF, `STA. 0K+339.000` 주석이 있는 단면 영역 | 단면 구조선 114개, 종점 EL·폭 분할 | 직접 추출 및 대조 |
@@ -12,7 +12,7 @@
| 측구 상세 | 전체 일반도 DXF의 별도 상세 영역 | BLOCK8 본체 단면과 측구 세부의 연결 표기를 확인하지 못함 | 별도 모델 반영 보류 |
| 제원·특기사항 및 구조도 | 사용자 제공 폴더의 DWG | 파일 존재 확인. DWG 직접 렌더링/교차검토는 이 실행 환경에서 수행하지 못함 | 후속 검증 필요 |
두 단면은 DXF의 동일 구조 윤곽 영역을 측점 주석(`319.000`, `339.000`)과 연결했다. 각 단면의 `CS-CNTL` 중심 표시 교차점 EL은 각각 102.094m와 101.517m이며, 같은 단면 영역의 EL 주석과 일치한다. 벽체 상단 EL 107.215m/107.857m도 종단 검토 영역에 반복 표기되어 각 단면과 맞는다. 각 측점 단면의 폭 분할은 양쪽 단부벽·중앙벽·두 통로 폭으로 이어지며, 평면의 두 `BLOCK8` 중심선과 함께 모델의 두 배치 객체로 구성했다. 두 평면 배치에 대표 단면을 공통 적용한 관계는 구조도 대조가 끝나지 않아 `확인 필요`로 표시한다.
두 단면은 DXF의 동일 구조 윤곽 영역을 측점 주석(`319.000`, `339.000`)과 연결했다. 각 단면의 `CS-CNTL` 중심 표시 교차점 EL은 각각 102.094m와 101.517m이며, 같은 단면 영역의 EL 주석과 일치한다. 벽체 상단 EL 107.215m/107.857m도 종단 검토 영역에 반복 표기되어 각 단면과 맞는다. 각 측점 단면의 폭 분할은 양쪽 단부벽·중앙벽·두 통로 폭으로 이어진다. 평면도와 상부평면도에 보이는 두 중심선 표현은 도면 배치상 서로 다른 위치에 그려진 같은 구조물의 표현으로 해석했다. 따라서 단면 A-A/B-B 및 종단 기준과 일치하는 하나의 중심선을 모델 기준으로 채택했고, 도면상 35m의 배치 간격은 실제 구조물 간격이 아닌 도면 표현 간 거리로 보아 모델에 적용하지 않았다.
## 축척 및 좌표 단위 확인
@@ -29,8 +29,8 @@
| 모델 시점 | STA. 0K+319.010 | 평면도 BLOCK8 구조 끝단과 중심선 교점 부근 주석 | 도면 명시값 |
| 모델 종점 | STA. 0K+338.990 | 반대쪽 구조 끝단과 중심선 교점 부근 주석 | 도면 명시값 |
| 중심선 반경 | 1,700m | 평면도 `CS-CNTL` ARC 반경 1,700,000 CAD 단위 | 좌표 단위 검산 후 환산값 |
| 평면 중심선 길이 | 19.980m, 각 배치 | ARC와 `CS-CONC-MAJR` 양 끝선 교점 사이 원호 길이. 교점 각도 약 269.663303°~270.336697° | 계산으로 도출한 값; 측점 차와 일치 |
| 두 평면 중심선 간 CAD 배치 차이 | 35.000m | 두 원호 중심 X 좌표 차 35,000 CAD 단위 | 계산으로 도출; 실제 독립 구조물 관계는 확인 필요 |
| 단일 모델 중심선 길이 | 19.980m | 기준 ARC와 `CS-CONC-MAJR` 양 끝선 교점 사이 원호 길이. 교점 각도 약 269.663303°~270.336697° | 계산으로 도출한 값; 측점 차와 일치 |
| 도면 표현 간 CAD 배치 차이 | 35.000m | 평면도/상부평면도에 배치된 두 중심선 표기의 원호 중심 X 좌표 차 | 도면 배치 좌표 차; 모델 간격으로 사용하지 않음 |
| 단면 A-A 측점 | STA. 0K+319.000 | 단면 A 표기 | 도면 명시값 |
| 단면 A-A 벽체 상단 EL | 107.215 | 상단 EL 주석 및 상단 구조선 | 도면 명시값 |
| 단면 A-A 기초 하단 EL | 100.877 | 하단 EL 주석 및 기초 구조선 | 도면 명시값 |
@@ -54,7 +54,7 @@
1. 단면 외곽 구조선 폭은 20.500m인데 표기된 주 통로/본체 폭은 20.300m다. 좌우 0.100m씩 추가되는 외곽 선의 용도는 구조도 DWG와 확인해야 한다. 모델에는 두 값을 임의로 하나로 합치지 않고 원 구조선을 그대로 보존했다.
2. 구조선 최저 외곽점은 표기된 기초 하단 EL보다 약 0.100m 낮게 계산된다(A: 100.777m, B: 100.292m). 기초 상세 또는 버림층 여부가 확인될 때까지 외곽 구조선 그대로 표시하고 차이를 남긴다.
3. 평면도에서 동일 `(BLOCK8)` 표기가 있는 중심선 2개를 찾았고 중심 간 도면 좌표 차는 35.000m다. 두 중심선이 독립 구조물인지, 도면상 중복/배치 복제인지 구조도와 종평면도로 확인해야 한다. 웹 뷰어에서 서로 독립적으로 숨김 처리할 수 있게 했다.
3. 평면도와 상부평면도에 같은 구조물 중심선이 도면상 35.000m 떨어져 반복 표현되어 있다. 이는 도면 뷰 간 배치 차이로 해석하여 3D에는 한 번만 모델링했다. 중심선 형상과 측점은 종단·A-A/B-B 단면 기준으로 대응시켰다.
4. 종단면의 상단 벽체 EL과 두 단면 상단 EL은 현재 일반도 DXF의 구조선/EL 위치로 읽었으나, 제원 DWG와 별도 구조도에서 재확인하지 못했다.
5. 측구 상세도 기호와 본체 구조선의 실제 접합 위치, 부재 두께/재료, 닫힌 체적 경계는 미검증이다.
@@ -63,15 +63,15 @@
- 생성 데이터: `src/model/block8.json`. 각 부재에 A/B 단면점, 원본 레이어와 HANDLE을 기록한다.
- 추출 스크립트: `scripts/extract_block8_model.py`. 표준 Python만으로 DXF 단면 선, 원호, INSERT 내부 레이어를 재추출한다.
- 화면: `src/ModelViewer.jsx`와 `src/App.jsx`. 끌어서 회전, 휠 확대/축소, Shift/오른쪽 끌기로 이동, 전체·평면·정면·측면·A-A·B-B 시점을 제공한다.
- 객체는 2개 평면 중심선에 각각 154개씩 구성했다(단면 구조선 114개와 삽입 블록 윤곽 40개). 원본선 단위 선택·숨김과 MAJR/MINR/블록 윤곽 그룹 숨김을 지원한다.
- 객체는 단일 중심선 모델에 구조선 114개와 삽입 블록 윤곽 40개, 총 154개 객체를 구성했다(단면 구조선 114개와 삽입 블록 윤곽 40개). 원본선 단위 선택·숨김과 MAJR/MINR/블록 윤곽 그룹 숨김을 지원한다.
- 화면의 면은 검토용 열린 표면 모델이다. 구조선 사이를 연결했으며 모든 두께/접합을 확정한 닫힌 체적이나 실사 렌더링은 아니다.
- 실행: `npm run dev`. 분석 파일과 작업 로그는 화면 상단 링크에서 연다.
## 검증 결과
- 추출 스크립트 실행 결과: 중심선 2개, 각 길이 19.980m, 단면 구조선 MAJR 108개와 MINR 6개, 내부 블록 객체를 양쪽 단면에서 대응했다.
- 추출 스크립트 실행 결과: 모델 중심선 1개, 길이 19.980m; 도면에 표현된 대응 중심선 2개 형상은 기준 대조에만 사용, 단면 구조선 MAJR 108개와 MINR 6개, 내부 블록 객체를 양쪽 단면에서 대응했다.
- `npm run build`: 통과.
- `npm run lint`: 미사용 함수 경고를 제거한 뒤 최종 재실행 예정.
- `npm run lint`: 통과, 경고 없음.
- `git diff --check`: 통과.
- 미검증: 원본 DWG에서 제원·특기사항, 구조 상세, 2개 중심선의 공간상 관계 및 측구 접합부를 CAD/PDF로 교차 대조하지 못했다.
- 미검증: 원본 DWG에서 제원·특기사항, 구조 상세, 평면/상부평면도 간 상세 배치 대응의 원본 DWG 교차검토 및 측구 접합부를 CAD/PDF로 교차 대조하지 못했다.
File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 537 KiB

After

Width:  |  Height:  |  Size: 270 KiB

+8 -8
View File
@@ -4,7 +4,7 @@
| 도면 | 자료 위치 | 읽은 내용 | 상태 |
|---|---|---|---|
| 평면도 | `public/drawings/광터지하차도_BLOCK8.dxf`, `CS-CNTL` 원호와 `CS-CONC-MAJR` 끝 구조선 | BLOCK8 표기 평면 중심선 2개, 반경 1,700m 원호, 원호와 끝 구조선의 교점 | 직접 좌표·레이어 대조 |
| 평면도 | `public/drawings/광터지하차도_BLOCK8.dxf`, `CS-CNTL` 원호와 `CS-CONC-MAJR` 끝 구조선 | 평면도와 상부평면도에 같은 형상으로 반복 표기된 중심선. 단일 모델 기준 원호 1개, 반경 1,700m, 원호와 끝 구조선의 교점 | 직접 좌표·레이어 대조 |
| 상부 평면 및 종단 검토 영역 | 같은 DXF, BLOCK8 주변 측점·EL 주석 | 모델 시종점 측점 319.010/338.990, 길이 19.980m | 측점은 도면값, 길이는 교점 간 계산값 |
| 단면 A-A | 같은 DXF, `STA. 0K+319.000` 주석이 있는 단면 영역 | 단면 구조선 114개, EL 표기와 폭 분할 | 직접 추출 및 대조 |
| 단면 B-B | 같은 DXF, `STA. 0K+339.000` 주석이 있는 단면 영역 | 단면 구조선 114개, 종점 EL·폭 분할 | 직접 추출 및 대조 |
@@ -12,7 +12,7 @@
| 측구 상세 | 전체 일반도 DXF의 별도 상세 영역 | BLOCK8 본체 단면과 측구 세부의 연결 표기를 확인하지 못함 | 별도 모델 반영 보류 |
| 제원·특기사항 및 구조도 | 사용자 제공 폴더의 DWG | 파일 존재 확인. DWG 직접 렌더링/교차검토는 이 실행 환경에서 수행하지 못함 | 후속 검증 필요 |
두 단면은 DXF의 동일 구조 윤곽 영역을 측점 주석(`319.000`, `339.000`)과 연결했다. 각 단면의 `CS-CNTL` 중심 표시 교차점 EL은 각각 102.094m와 101.517m이며, 같은 단면 영역의 EL 주석과 일치한다. 벽체 상단 EL 107.215m/107.857m도 종단 검토 영역에 반복 표기되어 각 단면과 맞는다. 각 측점 단면의 폭 분할은 양쪽 단부벽·중앙벽·두 통로 폭으로 이어지며, 평면의 두 `BLOCK8` 중심선과 함께 모델의 두 배치 객체로 구성했다. 두 평면 배치에 대표 단면을 공통 적용한 관계는 구조도 대조가 끝나지 않아 `확인 필요`로 표시한다.
두 단면은 DXF의 동일 구조 윤곽 영역을 측점 주석(`319.000`, `339.000`)과 연결했다. 각 단면의 `CS-CNTL` 중심 표시 교차점 EL은 각각 102.094m와 101.517m이며, 같은 단면 영역의 EL 주석과 일치한다. 벽체 상단 EL 107.215m/107.857m도 종단 검토 영역에 반복 표기되어 각 단면과 맞는다. 각 측점 단면의 폭 분할은 양쪽 단부벽·중앙벽·두 통로 폭으로 이어진다. 평면도와 상부평면도에 보이는 두 중심선 표현은 도면 배치상 서로 다른 위치에 그려진 같은 구조물의 표현으로 해석했다. 따라서 단면 A-A/B-B 및 종단 기준과 일치하는 하나의 중심선을 모델 기준으로 채택했고, 도면상 35m의 배치 간격은 실제 구조물 간격이 아닌 도면 표현 간 거리로 보아 모델에 적용하지 않았다.
## 축척 및 좌표 단위 확인
@@ -29,8 +29,8 @@
| 모델 시점 | STA. 0K+319.010 | 평면도 BLOCK8 구조 끝단과 중심선 교점 부근 주석 | 도면 명시값 |
| 모델 종점 | STA. 0K+338.990 | 반대쪽 구조 끝단과 중심선 교점 부근 주석 | 도면 명시값 |
| 중심선 반경 | 1,700m | 평면도 `CS-CNTL` ARC 반경 1,700,000 CAD 단위 | 좌표 단위 검산 후 환산값 |
| 평면 중심선 길이 | 19.980m, 각 배치 | ARC와 `CS-CONC-MAJR` 양 끝선 교점 사이 원호 길이. 교점 각도 약 269.663303°~270.336697° | 계산으로 도출한 값; 측점 차와 일치 |
| 두 평면 중심선 간 CAD 배치 차이 | 35.000m | 두 원호 중심 X 좌표 차 35,000 CAD 단위 | 계산으로 도출; 실제 독립 구조물 관계는 확인 필요 |
| 단일 모델 중심선 길이 | 19.980m | 기준 ARC와 `CS-CONC-MAJR` 양 끝선 교점 사이 원호 길이. 교점 각도 약 269.663303°~270.336697° | 계산으로 도출한 값; 측점 차와 일치 |
| 도면 표현 간 CAD 배치 차이 | 35.000m | 평면도/상부평면도에 배치된 두 중심선 표기의 원호 중심 X 좌표 차 | 도면 배치 좌표 차; 모델 간격으로 사용하지 않음 |
| 단면 A-A 측점 | STA. 0K+319.000 | 단면 A 표기 | 도면 명시값 |
| 단면 A-A 벽체 상단 EL | 107.215 | 상단 EL 주석 및 상단 구조선 | 도면 명시값 |
| 단면 A-A 기초 하단 EL | 100.877 | 하단 EL 주석 및 기초 구조선 | 도면 명시값 |
@@ -54,7 +54,7 @@
1. 단면 외곽 구조선 폭은 20.500m인데 표기된 주 통로/본체 폭은 20.300m다. 좌우 0.100m씩 추가되는 외곽 선의 용도는 구조도 DWG와 확인해야 한다. 모델에는 두 값을 임의로 하나로 합치지 않고 원 구조선을 그대로 보존했다.
2. 구조선 최저 외곽점은 표기된 기초 하단 EL보다 약 0.100m 낮게 계산된다(A: 100.777m, B: 100.292m). 기초 상세 또는 버림층 여부가 확인될 때까지 외곽 구조선 그대로 표시하고 차이를 남긴다.
3. 평면도에서 동일 `(BLOCK8)` 표기가 있는 중심선 2개를 찾았고 중심 간 도면 좌표 차는 35.000m다. 두 중심선이 독립 구조물인지, 도면상 중복/배치 복제인지 구조도와 종평면도로 확인해야 한다. 웹 뷰어에서 서로 독립적으로 숨김 처리할 수 있게 했다.
3. 평면도와 상부평면도에 같은 구조물 중심선이 도면상 35.000m 떨어져 반복 표현되어 있다. 이는 도면 뷰 간 배치 차이로 해석하여 3D에는 한 번만 모델링했다. 중심선 형상과 측점은 종단·A-A/B-B 단면 기준으로 대응시켰다.
4. 종단면의 상단 벽체 EL과 두 단면 상단 EL은 현재 일반도 DXF의 구조선/EL 위치로 읽었으나, 제원 DWG와 별도 구조도에서 재확인하지 못했다.
5. 측구 상세도 기호와 본체 구조선의 실제 접합 위치, 부재 두께/재료, 닫힌 체적 경계는 미검증이다.
@@ -63,15 +63,15 @@
- 생성 데이터: `src/model/block8.json`. 각 부재에 A/B 단면점, 원본 레이어와 HANDLE을 기록한다.
- 추출 스크립트: `scripts/extract_block8_model.py`. 표준 Python만으로 DXF 단면 선, 원호, INSERT 내부 레이어를 재추출한다.
- 화면: `src/ModelViewer.jsx`와 `src/App.jsx`. 끌어서 회전, 휠 확대/축소, Shift/오른쪽 끌기로 이동, 전체·평면·정면·측면·A-A·B-B 시점을 제공한다.
- 객체는 2개 평면 중심선에 각각 154개씩 구성했다(단면 구조선 114개와 삽입 블록 윤곽 40개). 원본선 단위 선택·숨김과 MAJR/MINR/블록 윤곽 그룹 숨김을 지원한다.
- 객체는 단일 중심선 모델에 구조선 114개와 삽입 블록 윤곽 40개, 총 154개 객체를 구성했다(단면 구조선 114개와 삽입 블록 윤곽 40개). 원본선 단위 선택·숨김과 MAJR/MINR/블록 윤곽 그룹 숨김을 지원한다.
- 화면의 면은 검토용 열린 표면 모델이다. 구조선 사이를 연결했으며 모든 두께/접합을 확정한 닫힌 체적이나 실사 렌더링은 아니다.
- 실행: `npm run dev`. 분석 파일과 작업 로그는 화면 상단 링크에서 연다.
## 검증 결과
- 추출 스크립트 실행 결과: 중심선 2개, 각 길이 19.980m, 단면 구조선 MAJR 108개와 MINR 6개, 내부 블록 객체를 양쪽 단면에서 대응했다.
- 추출 스크립트 실행 결과: 모델 중심선 1개, 길이 19.980m; 도면에 표현된 대응 중심선 2개 형상은 기준 대조에만 사용, 단면 구조선 MAJR 108개와 MINR 6개, 내부 블록 객체를 양쪽 단면에서 대응했다.
- `npm run build`: 통과.
- `npm run lint`: 미사용 함수 경고를 제거한 뒤 최종 재실행 예정.
- `npm run lint`: 통과, 경고 없음.
- `git diff --check`: 통과.
- 미검증: 원본 DWG에서 제원·특기사항, 구조 상세, 2개 중심선의 공간상 관계 및 측구 접합부를 CAD/PDF로 교차 대조하지 못했다.
- 미검증: 원본 DWG에서 제원·특기사항, 구조 상세, 평면/상부평면도 간 상세 배치 대응의 원본 DWG 교차검토 및 측구 접합부를 CAD/PDF로 교차 대조하지 못했다.
+7 -1
View File
@@ -28,6 +28,12 @@
- 단면 A/B 구조선 114개씩( MAJR 108, MINR 6 ) 추출
- A$C2017021411375356 블록 내부 콘크리트 레이어 18선과 원호 2개를 좌우 단면에 반영
- 중심 기준 EL 102.094 → 101.517, 벽체 상단 EL 107.215 → 107.857, 폭 분할 20.300m 대조
- 두 평면 중심선에 각각 3D 연결 표면 모델 생성, 부재 선택/숨김 및 보기 방향 제공
- 평면도와 상부평면도를 같은 구조물로 해석해 단일 3D 연결 표면 모델 생성, 부재 선택/숨김 및 보기 방향 제공
- 구조도와 대조가 필요한 배치 관계·외곽 폭 차이·기초 최저선 차이는 검토 항목으로 유지
- 모델 미리보기 SVG와 갱신된 분석 문서 추가
6. 평면도/상부평면도 표현 중복 정리
- 두 도면의 중심선 표기를 서로 다른 구조물로 보던 해석을 수정
- 단일 중심선 기준 3D 모델 1개로 재생성 (154개 객체)
- 35.000m는 도면 뷰 배치 좌표 차이로 기록하고 모델 간격에서 제외
- 분석 문서, 웹 뷰어, 모델 미리보기 갱신
+4 -2
View File
@@ -212,7 +212,7 @@ def alignment_units(entities):
length = radius_m * math.radians(theta1 - theta0)
units.append({
"id": f"alignment-{index + 1}",
"name": f"BLOCK8 평면 중심선 {index + 1}",
"name": "BLOCK8 기준 중심선",
"center": [(acx - center_x_origin) / 1000, (float(first(props(arc), 20)) - center_y_origin) / 1000],
"planOrigin": [center_x_origin, center_y_origin],
"radius": radius_m,
@@ -222,7 +222,9 @@ def alignment_units(entities):
"centerlineHandle": first(props(arc), 5, ""),
"boundaryHandles": boundary_handles,
})
return units
# 평면도와 상부평면도에 반복된 중심선은 같은 구조물의 도면 표현이다.
# 첫 번째 중심선을 모델 배치 기준으로 사용하고 두 번째는 검산에만 쓴다.
return units[:1]
def add_block_parts(parts, entities, blocks, center_x, sections):
+3 -3
View File
@@ -56,7 +56,7 @@ def main():
svg = [
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 660" role="img" aria-labelledby="title desc">',
'<title id="title">광터지하차도 BLOCK8 도면 기반 3D 구조 모델</title>',
'<desc id="desc">두 평면 중심선을 따라 시점과 종점 단면 구조선을 연결한 열린 표면 검토 모델. 일부 배치 대응은 확인 필요.</desc>',
'<desc id="desc">평면도와 상부평면도의 동일 구조물 중심선을 기준으로 시점과 종점 단면 구조선을 연결한 단일 열린 표면 검토 모델.</desc>',
'<defs><linearGradient id="bg" x2="0" y2="1"><stop stop-color="#f7faf6"/><stop offset="1" stop-color="#e8eee8"/></linearGradient></defs>',
'<rect width="1200" height="660" rx="22" fill="url(#bg)"/>',
'<text x="62" y="61" fill="#728579" font-family="sans-serif" font-size="14" font-weight="700" letter-spacing="2">광터지하차도 · 도면 기반 검토 모델</text>',
@@ -78,12 +78,12 @@ def main():
left = point_at(unit, [[0, 104]], [[0, 104]], 0, 0)
right = point_at(unit, [[0, 104]], [[0, 104]], 0, 1)
p0, p1 = project(left), project(right)
svg.append(f'<g font-family="sans-serif" font-size="12" fill="#55715c"><text x="{p0[0]:.1f}" y="{p0[1]-13:.1f}">중심선 {idx+1} · STA 319.010</text><text x="{p1[0]:.1f}" y="{p1[1]-12:.1f}">STA 338.990</text></g>')
svg.append(f'<g font-family="sans-serif" font-size="12" fill="#55715c"><text x="{p0[0]:.1f}" y="{p0[1]-13:.1f}">BLOCK8 중심선 · STA 319.010</text><text x="{p1[0]:.1f}" y="{p1[1]-12:.1f}">STA 338.990</text></g>')
svg.extend([
'<g font-family="sans-serif" font-size="12"><rect x="62" y="570" width="1076" height="54" rx="9" fill="#f8f3e8" stroke="#e5dbc4"/>',
'<text x="82" y="593" fill="#8c774e">길이 19.980m · 평면 중심선 교점으로 계산 · 시점/종점 단면 구조선 114개씩 + 블록 내부 윤곽</text>',
'<text x="82" y="612" fill="#8c774e">열린 표면 모델이며 닫힌 체적은 아닙니다. 두 중심선에 같은 단면을 적용한 대응은 검토가 필요합니다.</text></g>',
'<text x="82" y="612" fill="#8c774e">단일 구조물 모델입니다. 열린 표면이며 닫힌 체적이 아닙니다. 상부평면도 중심선은 같은 구조물 표현으로 해석했습니다.</text></g>',
'</svg>',
])
OUTPUT.write_text('\n'.join(svg) + '\n', encoding="utf-8")
+10 -18
View File
@@ -18,7 +18,6 @@ export default function App() {
const [view, setView] = useState('전체')
const [renderMode, setRenderMode] = useState(false)
const [hidden, setHidden] = useState(new Set())
const [hiddenUnits, setHiddenUnits] = useState(new Set())
const [selected, setSelected] = useState(null)
const [tab, setTab] = useState('부재')
const [search, setSearch] = useState('')
@@ -27,7 +26,7 @@ export default function App() {
const allParts = useMemo(() => model.units.flatMap(unit => unit.parts.map(part => ({ ...part, unitId: unit.id, unitName: unit.name }))), [])
const selectedPart = selected ? allParts.find(part => part.id === selected.partId && part.unitId === selected.unitId) : null
const selectedUnit = selected ? model.units.find(unit => unit.id === selected.unitId) : null
const visibleCount = allParts.filter(part => !hidden.has(part.id) && !hiddenUnits.has(part.unitId)).length
const visibleCount = allParts.filter(part => !hidden.has(part.id)).length
const filteredParts = allParts.filter(part => `${part.name} ${part.layer} ${part.unitName}`.toLowerCase().includes(search.toLowerCase()))
const dimensionRange = pointRange(allParts, 'a', 0)
const elevationRange = pointRange(allParts, 'a', 1)
@@ -49,12 +48,6 @@ export default function App() {
ids.forEach(id => hide ? next.add(id) : next.delete(id))
return next
})
const toggleUnit = id => setHiddenUnits(current => {
const next = new Set(current)
if (next.has(id)) next.delete(id); else next.add(id)
return next
})
return (
<main className="page-shell">
<header className="topbar">
@@ -72,9 +65,9 @@ export default function App() {
<div>
<span className="kicker">평면 중심선 · 종단 표고 · 단면 구조선을 연결한 검토 모델</span>
<h1>광터지하차도 <em>BLOCK8</em></h1>
<p>구조선과 블록 내부 콘크리트 선을 추출해 두 측점 단면 사이를 연결했습니다.</p>
<p>평면도·상부평면도는 같은 구조물의 표현으로 해석해 한 개 모델로 구성했습니다.</p>
</div>
<span className="status-pill"><i /> 일부 배치 대응 확인 필요</span>
<span className="status-pill"><i /> 단일 구조물 · 도면 대조 검토</span>
</section>
<section className="metrics" aria-label="주요 모델 기준값">
@@ -86,7 +79,7 @@ export default function App() {
<div className="notice">
<b>검토 상태</b>
<p>단면 A/B 구조선 114개씩과 내부 블록 윤곽을 반영했습니다. 평면도에 BLOCK8 표기가 있는 중심선이 2개 있어 둘 다 별도 객체로 표시했습니다. 두 배치에 같은 대표 단면을 적용한 것은 확인 필요 항목입니다.</p>
<p>단면 A-A/B-B 구조선과 내부 블록 윤곽을 한 구조물에 반영했습니다. 평면도와 상부평면도에 반복된 표현은 별도 구조물로 중복 생성하지 않았습니다.</p>
</div>
<section className="workspace">
@@ -100,8 +93,8 @@ export default function App() {
<span>{visibleCount} / {allParts.length} 객체 표시</span>
</div>
<div className={`canvas-wrap ${renderMode ? 'render-mode' : ''}`}>
<ModelViewer units={model.units} hidden={hidden} hiddenUnits={hiddenUnits} selected={selected} onSelect={setSelected} view={view} renderMode={renderMode} resetToken={resetToken} />
<span className="canvas-tag">{renderMode ? '명암을 더한 도면 검토 시각화 · 실사 렌더링 아님' : '구조선 연결 표면 · 두 배치 중심선 표시'}</span>
<ModelViewer units={model.units} hidden={hidden} selected={selected} onSelect={setSelected} view={view} renderMode={renderMode} resetToken={resetToken} />
<span className="canvas-tag">{renderMode ? '명암을 더한 도면 검토 시각화 · 실사 렌더링 아님' : '평면도·상부평면도 공통 구조물 모델'}</span>
</div>
<div className="viewer-foot"><span>끌기: 회전 · 휠: 확대/축소 · Shift+끌기 또는 오른쪽 끌기: 이동</span><button onClick={() => { setView('전체'); setResetToken(value => value + 1); setSelected(null) }}>전체 보기 ↗</button></div>
<div className="selection" aria-live="polite">
@@ -112,7 +105,7 @@ export default function App() {
<p>도면 레이어<strong>{selectedPart.layer}</strong></p>
<p>종단 연결 길이<strong>{selectedUnit.length.toFixed(3)} m</strong></p>
<p>단면 EL 범위<strong>{Math.min(...selectedPart.a.concat(selectedPart.b).map(p => p[1])).toFixed(3)} ~ {Math.max(...selectedPart.a.concat(selectedPart.b).map(p => p[1])).toFixed(3)}</strong></p>
<p>배치 객체<strong>{selectedUnit.name}</strong></p>
<p>모델 대상<strong>{selectedUnit.name}</strong></p>
</div>
<p className="source-note">원본 HANDLE: {(selectedPart.sourceHandles || [selectedPart.sourceHandle, ...(selectedPart.sourceInsertHandles || [])]).filter(Boolean).join(' / ') || '블록 내부 객체'} · 이 표면은 닫힌 체적이 아닙니다.</p>
<button className="subtle" onClick={() => togglePart(selectedPart.id)}>{hidden.has(selectedPart.id) ? '선택 객체 표시' : '선택 객체 숨기기'}</button>
@@ -124,7 +117,6 @@ export default function App() {
<div className="tabs">{['부재', '도면·검증'].map(name => <button key={name} className={tab === name ? 'active' : ''} onClick={() => setTab(name)}>{name}</button>)}</div>
{tab === '부재' ? <>
<div className="panel-heading"><h2>모델 객체</h2><span>{visibleCount} 표시</span></div>
<div className="unit-toggles">{model.units.map((unit, index) => <label key={unit.id}><input type="checkbox" checked={!hiddenUnits.has(unit.id)} onChange={() => toggleUnit(unit.id)} />평면 중심선 {index + 1}<small>CAD상 중심 간격 {index ? '35.000' : '—'} m</small></label>)}</div>
<input className="search" aria-label="모델 객체 검색" placeholder="레이어 또는 객체 이름 검색" value={search} onChange={event => setSearch(event.target.value)} />
<div className="group-toggles">
{Object.entries(categoryLabels).map(([group, label]) => {
@@ -135,7 +127,7 @@ export default function App() {
</div>
<div className="part-list">
{filteredParts.map(part => <div className={`part-row ${selected?.partId === part.id ? 'chosen' : ''} ${hidden.has(part.id) ? 'is-hidden' : ''}`} key={part.id}>
<button onClick={() => { setSelected({ unitId: part.unitId, partId: part.id }); setTab('부재') }} title={part.name}>{part.name}<small>{part.unitName.replace('BLOCK8 평면 중심선 ', '배치 ')}</small></button>
<button onClick={() => { setSelected({ unitId: part.unitId, partId: part.id }); setTab('부재') }} title={part.name}>{part.name}<small>단일 구조물</small></button>
<input type="checkbox" aria-label={`${part.name} 표시`} checked={!hidden.has(part.id)} onChange={() => togglePart(part.id)} />
</div>)}
{!filteredParts.length && <p className="empty">검색 결과가 없습니다.</p>}
@@ -147,7 +139,7 @@ export default function App() {
<li><b>확인</b><span>측점 간격 19.980m<small>중심선 원호와 CS-CONC-MAJR 끝선 교점으로 계산</small></span></li>
<li><b>확인</b><span>단면 구조선 114개씩<small>CS-CONC-MAJR 108 + CS-CONC-MINR 6</small></span></li>
<li><b>확인</b><span>블록 내부 콘크리트<small>A$C2017021411375356 내부 18선 + 2원호, 4개 삽입 참조</small></span></li>
<li className="pending"><b>검토</b><span>두 중심선과 대표 단면 대응<small>두 평면 배치는 표시했으며 구조도에서 동일 단면 적용 여부 확인 필요</small></span></li>
<li><b>확인</b><span>도면 표현 연결<small>평면도·상부평면도는 같은 구조물을 나타내며 모델은 한 개로 구성</small></span></li>
<li className="pending"><b>검토</b><span>폭 20.300m / 외곽선 20.500m<small>단면 치수와 외곽 구조선 범위 차이 0.200m</small></span></li>
<li className="pending"><b>검토</b><span>기초 최저선과 EL 주석 차이<small>모델 선 끝이 표기 기초 EL보다 0.100m 낮음</small></span></li>
</ul>
@@ -161,7 +153,7 @@ export default function App() {
<article className="info-card"><span className="eyebrow">단면 기준 A-A → B-B</span><h2>EL값과 폭 분할</h2><p>중심 EL은 102.094 → 101.517m, 벽체 상단은 107.215 → 107.857m입니다. 표기 기초 하단은 100.877 → 100.392m입니다. 폭 치수는 0.9 + 8.75 + 1.0 + 8.75 + 0.9 = 20.300m입니다.</p></article>
<article className="info-card"><span className="eyebrow">모델 근거</span><h2>좌표 단위 검산</h2><p>DXF의 단위 설정은 비어 있지만, 구조선 좌표 차 20,300과 도면 치수 20.300m가 맞아 좌표를 mm로 환산했습니다. 현재 범위 EL {elevationRange[0].toFixed(3)}~{elevationRange[1].toFixed(3)}m, 단면 외곽선 폭 {dimensionRange[1] - dimensionRange[0] === 0 ? '—' : '20.500'}m입니다.</p></article>
</section>
<footer>출처: BLOCK8 DXF · 3D 면은 두 단면 선형 보간 · 미확정 대응은 검토 패널에 표시</footer>
<footer>출처: BLOCK8 DXF · 3D 면은 두 단면 선형 보간 · 평면도와 상부평면도는 단일 구조물 기준</footer>
</main>
)
}
+7 -9
View File
@@ -37,16 +37,16 @@ function inPolygon(point, polygon) {
return inside
}
export default function ModelViewer({ units, hidden, hiddenUnits, selected, onSelect, view, renderMode, resetToken }) {
export default function ModelViewer({ units, hidden, selected, onSelect, view, renderMode, resetToken }) {
const canvasRef = useRef(null)
const cameraRef = useRef({ ...DEFAULT_CAMERA })
const sceneRef = useRef({ units, hidden, hiddenUnits, selected, view, renderMode, onSelect })
const sceneRef = useRef({ units, hidden, selected, view, renderMode, onSelect })
const drawRef = useRef(() => {})
useEffect(() => {
sceneRef.current = { units, hidden, hiddenUnits, selected, view, renderMode, onSelect }
sceneRef.current = { units, hidden, selected, view, renderMode, onSelect }
drawRef.current()
}, [units, hidden, hiddenUnits, selected, view, renderMode, onSelect])
}, [units, hidden, selected, view, renderMode, onSelect])
useEffect(() => {
const canvas = canvasRef.current
@@ -74,7 +74,7 @@ export default function ModelViewer({ units, hidden, hiddenUnits, selected, onSe
canvas.width = Math.round(width * dpr); canvas.height = Math.round(height * dpr)
}
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
const { units, hidden, hiddenUnits, selected, renderMode } = sceneRef.current
const { units, hidden, selected, renderMode } = sceneRef.current
const background = ctx.createLinearGradient(0, 0, 0, height)
background.addColorStop(0, renderMode ? '#e8efea' : '#f8faf8')
background.addColorStop(1, renderMode ? '#cbd8cf' : '#e9efea')
@@ -91,7 +91,6 @@ export default function ModelViewer({ units, hidden, hiddenUnits, selected, onSe
const faces = []
const longitudinalSteps = 8
for (const unit of units) {
if (hiddenUnits.has(unit.id)) continue
for (const part of unit.parts) {
if (hidden.has(part.id) || part.a.length !== part.b.length) continue
const color = COLORS[part.group] || COLORS.major
@@ -129,11 +128,10 @@ export default function ModelViewer({ units, hidden, hiddenUnits, selected, onSe
ctx.font = '11px sans-serif'
ctx.textAlign = 'left'
ctx.fillStyle = '#68806d'
units.forEach((unit, index) => {
if (hiddenUnits.has(unit.id)) return
units.forEach(unit => {
const start = project(worldPoint(unit, [[0, 104]], [[0, 104]], 0, 0), width, height)
const end = project(worldPoint(unit, [[0, 104]], [[0, 104]], 0, 1), width, height)
ctx.fillText(`중심선 ${index + 1} · STA 319.010`, start[0] - 5, start[1] - 16)
ctx.fillText('BLOCK8 중심선 · STA 319.010', start[0] - 5, start[1] - 16)
ctx.fillText(`STA 338.990`, end[0] - 8, end[1] - 14)
})
ctx.fillStyle = '#6f8275'
+1 -5318
View File
File diff suppressed because it is too large. Load diff