현재 프로젝트 정리 사항 반영

This commit is contained in:
김지영 committed 2026-10-28 09:05:45 +09:00
1 parent 5c0ffb1ffe
commit 804833bebd
8 files changed
+13 -285

No files matched your search

-43
View File
@@ -1,43 +0,0 @@
# 프로젝트 지침: 도면 기반 웹 3D 모델
## 1. 도면 분석
- 평면도, 상부평면도, 종단면도, 단면도 A-A, 단면도 B-B, 측구 상세도를 참고한다.
- 평면도·종단면도·단면도·상세도에서 같은 대상을 어떤 근거로 연결해 읽었는지 설명한다.
- 모델은 평면도상의 선형 중심선과 종단면도, 단면 A-A, 단면 B-B를 기준으로 한다.
- 평면도·종단면도·단면도·상세도의 축척값을 확인한다.
- 평면도 중심 선형의 시점과 단면도 A-A, 종점과 단면도 B-B의 중심 기준점 EL값을 일치시킨다.
- 종단면도의 시종점 벽체 상단 EL값과 단면 A-A, 단면 B-B의 벽체 상단 EL값이 같은지 확인한다.
- 실제 모델 윤곽선인 `CS-CONC-MAJR`, `CS-CONC-MINR` 레이어의 구조물선과 해당 치수가 일치하는지 확인한다.
- 두 레이어의 표기 치수가 부족하면 실제 윤곽선을 측정해 모델링한다.
## 2. 모델링 값 기록
- 대상별 위치, 길이, 폭, 높이, 두께, 간격, 경사 등 모델에 필요한 값을 표로 정리한다.
- 각 값에 근거 도면 번호와 도면 안의 위치를 함께 기록한다.
- 각 값을 다음 상태 중 하나로 표시한다: `도면 명시값`, `계산으로 도출한 값`, `추정값`, `확인 필요`.
- 축척으로 재어 얻은 값은 도면에 적힌 치수와 혼동되지 않도록 별도로 표시한다.
## 3. 오류 및 정보 부족 검토
- 도면 간 치수·위치·높이의 불일치를 확인해 기록한다.
- 누락되었거나 판독하기 어려운 정보를 목록으로 만든다.
- 형상과 배치에 영향을 주는 치수는 임의로 결정하지 않고 사용자 확인을 요청한다.
## 4. 모델 구성 및 생성
- 단면도의 치수선을 제외한 구조물선을 빠짐없이 모델링에 반영한다.
- 부재를 개별 선택·숨김·수정할 수 있도록 객체를 나누고 알아보기 쉬운 이름을 붙인다.
- 도면 개정 때 치수와 배치를 쉽게 바꾸도록 값을 코드와 모델 구조에서 관리하기 쉽게 구성한다.
- 먼저 주요 형상과 전체 배치를 만들고, 이후 세부 형상을 추가한다.
## 5. 웹 3D 뷰어
- 마우스 또는 터치로 회전·확대/축소·이동할 수 있게 한다.
- 전체 보기와 평면·정면·측면 보기를 제공한다.
- 객체를 선택하면 이름과 주요 치수를 보여준다.
- 객체별 표시/숨김 기능을 제공한다.
- 추정값 또는 확인 필요 항목으로 만든 형상은 화면에서 명확히 구분한다.
- 기본 모델 보기와 표면 명암·배경·그림자를 적용한 렌더링 보기를 전환할 수 있게 한다. 렌더링 보기는 도면 검토용 시각화이며 실사 렌더링으로 오해하지 않도록 표시한다.
## 6. 검증 및 결과 보고
- 주요 치수, 기준 높이, 부재 개수와 배치를 원도면과 대조한다.
- 검증 완료 항목과 미검증 항목을 나누어 보고한다.
- 도면과 다르게 표현한 부분, 추정한 부분, 생략한 부분을 기록한다.
- 실행 방법과 다음 수정 때 변경할 항목을 안내한다.
- 변경 후 가능한 실행·검증을 수행하고, 확인하지 못한 부분을 명시한다.
+13 -12
View File
@@ -1,23 +1,24 @@
# 새 3D 모델 프로젝트
# 광터지하차도 BLOCK8 3D 모델
React와 Vite를 사용하는 웹 프로젝트 기본 틀입니다. 작업을 시작할 수 있도록 앱 화면과 프로젝트별 자료를 비워 두었습니다.
React와 Vite로 만든 도면 검토용 웹 3D 모델입니다. 평면 중심선과 종단·A-A/B-B 단면 구조선을 연결해 단일 구조물로 표현합니다. 화면의 면은 검토용 열린 표면이며 제작용 상세 모델이 아닙니다.
## 실행
```bash
npm install
npm run dev
```
## 주요 명령
주요 확인 명령은 `npm run build`와 `npm run lint`입니다.
- `npm run dev`: 개발 서버 실행
- `npm run build`: 배포용 파일 생성
- `npm run preview`: 빌드한 화면 미리보기
- `npm run lint`: 코드 기본 검사
## 주요 경로
## 시작 위치
- `src/App.jsx`, `src/ModelViewer.jsx`: 웹 화면과 3D 뷰어
- `src/model/block8.json`: 모델 데이터
- `public/drawings/광터지하차도_BLOCK8.dxf`: 원본 도면
- `public/analysis/도면분석.md`: 도면 판독 근거, 모델 값 및 미검증 사항
- `public/작업기록.txt`: 작업 이력
- `src/App.jsx`: 웹 화면
- `src/index.css`: 화면 스타일
- `src/main.jsx`: 앱 시작 파일
- `index.html`: 페이지 기본 정보
## 도면 분석 원칙
도면에 적힌 값, 계산값, 축척 측정값, 추정값을 구분해 기록합니다. 도면 간 차이와 확인이 필요한 항목은 분석 문서에 남기며, 근거가 부족한 형상·치수는 임의로 확정하지 않습니다.
-81
View File
@@ -1,81 +0,0 @@
# 광터지하차도 BLOCK8 도면 분석 및 모델링 기록
## 사용 도면과 연결 근거
| 도면 | 자료 위치 | 읽은 내용 | 상태 |
|---|---|---|---|
| 평면도 | `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·폭 분할 | 직접 추출 및 대조 |
| 블록 내부 콘크리트 윤곽 | `A$C2017021411375356` 블록, 단면별 INSERT 4곳 | 내부 `CS-CONC-MAJR` 18선과 원호 2개를 확인해 좌우 삽입 형상으로 처리 | 블록 기준점·삽입 위치·좌우 반전 반영 |
| 측구 상세 | 전체 일반도 DXF의 별도 상세 영역 | BLOCK8 본체 단면과 측구 세부의 연결 표기를 확인하지 못함 | 별도 모델 반영 보류 |
| 제원·특기사항 및 구조도 | 사용자 제공 폴더의 DWG | 파일 존재 확인. DWG 직접 렌더링/교차검토는 이 실행 환경에서 수행하지 못함 | 후속 검증 필요 |
두 단면은 DXF의 동일 구조 윤곽 영역을 측점 주석(`319.000`, `339.000`)과 연결했다. 각 단면의 `CS-CNTL` 중심 표시 교차점 EL은 각각 102.094m와 101.517m이며, 같은 단면 영역의 EL 주석과 일치한다. 벽체 상단 EL 107.215m/107.857m도 종단 검토 영역에 반복 표기되어 각 단면과 맞는다. 각 측점 단면의 폭 분할은 양쪽 단부벽·중앙벽·두 통로 폭으로 이어진다. 평면도와 상부평면도에 보이는 두 중심선 표현은 도면 배치상 서로 다른 위치에 그려진 같은 구조물의 표현으로 해석했다. 따라서 단면 A-A/B-B 및 종단 기준과 일치하는 하나의 중심선을 모델 기준으로 채택했고, 도면상 35m의 배치 간격은 실제 구조물 간격이 아닌 도면 표현 간 거리로 보아 모델에 적용하지 않았다.
## 축척 및 좌표 단위 확인
- 평면도 영역의 축척 표기: `S=1:100`.
- 단면 영역의 축척 표기: `S=1:100`.
- 종단 검토 영역의 축척 표기: `S=1:100`.
- 상세 영역에는 `S=1:30`, `S=1:20`, `S=1:3500` 등 다른 표기도 있으므로 각 상세별 표기를 따로 적용해야 한다.
- DXF HEADER는 `$INSUNITS=0`으로 단위를 지정하지 않았다. 다만 단면 구조선의 좌표 차 20,300이 표기 치수 20.300m와 맞고, 중심선 원호를 구조물 끝선에 교차시켜 계산한 길이가 19.980m로 측점 차와 일치한다. 이 교차 검산에 따라 모델 생성에 한해 좌표를 mm로 보고 m로 환산했다. 단위 판정은 계산 근거이며 HEADER 명시값은 아니다.
## 모델링 값과 근거
| 대상/값 | 모델 값 | 근거 위치 | 상태 |
|---|---:|---|---|
| 모델 시점 | 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 좌표 차 | 도면 배치 좌표 차; 모델 간격으로 사용하지 않음 |
| 단면 A-A 측점 | STA. 0K+319.000 | 단면 A 표기 | 도면 명시값 |
| 단면 A-A 벽체 상단 EL | 107.215 | 상단 EL 주석 및 상단 구조선 | 도면 명시값 |
| 단면 A-A 기초 하단 EL | 100.877 | 하단 EL 주석 및 기초 구조선 | 도면 명시값 |
| 단면 A-A 기초 상단 EL | 101.777 | EL100.877 + 구조선상 기초 두께 0.900m | 계산으로 도출한 값 |
| 단면 A-A 중심 기준 EL | 102.094 | `CS-CNTL` 중심 표시 교점과 단면 EL 주석 | 좌표 환산 계산, 표기값과 일치 |
| 단면 B-B 측점 | STA. 0K+339.000 | 단면 B 표기 | 도면 명시값 |
| 단면 B-B 벽체 상단 EL | 107.857 | 상단 EL 주석 및 상단 구조선 | 도면 명시값 |
| 단면 B-B 기초 하단 EL | 100.392 | 하단 EL 주석 및 기초 구조선 | 도면 명시값 |
| 단면 B-B 기초 상단 EL | 101.292 | EL100.392 + 구조선상 기초 두께 0.900m | 계산으로 도출한 값 |
| 단면 B-B 중심 기준 EL | 101.517 | `CS-CNTL` 중심 표시 교점과 단면 EL 주석 | 좌표 환산 계산, 표기값과 일치 |
| 단면 주 통로 폭 분할 | 0.900 + 8.750 + 1.000 + 8.750 + 0.900 = 20.300m | 단면 치수 표기와 `CS-CONC-MAJR/MINR` 좌표 간격 | 도면 명시값 및 좌표 대조 |
| 구조선 최대 외곽 폭 | 20.500m | 단면 구조선 X 좌표 최댓값-최솟값 | 축척·좌표 측정값; 표기 폭과 별도 관리 |
| 구조선 기초 두께 | 0.900m | 단면 상·하부 구조선 좌표 차 | 계산으로 도출한 값 |
| 원시 구조선 수/단면 | MAJR 108 + MINR 6 = 114 | 각 단면의 두 구조 레이어 | 추출 개수 |
| 블록 내부 구조선 | MAJR 18선 + 원호 2개; 단면당 좌우 INSERT 2개 | `A$C2017021411375356` 블록과 삽입 참조 | 내부 레이어·반전 변환 적용 |
| 블록 연결 길이 | 19.980m | 단면 A/B 내부 블록 대응을 따라 선형 보간 | 계산으로 도출 |
모델은 두 단면의 구조선을 대응시켜 각 구조선을 길이 방향 표면으로 연결했다. 평면 원호는 양 끝 구조선과의 교점에 맞춰 길이 19.980m로 잘랐다. 단면 도면은 STA 319.000/339.000에 있고 모델 경계는 각각 0.010m 안쪽의 STA 319.010/338.990이므로, 두 끝에서 0.010m씩 단면 형상을 선형 보간해 경계에 맞췄다.
## 오류 및 확인 필요 사항
1. 단면 외곽 구조선 폭은 20.500m인데 표기된 주 통로/본체 폭은 20.300m다. 좌우 0.100m씩 추가되는 외곽 선의 용도는 구조도 DWG와 확인해야 한다. 모델에는 두 값을 임의로 하나로 합치지 않고 원 구조선을 그대로 보존했다.
2. 구조선 최저 외곽점은 표기된 기초 하단 EL보다 약 0.100m 낮게 계산된다(A: 100.777m, B: 100.292m). 기초 상세 또는 버림층 여부가 확인될 때까지 외곽 구조선 그대로 표시하고 차이를 남긴다.
3. 평면도와 상부평면도에 같은 구조물 중심선이 도면상 35.000m 떨어져 반복 표현되어 있다. 이는 도면 뷰 간 배치 차이로 해석하여 3D에는 한 번만 모델링했다. 중심선 형상과 측점은 종단·A-A/B-B 단면 기준으로 대응시켰다.
4. 종단면의 상단 벽체 EL과 두 단면 상단 EL은 현재 일반도 DXF의 구조선/EL 위치로 읽었으나, 제원 DWG와 별도 구조도에서 재확인하지 못했다.
5. 측구 상세도 기호와 본체 구조선의 실제 접합 위치, 부재 두께/재료, 닫힌 체적 경계는 미검증이다.
## 모델 구성과 화면 기능
- 생성 데이터: `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 시점을 제공한다.
- 객체는 단일 중심선 모델에 구조선 114개와 삽입 블록 윤곽 40개, 총 154개 객체를 구성했다(단면 구조선 114개와 삽입 블록 윤곽 40개). 원본선 단위 선택·숨김과 MAJR/MINR/블록 윤곽 그룹 숨김을 지원한다.
- 화면의 면은 검토용 열린 표면 모델이다. 구조선 사이를 연결했으며 모든 두께/접합을 확정한 닫힌 체적이나 실사 렌더링은 아니다.
- `scripts/render_block8_drawing_views.py`는 DXF 도면 영역의 선·문자를 잘라 평면도, 상부평면도, 종단면도, A-A, B-B SVG 이미지 5개와 픽셀-CAD 좌표 연결값을 만든다. 뷰어에서 현재 보기와 도면 종류를 연결하고 투명도를 조절할 수 있다.
- A-A/B-B 이미지는 각 끝 측점의 단면 평면에 놓고, 종단면 이미지는 STA 주석과 EL 주석으로 길이·높이를 변환해 구조물 측면에 놓는다. 평면도는 DXF 좌표를 모델 좌표에 대응시키며, 상부평면도 표기는 도면 간 35m 배치 차이를 이동해 단일 모델 위에 포개고 화면 표시용 EL 108.8m에 둔다.
- 클리핑은 X(좌우 폭), Y(표고), Z(길이/측점 방향) 축 중 하나를 골라 전체 모델 좌표 범위의 0~100% 위치에서 한쪽 표면을 잘라 표시한다. 열린 표면 모델이므로 절단면은 막지 않으며, 도면 이미지 오버레이는 참고면으로 남아 절단되지 않는다.
- 이들은 도면 요소를 3D 화면의 기준 평면에 투영한 검토용 이미지다. 평면의 표시 높이와 종단면의 측면 이격은 보이도록 둔 시각화 위치이며 실제 부재나 설계 고도를 뜻하지 않는다. DXF 글꼴 및 일부 블록 표현은 SVG에서 단순화될 수 있다.
- 실행: `npm run dev`. 분석 파일과 작업 로그는 화면 상단 링크에서 연다.
## 검증 결과
- 추출 스크립트 실행 결과: 모델 중심선 1개, 길이 19.980m; 도면에 표현된 대응 중심선 2개 형상은 기준 대조에만 사용, 단면 구조선 MAJR 108개와 MINR 6개, 내부 블록 객체를 양쪽 단면에서 대응했다.
- `npm run build`: 통과.
- `npm run lint`: 미사용 함수 경고를 제거한 뒤 최종 재실행 예정.
- `npm run lint`: 통과, 경고 없음.
- `git diff --check`: 통과.
- 미검증: 원본 DWG에서 제원·특기사항, 구조 상세, 평면/상부평면도 간 상세 배치 대응의 원본 DWG 교차검토 및 측구 접합부를 CAD/PDF로 교차 대조하지 못했다.
-25
View File
@@ -1,25 +0,0 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#eff2ed">
<meta name="description" content="도면의 중심선과 구조 단면을 연결한 광터지하차도 BLOCK8 3D 검토 모델입니다.">
<title>광터지하차도 BLOCK8 3D 모델</title>
<style>:root{color:#263a32;font-synthesis:none;text-rendering:optimizelegibility;background:#eff2ed;font-family:Malgun Gothic,Apple SD Gothic Neo,sans-serif}*{box-sizing:border-box}body{min-width:320px;min-height:100vh;margin:0}button,input{font:inherit}button{color:inherit;cursor:pointer;background:0 0;border:0}a{color:inherit;text-decoration:none}button:focus-visible,a:focus-visible,input:focus-visible{outline-offset:3px;outline:2px solid #bc763b}.page-shell{width:min(100% - 64px,1600px);margin:auto;padding-bottom:24px}.topbar{border-bottom:1px solid #d9e1d7;align-items:center;gap:18px;min-height:72px;display:flex}.brand{white-space:nowrap;align-items:center;gap:10px;font-size:14px;font-weight:700;display:flex}.brand-mark{color:#fff;background:#446b58;border-radius:10px;place-items:center;width:34px;height:34px;font-size:11px;display:grid}.project-chip{color:#7a897e;margin-right:auto;font-size:11px}.file-links{gap:7px;display:flex}.file-links a{color:#536d5b;white-space:nowrap;background:#fafcf8;border:1px solid #d3ddd2;border-radius:7px;padding:9px 11px;font-size:10px}.file-links a:hover,.source-links a:hover{color:#315c42;border-color:#92a994}.intro{justify-content:space-between;align-items:center;gap:18px;padding:29px 0 20px;display:flex}.kicker,.eyebrow{color:#7a8d7e;letter-spacing:.08em;font-size:10px;font-weight:700;display:block}h1{letter-spacing:-.055em;margin:8px 0;font-size:clamp(26px,3.3vw,34px)}h1 em{color:#738b79;margin-left:7px;font-size:.8em;font-style:normal;font-weight:400}.intro p{color:#7c8d80;margin:0;font-size:11px;line-height:1.7}.status-pill{color:#9b793c;white-space:nowrap;background:#f8f4e9;border:1px solid #e0d6bd;border-radius:30px;align-items:center;gap:8px;padding:9px 13px;font-size:10px;display:flex}.status-pill i{background:#b08a45;border-radius:50%;width:7px;height:7px}.metrics{background:#f9fbf7;border:1px solid #dce4d9;border-radius:11px;grid-template-columns:repeat(4,1fr);margin-bottom:13px;padding:17px 0;display:grid}.metrics article{border-right:1px solid #e3e8df;min-width:0;padding:0 18px}.metrics article:last-child{border:0}.metrics small{color:#7d8b7d;font-size:9px;display:block}.metrics strong{white-space:nowrap;margin:8px 0 5px;font-family:Georgia,serif;font-size:clamp(19px,2.3vw,26px);font-weight:400;display:block}.metrics strong.el{font-size:clamp(17px,2vw,22px)}.metrics strong i{color:#899584;font-family:sans-serif;font-size:11px;font-style:normal}.metrics p{color:#8b9687;margin:0;font-size:9px;line-height:1.55}.notice{background:#f5f0e4;border:1px solid #e6dcc7;border-radius:8px;align-items:flex-start;gap:11px;margin-bottom:15px;padding:10px 13px;display:flex}.notice b{color:#967136;white-space:nowrap;padding-top:2px;font-size:10px}.notice p{color:#8b7e62;margin:0;font-size:10px;line-height:1.65}.workspace{grid-template-columns:minmax(0,1fr) 322px;align-items:start;gap:16px;display:grid}.viewer-card,.side-card,.info-card{background:#fafcf8;border:1px solid #dbe4d7;border-radius:11px;min-width:0;overflow:hidden}.viewer-head{justify-content:space-between;align-items:center;gap:12px;padding:17px 19px 13px;display:flex}.viewer-actions{align-items:center;gap:8px;display:flex}.drawing-toggle{color:#5c765f;white-space:nowrap;border:1px solid #d4dfd2;border-radius:6px;padding:7px 10px;font-size:9px}.drawing-toggle.active{color:#385f42;background:#e9f0e7;border-color:#8aa48c}h2{letter-spacing:-.04em;margin:5px 0 0;font-size:16px}.segmented{background:#eaf0e7;border-radius:7px;padding:3px;display:flex}.segmented button{color:#7f8d7b;white-space:nowrap;border-radius:5px;padding:6px 9px;font-size:9px}.segmented button.active{color:#496c52;background:#fff;box-shadow:0 1px 4px #233c2912}.viewbar{justify-content:space-between;align-items:center;gap:10px;padding:0 17px 10px;display:flex}.views{gap:3px;display:flex}.views button{border:1px solid #0000;border-radius:5px;padding:6px 9px;font-size:9px}.views button.active{color:#426a50;background:#eef3ea;border-color:#ccdac9}.viewbar>span{color:#8b9687;white-space:nowrap;font-size:9px}.canvas-wrap{background:#f4f7f3;border-top:1px solid #dfe8da;border-bottom:1px solid #dfe8da;position:relative;overflow:hidden}.drawing-controls{background:#f7faf5;border-bottom:1px solid #e2e9dd;align-items:center;gap:14px;padding:9px 15px;display:flex}.drawing-controls label{color:#647568;white-space:nowrap;align-items:center;gap:7px;font-size:9px;display:flex}.drawing-controls select{color:#526657;background:#fff;border:1px solid #d6e0d4;border-radius:5px;max-width:180px;padding:5px 7px;font-size:9px}.drawing-controls input{accent-color:#59785e;width:95px}.drawing-controls>span{color:#879287;font-size:8px;line-height:1.5}.clip-controls{background:#f4f8f1;border-bottom:1px solid #e2e9dd;flex-wrap:wrap;align-items:center;gap:9px 14px;padding:10px 15px;display:flex}.clip-controls label{color:#526b58;white-space:nowrap;align-items:center;gap:7px;font-size:9px;display:flex}.clip-controls select{color:#526657;background:#fff;border:1px solid #d2dfd0Line truncated
</style>
</head>
<body>
<div id="root"></div>
<script>window.BLOCK8_DRAWING_IMAGES={"plan": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExNjAiIHZpZXdCb3g9IjAgMCAxNDAwIDExNjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0i7Y+J66m064+EIOybkOuzuCBEWEYg7JiB7JetIj4KPHRpdGxlPu2PieuptOuPhCDCtyDsm5Drs7ggRFhGIOuyoe2EsCDsuqHsspg8L3RpdGxlPgo8Zz48cGF0aCBkPSJNMTE1OC41NSwzMDIuOTYgTDExNjMuMzMsMTExNC45NSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNDM4YjllIiBzdHJva2Utd2lkdGg9IjEuMjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzY0LjEzLDMwMi45NiBMMzU5LjM1LDExMTQuOTUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzQzOGI5ZSIgc3Ryb2tlLXdpZHRoPSIxLjI1IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTQ2MS4wOCw3NjkuNDcgTDQ2MC42OSw4NTcuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNNDYwLjY5LDg1Ny45MSBMNDYwLjY1LDg2NS45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik00NjEuNjEsNjQ5LjQ3IEw0NjIuMDAsNTYxLjAzIiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTQ2Mi4wMCw1NjEuMDMgTDQ2Mi4wMyw1NTMuMDMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTE5OS4xNiwzMzguNzIgTDEyNDEuMTYsMzM4LjQ4IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTExOTguOTUsMzAyLjcyIEwxMjQwLjk1LDMwMi40OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xMjM5LjE2LDMzOC40OSBMMTIzOC45NSwzMDIuNDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMS4yMiw2ODguNzIgTDEyNDMuMjIsNjg4LjQ3IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTExOTkuMTYsMzM4LjcyIEwxMjQxLjE2LDMzOC40OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xMjQxLjIyLDY4OC40OCBMMTIzOS4xNiwzMzguNDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMS40Niw3MjguNzIgTDEyNDMuNDUsNzI4LjQ3IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTEyMDEuMjIsNjg4LjcyIEwxMjQzLjIyLDY4OC40NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xMjQxLjQ1LDcyOC40OCBMMTI0MS4yMiw2ODguNDgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMy41MSwxMDc4LjcxIEwxMjQ1LjUxLDEwNzguNDYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMS40Niw3MjguNzIgTDEyNDMuNDUsNzI4LjQ3IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTEyNDMuNTEsMTA3OC40OCBMMTI0MS40NSw3MjguNDgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMy43MywxMTE0LjcxIEwxMjQ1LjczLDExMTQuNDYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTIwMy41MSwxMDc4LjcxIEwxMjQ1LjUxLDEwNzguNDYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTI0My43MywxMTE0LjQ4IEwxMjQzLjUxLDEwNzguNDgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzIzLjUyLDMzOC43MiBMMjgxLjUyLDMzOC40OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMjMuNzMsMzAyLjcyIEwyODEuNzMsMzAyLjQ4IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI4My41MiwzMzguNDkgTDI4My43MywzMDIuNDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzIxLjQ2LDY4OC43MiBMMjc5LjQ2LDY4OC40NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMjMuNTIsMzM4LjcyIEwyODEuNTIsMzM4LjQ4IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI4MS40Niw2ODguNDggTDI4My41MiwzMzguNDkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzIxLjIzLDcyOC43MiBMMjc5LjIzLDcyOC40NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDI1MjQ1IiBzdHJva2Utd2lkdGg9IjAuOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMjEuNDYsNjg4LjcyIEwyNzkuNDYsNjg4LjQ3IiBmaWxsPSJub25lIiBzdHJva2U9IiNkMjUyNDUiIHN0cm9rZS13aWR0aD0iMC44IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI4MS4yMyw3MjguNDggTDI4MS40Niw2ODguNDgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QyNTI0NSIgc3Ryb2tlLXdpZHRoPSIwLjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzE5LjE3LDEwNzguNzEgTDI3Ny4xNywxMDc4LjQ2IiBmaWxsPSJub25lIiBzdHJvLine truncated
<script>var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t,t.exports),e=null),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,o)=>(o=n==null?{}:e(i(n)),s(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var l=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.for(`react.view_transition`),m=Symbol.iterator;function h(e){return typeof e!=`object`||!e?null:(e=m&&e[m]||e[`@@iterator`],typeof e==`function`?e:null)}var g={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_=Object.assign,v={};function y(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}y.prototype.isReactComponent={},y.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},y.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function b(){}b.prototype=y.prototype;function ee(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}var x=ee.prototype=new b;x.constructor=ee,_(x,y.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function te(){}var C={H:null,A:null,T:null,S:null},ne=Object.prototype.hasOwnProperty;function w(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function re(e,t){return w(e.type,t,e.props)}function ie(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function ae(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var T=/\/+/g;function E(e,t){return typeof e==`object`&&e&&e.key!=null?ae(``+e.key):t.toString(36)}function oe(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(te,te):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function se(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,se(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+E(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(T,`$&/`)+`/`),se(o,r,i,``,function(e){return e})):o!=null&&(ie(o)&&(o=re(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(T,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u<e.length;u++)a=e[u],s=l+E(a,u),c+=se(a,r,i,s,o);else if(u=h(e),typeof u==`function`)for(e=u.call(e),u=0;!(a=e.next()).done;)a=a.value,s=l+E(a,u++),c+=se(a,r,i,s,o);else if(s===`object`){if(typeof e.then==`function`)return se(oe(e),r,i,a,o);throw r=String(e),Error(`Objects are not valid as a React child (found: `+(r===`[object Object]`?`object with keys {`+Object.keys(e).join(`, `)+`}`:r)+`). If you meant to render a collection of children, use an array instead.`)}return c}function ce(e,t,n){if(e==null)return e;var r=[],i=0;return se(e,r,``,``,function(e){return t.call(n,e,i++)}),r}function le(e){if(e._status===-1){var t=e._result,n=t();n.then(function(t){(e._status===0||e._status===-1)&&(e._status=1,e._result=t,n.status===void 0&&(n.status=`fulfilled`,n.value=t))},function(t){(e._status===0||e._status===-1)&&(e._status=2,e._result=t,n.status===void 0&&(n.status=`rejected`,n.reason=t))}),e._status===-1&&(e.Line truncated
at`)?` (<anonymous>)`:-1<e.stack.indexOf(`@`)?`@unknown:0:0`:``}return`
`+Le+e+Re}var Be=!1;function Ve(e,t){if(!e||Be)return``;Be=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var r={DetermineComponentFrameRoot:function(){try{if(t){var n=function(){throw Error()};if(Object.defineProperty(n.prototype,"props",{set:function(){throw Error()}}),typeof Reflect==`object`&&Reflect.construct){try{Reflect.construct(n,[])}catch(e){var r=e}Reflect.construct(e,[],n)}else{try{n.call()}catch(e){r=e}n=!1;try{var i=Object.getOwnPropertyDescriptor(e.prototype,`props`);Object.defineProperty(e.prototype,"props",{configurable:!0,set:function(){throw Error()}}),n=!0,new e}finally{n&&(i===void 0?delete e.prototype.props:Object.defineProperty(e.prototype,"props",i))}}}else{try{throw Error()}catch(e){r=e}(n=e())&&typeof n.catch==`function`&&n.catch(function(){})}}catch(e){if(e&&r&&typeof e.stack==`string`)return[e.stack,r.stack]}return[null,null]}};r.DetermineComponentFrameRoot.displayName=`DetermineComponentFrameRoot`;var i=Object.getOwnPropertyDescriptor(r.DetermineComponentFrameRoot,`name`);i&&i.configurable&&Object.defineProperty(r.DetermineComponentFrameRoot,"name",{value:`DetermineComponentFrameRoot`});var a=r.DetermineComponentFrameRoot(),o=a[0],s=a[1];if(o&&s){var c=o.split(`
`),l=s.split(`
`);for(i=r=0;r<c.length&&!c[r].includes(`DetermineComponentFrameRoot`);)r++;for(;i<l.length&&!l[i].includes(`DetermineComponentFrameRoot`);)i++;if(r===c.length||i===l.length)for(r=c.length-1,i=l.length-1;1<=r&&0<=i&&c[r]!==l[i];)i--;for(;1<=r&&0<=i;r--,i--)if(c[r]!==l[i]){if(r!==1||i!==1)do if(r--,i--,0>i||c[r]!==l[i]){var u=`
`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(`<anonymous>`)&&(u=u.replace(`<anonymous>`,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{Be=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?ze(n):``}function He(e,t){switch(e.tag){case 26:case 27:case 5:return ze(e.type);case 16:return ze(`Lazy`);case 13:return e.child!==t&&t!==null?ze(`Suspense Fallback`):ze(`Suspense`);case 19:return ze(`SuspenseList`);case 0:case 15:return Ve(e.type,!1);case 11:return Ve(e.type.render,!1);case 1:return Ve(e.type,!0);case 31:return ze(`Activity`);case 30:return ze(`ViewTransition`);default:return``}}function Ue(e){try{var t=``,n=null;do t+=He(e,n),n=e,e=e.return;while(e);return t}catch(e){return`
Error generating stack: `+e.message+`
`+e.stack}}var We=Object.prototype.hasOwnProperty,Ge=t.unstable_scheduleCallback,Ke=t.unstable_cancelCallback,qe=t.unstable_shouldYield,Je=t.unstable_requestPaint,Ye=t.unstable_now,Xe=t.unstable_getCurrentPriorityLevel,Ze=t.unstable_ImmediatePriority,Qe=t.unstable_UserBlockingPriority,$e=t.unstable_NormalPriority,et=t.unstable_LowPriority,tt=t.unstable_IdlePriority,nt=t.log,rt=t.unstable_setDisableYieldValue,it=null,at=null;function ot(e){if(typeof nt==`function`&&rt(e),at&&typeof at.setStrictMode==`function`)try{at.setStrictMode(it,e)}catch{}}var st=Math.clz32?Math.clz32:ut,ct=Math.log,lt=Math.LN2;function ut(e){return e>>>=0,e===0?32:31-(ct(e)/lt|0)|0}var dt=256,ft=262144,pt=4194304;function mt(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&-e;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function ht(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=mt(n))):i=mt(o):i=mt(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=mt(n))):i=mt(o)):i=mt(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function gt(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function _t(e,t){t&8&&(t|=t&32);var n=e.entangledLanes;if(n!==0)for(e=e.entanglements,n&=t;0<n;){var r=31-st(n),i=1<<r;t|=e[r],n&=~i}return t}function vt(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function yt(){var e=pt;return pt<<=1,!(pt&62914560)&&(pt=4194304),e}function bt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function xt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function St(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0<n;){var u=31-st(n),d=1<<u;s[u]=0,c[u]=-1;var f=l[u];if(f!==null)for(l[u]=null,u=0;u<f.length;u++){var p=f[u];p!==null&&(p.lane&=-536870913)}n&=~d}r!==0&&Ct(e,r,0),a!==0&&i===0&&e.tag!==0&&(e.suspendedLanes|=a&~(o&~t))}function Ct(e,t,n){e.pendingLanes|=t,e.suspendedLanes&=~t;var r=31-st(t);e.entangledLanes|=t,e.entanglements[r]=e.entanglements[r]|1073741824|n&261930}function wt(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-st(n),i=1<<r;i&t|e[r]&t&&(e[r]|=t),n&=~i}}function Tt(e,t){var n=t&-t;return n=n&42?1:Et(n),(n&(e.suspendedLanes|t))===0?n:0}function Et(e){switch(e){case 2:e=1;break;case 8:e=4;break;case 32:e=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:e=128;break;case 268435456:e=134217728;break;default:e=0}return e}function Dt(e){return e&=-e,2<e?8<e?e&134217727?32:268435456:8:2}function Ot(){var e=O.p;return e===0?(e=window.event,e===void 0?32:Ch(e.type)):e}function kt(e,t){var n=O.p;try{return O.p=e,t()}finally{O.p=n}}var At=Math.random().toString(36).slice(2),jt=`__reactFiber$`+At,Mt=`__reactProps$`+At,Nt=`__reactContainer$`+At,Pt=`__reactEvents$`+At,Ft=`__reactListeners$`+At,It=`__reactHandles$`+At,Lt=`__reactResources$`+At,Rt=`__reactMarker$`+At,zt=`__reactLoad$`+At;function Bt(e){delete e[jt],delete e[Mt],delete e[Ft],delete e[It]}function Vt(e){var t;if(t=e[jt])return t;for(var n=e.parentNode;n;){if(t=n[Nt]||n[jt]){if(n=t.alternate,t.child!==null||n!==null&&n.child!==null)for(e=fm(e);e!==null;){if(n=e[jt])return n;e=fm(e)}return t}e=n,n=e.parentNode}return null}function Ht(e){if(e=e[jt]||e[Nt]){var t=e.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return e}return null}function Ut(e){var t=e.tag;if(t===5||t===26||t===27||t===6)return e.stateNode;throw Error(i(33))}function Wt(e){var t=e[Lt];return t||=e[Lt]={hoistableStyles:new Map,hoistableScripts:new Map},t}function Gt(e){e[Rt]=!0}function Kt(e){e[zt]=void 0}var qt=new Set,Jt={};function Yt(e,t){Xt(e,t),Xt(e+`Capture`,t)}function Xt(e,t){for(Jt[e]=t,e=0;e<t.leLine truncated
`).replace(Qf,``)}function ep(e,t){return t=$f(t),$f(e)===t}function $(e,t,n,r,a,o){switch(n){case`children`:if(typeof r==`string`)t===`body`||t===`textarea`&&r===``||yn(e,r);else if(typeof r==`number`||typeof r==`bigint`)t!==`body`&&yn(e,``+r);else return;break;case`className`:rn(e,`class`,r);break;case`tabIndex`:rn(e,`tabindex`,r);break;case`dir`:case`role`:case`viewBox`:case`width`:case`height`:rn(e,n,r);break;case`style`:Sn(e,r,o);return;case`data`:if(t!==`object`){rn(e,`data`,r);break}case`src`:case`href`:if(r===``&&(t!==`a`||n!==`href`)||r==null||typeof r==`function`||typeof r==`symbol`||typeof r==`boolean`){e.removeAttribute(n);break}r=En(r),e.setAttribute(n,r);break;case`action`:case`formAction`:if(typeof r==`function`){e.setAttribute(n,`javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')`);break}if(typeof o==`function`&&(n===`formAction`?(t!==`input`&&$(e,t,`name`,a.name,a,null),$(e,t,`formEncType`,a.formEncType,a,null),$(e,t,`formMethod`,a.formMethod,a,null),$(e,t,`formTarget`,a.formTarget,a,null)):($(e,t,`encType`,a.encType,a,null),$(e,t,`method`,a.method,a,null),$(e,t,`target`,a.target,a,null))),r==null||typeof r==`symbol`||typeof r==`boolean`){e.removeAttribute(n);break}r=En(r),e.setAttribute(n,r);break;case`onClick`:r!=null&&(e.onclick=Dn);return;case`onScroll`:r!=null&&Q(`scroll`,e);return;case`onScrollEnd`:r!=null&&Q(`scrollend`,e);return;case`dangerouslySetInnerHTML`:if(r!=null){if(typeof r!=`object`||!(`__html`in r))throw Error(i(61));if(n=r.__html,n!=null){if(a.children!=null)throw Error(i(60));o?.__html!==n&&(e.innerHTML=n)}}break;case`multiple`:e.multiple=r&&typeof r!=`function`&&typeof r!=`symbol`;break;case`muted`:e.muted=r&&typeof r!=`function`&&typeof r!=`symbol`;break;case`suppressContentEditableWarning`:case`suppressHydrationWarning`:case`defaultValue`:case`defaultChecked`:case`innerHTML`:case`ref`:break;case`autoFocus`:break;case`xlinkHref`:if(r==null||typeof r==`function`||typeof r==`boolean`||typeof r==`symbol`){e.removeAttribute(`xlink:href`);break}n=En(r),e.setAttributeNS(`http://www.w3.org/1999/xlink`,`xlink:href`,n);break;case`contentEditable`:case`spellCheck`:case`draggable`:case`value`:case`autoReverse`:case`externalResourcesRequired`:case`focusable`:case`preserveAlpha`:r!=null&&typeof r!=`function`&&typeof r!=`symbol`?e.setAttribute(n,r):e.removeAttribute(n);break;case`inert`:case`allowFullScreen`:case`async`:case`autoPlay`:case`controls`:case`credentialless`:case`default`:case`defer`:case`disabled`:case`disablePictureInPicture`:case`disableRemotePlayback`:case`formNoValidate`:case`hidden`:case`loop`:case`noModule`:case`noValidate`:case`open`:case`playsInline`:case`readOnly`:case`required`:case`reversed`:case`scoped`:case`seamless`:case`itemScope`:r&&typeof r!=`function`&&typeof r!=`symbol`?e.setAttribute(n,``):e.removeAttribute(n);break;case`capture`:case`download`:!0===r?e.setAttribute(n,``):!1!==r&&r!=null&&typeof r!=`function`&&typeof r!=`symbol`?e.setAttribute(n,r):e.removeAttribute(n);break;case`cols`:case`rows`:case`size`:case`span`:r!=null&&typeof r!=`function`&&typeof r!=`symbol`&&!isNaN(r)&&1<=r?e.setAttribute(n,r):e.removeAttribute(n);break;case`rowSpan`:case`start`:r==null||typeof r==`function`||typeof r==`symbol`||isNaN(r)?e.removeAttribute(n):e.setAttribute(n,r);break;case`popover`:Q(`beforetoggle`,e),Q(`toggle`,e),nn(e,`popover`,r);break;case`xlinkActuate`:an(e,`http://www.w3.org/1999/xlink`,`xlink:actuate`,r);break;case`xlinkArcrole`:an(e,`http://www.w3.org/1999/xlink`,`xlink:arcrole`,r);break;case`xlinkRole`:an(e,`http://www.w3.org/1999/xlink`,`xlink:role`,r);break;case`xlinkShow`:an(e,`http://www.w3.org/1999/xlink`,`xlink:show`,r);break;case`xlinkTitle`:an(e,`http://www.w3.org/1999/xlink`,`xlink:title`,r);break;case`xlinkType`:an(e,`http://www.w3.org/1999/xlink`,`xlink:type`,r);break;case`xmlBase`:an(e,`http://www.w3.org/XML/1998/namespace`,`xml:base`,r);break;case`xmlLang`:an(e,`http://www.w3.org/XML/1998/namespace`,`xml:lang`,r);break;case`xmlSpace`:an(e,`http://www.w3.org/XML/1998/namespace`,`xml:space`,r);break;case`is`:nn(e,`is`,r);break;case`innerText`:case`textContent`:return;default:if(!(2<n.length)||n[0]!==`o`&&n[0]!==`O`||n[1]!==`n`&&n[1]!==`N`)n=wn.get(n)||n,nn(e,n,r);else return}A=!0}function tp(e,t,n,r,a,o){switch(n){case`style`:Sn(e,r,o);return;case`dangerouslySetInnerHTML`:if(r!=null){if(typeof r!=`object`||!(`__html`in r))throw Error(i(61));if(n=r.__html,n!=null){if(a.children!=null)throw Error(i(60));o?.__html!==n&&(e.innerHTML=n)}}break;case`children`:if(typeof r==`string`)yn(e,r);else if(typeof r==`number`||typeof r==`bigint`)yn(e,``+r);else return;break;case`onScroll`:r!=null&&Q(`scroll`,e);return;case`onScrollEnd`:r!=null&&Q(`scrollend`,e);return;case`onClick`:r!=null&&(e.onclick=Dn);return;case`suppressContentLine truncated
</body>
</html>
Binary file not shown.
-90
View File
@@ -1,90 +0,0 @@
# 광터지하차도 BLOCK8 작업 인수인계
- 작성일: 2026-10-20
- 기준 커밋: `e473a7c` — 평면도와 상부평면도 모델을 하나로 통합
- 프로젝트: React + Vite 기반 도면 검토용 웹 3D 모델
## 1. 작업 개요
BLOCK8 DXF 도면을 분석해 종단면 및 단면 A-A/B-B의 구조 윤곽을 연결한 3D 검토 모델을 만들었습니다. 평면도와 상부평면도에 보이는 중심선은 같은 구조물의 표현으로 해석하여 모델은 하나만 생성했습니다. 도면 뷰에 배치된 두 중심선 표현 사이의 CAD 좌표 차 35.000m는 실제 구조물 간격으로 사용하지 않았습니다.
## 2. 현재 모델과 주요 값
- 모델 대상: BLOCK8 구조물 1개
- 기준 측점: STA. 0K+319.010 ~ STA. 0K+338.990
- 중심선 길이: 19.980m (도면 중심선 ARC와 양 끝 `CS-CONC-MAJR` 구조선 교점 사이 계산값)
- 단면 측점: A-A `0K+319.000`, B-B `0K+339.000`
- 중심 기준 EL: A-A 102.094m → B-B 101.517m
- 벽체 상단 EL: A-A 107.215m → B-B 107.857m
- 기초 하단 표기 EL: A-A 100.877m, B-B 100.392m
- 단면 폭 치수 합계: 20.300m
- 실제 구조선 좌표에서 측정한 외곽 폭: 20.500m
- 모델 객체: 총 154개 (단면 구조선 114개 + 블록 내부 윤곽 40개)
- 단면 구조선: `CS-CONC-MAJR` 108개, `CS-CONC-MINR` 6개
## 3. 분석 근거와 남은 확인 사항
DXF에는 단위가 지정되어 있지 않습니다 (`$INSUNITS=0`). 단면 좌표 차 20,300이 표기 치수 20.300m와 일치하는 점, 중심선 길이 계산값 19.980m가 측점 차와 일치하는 점을 이용해 모델 생성용 좌표를 mm로 해석했습니다.
단면의 폭 치수(20.300m)와 외곽 구조선 폭(20.500m)이 0.200m 차이 납니다. 구조선 최저점도 표기된 기초 하단 EL보다 0.100m 낮습니다. 구조도와 기초 상세를 확인하기 전까지 이 선들을 원 도면 그대로 보존했으며 임의로 삭제하거나 치수를 맞추지 않았습니다.
측구 상세와 본체의 실제 접합 위치, 재료 및 닫힌 체적 경계, 원본 DWG의 제원·특기사항은 추가 대조가 필요합니다. 화면은 도면 구조선을 연결한 열린 표면 검토 모델이며 제작용 상세 모델이나 실사 렌더링이 아닙니다.
## 4. 주요 파일
- `public/drawings/광터지하차도_BLOCK8.dxf` — 등록된 원본 DXF
- `docs/도면분석.md` 및 `public/analysis/도면분석.md` — 도면 판독 및 값 근거
- `public/drawing-images/` — DXF에서 추출한 평면·상부평면·종단·A-A/B-B 벡터 이미지
- `public/작업기록.txt` — 초기화 이후 작업 이력
- `src/model/block8.json` — 웹 뷰어가 읽는 모델 데이터
- `scripts/extract_block8_model.py` — DXF에서 모델 데이터를 다시 추출
- `scripts/render_block8_preview.py` — SVG 미리보기 생성
- `scripts/render_block8_drawing_views.py` — 원본 DXF 도면 영역 이미지 생성
- `docs/BLOCK8_3D모델_미리보기.svg` — 모델 이미지
- `src/App.jsx`, `src/ModelViewer.jsx` — 웹 화면과 조작 기능
## 5. 실행 및 확인 방법
프로젝트 폴더에서 다음 명령을 실행합니다.
```bash
npm install
npm run dev
```
터미널에 표시된 로컬 주소를 브라우저에서 열면 모델을 확인할 수 있습니다. 마우스로 끌어 회전하고, 휠로 확대·축소합니다. `Shift`를 누르거나 마우스 오른쪽 버튼을 누른 채 끌면 화면을 이동합니다. 상단의 보기 버튼에서 전체·평면·정면·측면·A-A·B-B를 선택할 수 있습니다.
변경 후 확인 명령:
```bash
npm run build
npm run lint
python3 scripts/extract_block8_model.py
python3 scripts/render_block8_preview.py
python3 scripts/render_block8_drawing_views.py
python3 scripts/build_standalone_html.py
```
## 6. 검증 기록
기준 커밋 시점에 모델 추출 결과는 중심선 1개, 길이 19.980m, 객체 154개였습니다. `npm run build`, `npm run lint`, `git diff --check`가 통과했습니다. 원본 DWG/PDF를 직접 열어 확인하는 검증은 수행하지 못했습니다.
## 7. 커밋 이력
- `0adb717` 도면 기반 모델 작업지침 추가
- `1783d1f` BLOCK8 도면 자료와 1차 분석 기록 추가
- `62c7395` BLOCK8 윤곽 추출 한계와 추가 자료 기록
- `e7d0250` BLOCK8 확인 구간 모델 도식 추가
- `1986680` BLOCK8 기준 보기와 분석·작업 로그 화면 추가
- `9d11b4e` BLOCK8 중심선과 단면 근거로 3D 모델 생성
- `e473a7c` 평면도와 상부평면도 모델을 하나로 통합
원격 저장소에는 푸시하지 않은 로컬 커밋 기록입니다.
## 8. 도면 이미지 함께 보기
- `public/drawing-images/`에는 DXF에서 잘라낸 평면도, 상부평면도, 종단면도, A-A, B-B SVG 이미지가 있습니다.
- 웹 화면의 `도면 함께 보기`를 켜면 현재 뷰와 연결된 도면을 모델 화면 위에 참고 오버레이로 표시합니다. 도면 종류를 바꾸거나 투명도를 조절할 수 있습니다.
- 오버레이는 DXF 픽셀-CAD 좌표 변환으로 3D 기준면에 투영한 검토 이미지입니다. A-A/B-B는 끝 측점 단면에, 종단은 STA/EL 주석에 맞춰 모델 옆에 놓습니다. 평면도와 상부평면도는 같은 구조물이 되도록 도면 배치 차이를 보정하며, 평면 기준면의 EL과 종단의 측면 이격은 보기 위한 임시 위치입니다. 도면 글꼴 및 블록 표현은 SVG에서 단순화될 수 있으므로 최종 치수 검증용으로 사용하지 않습니다.
- DXF에서 벡터를 추출하는 명령: `python3 scripts/render_block8_drawing_views.py`
- 단독 HTML 갱신 순서: `npm run build` 후 `python3 scripts/build_standalone_html.py`
-32
View File
@@ -1,32 +0,0 @@
# 광터지하차도 BLOCK8 커밋 기록
이 파일은 로컬 Git에 저장된 커밋 이력을 사람이 읽기 쉽게 정리한 목록입니다. 최신 커밋부터 표시합니다.
아래 목록은 이 파일을 갱신하기 직전인 `0b17a2a`까지의 기록이며, 이 커밋 로그 파일 자체를 갱신한 후속 커밋은 포함하지 않습니다.
원격 저장소에 푸시하지 않은 로컬 기록입니다.
| 커밋 | 날짜 | 내용 |
|---|---|---|
| `0b17a2a` | 2026-10-20 | 모델 클리핑 기능 추가 |
| `9c55bbc` | 2026-10-20 | BLOCK8 작업 인수인계 파일 추가 |
| `e473a7c` | 2026-10-20 | 평면도와 상부평면도 모델을 하나로 통합 |
| `9d11b4e` | 2026-10-20 | BLOCK8 중심선과 단면 근거로 3D 모델 생성 |
| `e7d0250` | 2026-10-20 | BLOCK8 확인 구간 모델 도식 추가 |
| `1986680` | 2026-10-20 | BLOCK8 기준 보기와 분석·작업 로그 화면 추가 |
| `62c7395` | 2026-10-20 | BLOCK8 윤곽 추출 한계와 추가 자료 기록 |
| `1783d1f` | 2026-10-20 | BLOCK8 도면 자료와 1차 분석 기록 추가 |
| `0adb717` | 2026-10-20 | 도면 기반 모델 작업지침 추가 |
| `aadccee` | 2026-10-20 | 프로젝트 틀만 남기고 초기화 |
| `fac29cb` | 2026-10-20 | 원본 좌표와 브라우저 검증 결과 및 실행 수정 안내 기록 |
| `d01ef8b` | 2026-10-20 | 허용된 19.980m 직선 연결과 검토용 표면 명암 및 그림자 적용 |
| `02eb05b` | 2026-10-20 | 단면 윤곽의 OBJ 파일 및 근거 JSON 내보내기 추가 |
| `0933382` | 2026-10-20 | 한국어 3D 단면 뷰어와 객체 선택 숨김 및 도면 근거 패널 구현 |
| `9072507` | 2026-10-20 | 원본 단면 구조선 114쌍과 공동구 상세 윤곽을 3D 데이터로 구성 |
| `39231d5` | 2026-10-20 | BLOCK7 원본 구조선과 표고 재분석 및 불일치 기록 |
| `100480b` | 2026-10-20 | 원본 도면 1차 분석 기록 |
| `4c331d8` | 2026-10-20 | 광터지하차도 새 모델 프로젝트 시작 |
## 참고
- `aadccee`는 프로젝트 초기화 기준 커밋입니다.
- 초기화 기준 커밋보다 앞선 기록은 기존 저장소 이력으로 보존되어 있어 전체 목록에 함께 표시했습니다.
- 상세 변경 내역은 프로젝트의 작업 로그와 도면 분석 파일을 참고하세요.
-2
View File
@@ -72,8 +72,6 @@ export default function App() {
<nav className="file-links" aria-label="작업 자료">
<a href="/analysis/BLOCK8_3D모델_미리보기.svg" target="_blank" rel="noreferrer">모델 이미지 ↗</a>
<a href="/analysis/도면분석.md" target="_blank" rel="noreferrer">분석 파일 ↗</a>
<a href="/BLOCK8_작업인수인계.md" target="_blank" rel="noreferrer">인수인계 파일 ↗</a>
<a href="/BLOCK8_커밋기록.md" target="_blank" rel="noreferrer">커밋 기록 ↗</a>
<a href="/BLOCK8_모델웹페이지.html" target="_blank" rel="noreferrer">단독 HTML ↗</a>
<a href="/BLOCK8_소스코드_전달자료.zip" download>전체 소스 ZIP ↓</a>
<a href="/작업기록.txt" target="_blank" rel="noreferrer">작업 로그 ↗</a>