이슈 #1 중 코치 몫(SPEC·CLAUDE·검사). - SPEC.md: 지금 지킬 규약만 남긴 새 판 - CLAUDE.md: 이슈 방식 작업 규칙 (하이쿠용) - check.sh: F17 기준을 짧은 README 기준으로 바꾸고 이슈 #1 완료 기준 F47 추가 코치가 저장소 밖 시험 사본(별도 README 참조안)으로 F47 이 통과 가능함을 확인. 이 저장소에서는 README·FEATURES·PROGRESS 작업 전이라 지금 F47 이 실패하는 것이 정상. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f33e00bce4
commit
bdd296597a
3 files changed
+69
-648
No files matched your search
@@ -1,37 +1,23 @@
|
|||||||
# drawing3d-web — 작업 규칙 (Haiku 세션용)
|
# drawing3d-web — 작업 규칙 (Haiku)
|
||||||
|
|
||||||
원형 수직구 일반도를 라이브러리 없이 Canvas 2D 로 보여 주는 3D 뷰어. 설계는 `SPEC.md`, 할 일은 `FEATURES.json`.
|
규약은 `SPEC.md`. **작업 목록과 설계 배경은 Gitea 이슈(tdc_engineer/drawing3d-web)에 있다.**
|
||||||
|
|
||||||
## 시작할 때마다
|
## 시작
|
||||||
1. `PROGRESS.md` 마지막 5줄과 `git log --oneline -5` 를 읽는다.
|
1. 지시받은 이슈를 **제목이나 번호로 찾아 그 이슈만** 읽는다. 다른 이슈·긴 문서를 미리 다 읽지 않는다.
|
||||||
2. `FEATURES.json` 에서 `"passes": false` 인 **맨 앞 기능 하나**만 고른다. 두 개를 같이 하지 않는다.
|
2. `git log --oneline -5` 를 본다.
|
||||||
3. 그 기능의 `steps` 와 SPEC 의 해당 장을 읽고 시작한다.
|
|
||||||
|
|
||||||
## 실행과 검증
|
## 끝 (이슈 하나 = 커밋 하나)
|
||||||
- 화면 보기: `explorer.exe index.html` (Windows 브라우저로 열림). 수석이 화면을 확인한다.
|
1. `bash check.sh` 가 FAIL 0 이어야 끝이다. 아니면 커밋하지 않고 멈춰 보고한다.
|
||||||
- 판정: `bash check.sh F0n` (n = 지금 기능 번호). 그 기능과 앞 기능 항목이 전부 `[PASS]` 여야 끝이다.
|
2. 커밋 메시지 첫 줄 끝에 `(#이슈번호)`, 본문에 check.sh 마지막 줄과 `Co-Authored-By` 줄.
|
||||||
- 끝났다고 말할 때는 **check.sh 출력 전체를 그대로 붙인다.** 요약하거나 고쳐 쓰지 않는다.
|
3. 이슈 댓글(완료 기준 확인 표·검사 결과·커밋 해시·경위 한 줄)과 push·이슈 닫기는 **지시가 있을 때만** 한다.
|
||||||
|
|
||||||
## 끝낼 때 (기능 1개 = 커밋 1개)
|
|
||||||
1. `FEATURES.json` 에서 그 기능의 `passes` 만 `true` 로 바꾼다. 다른 글자는 건드리지 않는다.
|
|
||||||
2. `PROGRESS.md` 에 한 줄 추가(양식은 파일 첫 줄).
|
|
||||||
3. `git add -A` 후 커밋. 메시지 첫 줄 `F0n: 한 일`, 본문에 check.sh 마지막 줄.
|
|
||||||
4. 다음 기능으로 넘어가지 말고 수석에게 보고하고 멈춘다.
|
|
||||||
|
|
||||||
## 금지
|
## 금지
|
||||||
- `check.sh`, `SPEC.md`, `CLAUDE.md` 수정 금지. 틀렸다고 생각되면 고치지 말고 수석에게 말한다.
|
- `check.sh`·`SPEC.md`·`CLAUDE.md` 수정. 틀렸다고 생각되면 고치지 말고 보고한다.
|
||||||
- three.js 등 라이브러리, CDN, `import`, `require`, 외부 URL 금지. 파일은 index.html, viewer.js, model.js, README.md, .gitignore, guide/ (구동설명서 HTML), tools/prep_drawing.py, tools/extract_from_dxf.py, model_extracted.js, compare.html. F33~F34 에서는 model.js 를 고치지 않는다.
|
- 검사를 통과시키려고 파일을 숨기거나 검사를 우회하기.
|
||||||
- 도면은 `private/` 의 공개용 정리본만 커밋한다: `drawing.dxf`·`drawing.svg`·`drawing_gen.svg` (SPEC 34장). 원본 DWG·`full.dxf`·지명·과업명·실제 좌표가 든 파일은 절대 커밋하지 않는다. `git add -A` 전에 `git status` 로 확인한다.
|
- 화면 코드의 라이브러리·CDN·import·외부 URL. 도구는 표준 라이브러리만.
|
||||||
- `model.js` 는 `const MODEL = ` 뒤에 순수 JSON(따옴표 키, 주석·끝 쉼표 없음) 그리고 `;`.
|
- 정리본 3개(`private/drawing.dxf`·`drawing.svg`·`drawing_gen.svg`) 밖의 도면, 원본 DWG, 지명·과업명·실제 좌표, 비밀값 커밋. `git add -A` 전에 `git status`.
|
||||||
- 커밋 메시지의 `Co-Authored-By` 줄을 지우지 않는다. `--no-verify`, `--amend`, `rebase`, 날짜 지정 금지.
|
- `model.js` 는 `const MODEL = ` 뒤 순수 JSON 과 `;`.
|
||||||
- 실패한 시도도 커밋을 지우지 않는다. 되돌릴 때는 새 커밋으로.
|
- `Co-Authored-By` 줄 지우기, `--no-verify`·`--amend`·`rebase`·날짜 지정, 실패한 커밋 지우기.
|
||||||
- 원본 도면 파일(DWG·정리 전 DXF), 회사 파일명, 비밀값을 리포에 넣지 않는다.
|
|
||||||
|
|
||||||
## 코드
|
|
||||||
- 순수 JavaScript 한 파일(viewer.js). 함수 이름은 SPEC 7장 목록을 쓴다.
|
|
||||||
- 길이 단위는 mm, z 위로 +. 각도는 X축에서 반시계, 도.
|
|
||||||
- 면 = 점 배열 + 색 + alpha. 먼 면부터 그린다.
|
|
||||||
|
|
||||||
## 막히면
|
## 막히면
|
||||||
- 같은 문제를 두 번 고쳐도 안 되면 멈추고 수석에게 무엇이 안 되는지 보고한다.
|
같은 문제를 두 번 고쳐도 안 되면 멈추고 무엇이 안 되는지 보고한다. 모르는 치수는 지어내지 않는다.
|
||||||
- 모르는 치수는 지어내지 않는다. SPEC 에 없으면 수석에게 묻는다.
|
|
||||||
@@ -1,615 +1,40 @@
|
|||||||
# SPEC — 전력구 수직구 3D 뷰어 (MVP)
|
# drawing3d-web 설계 규약
|
||||||
|
|
||||||
> 🔒 사내교육용 — 열람 외 재배포 등의 활동을 금함 (회사 도면의 치수를 옮겨 적음)
|
지금 지켜야 할 규약만 둔다. **작업 목록·설계 배경·결정 이력은 Gitea 이슈**에 있고, 옛 설계서(34장)는 git 이력(9068b6c)에 남아 있다.
|
||||||
> 설계 면담(수석 + 코치 세션, 2026-10-19~20)으로 확정. 이 파일은 하이쿠가 고치지 않는다.
|
|
||||||
|
|
||||||
## 1. 목적
|
## 1. 무엇
|
||||||
원형 수직구 일반도 한 장을 **라이브러리 없이** 브라우저 3D 화면으로 보여 준다.
|
원형 수직구 일반도(DWG) 한 장을 도구가 읽어 3D 데이터(`model_extracted.js`)와 도면 그림(`private/drawing_gen.svg`)을 만들고, `index.html` 이 라이브러리 없이 Canvas 2D 로 보여 준다. `model.js` 는 손으로 만든 정답 모델 — 검사 기준이자 `answer.html` 화면.
|
||||||
청중은 교육 담당자와 동료이고, 3분 시연에서 아래 세 문장을 화면을 돌려 가며 말할 수 있으면 끝이다.
|
|
||||||
1. 외경 11m, 깊이 70.3m 원통이다.
|
|
||||||
2. 안에 슬래브 19장 + 하부 슬래브가 있고, 층마다 계단이 지그재그로 내려간다.
|
|
||||||
3. 바닥 근처 19B 층에서 4시 방향으로 TBM 터널(내경 2.6m)이 나가고, 터널 바닥 패드와 19B 패드 상면 높이가 같다.
|
|
||||||
|
|
||||||
## 2. 범위
|
## 2. 좌표·단위
|
||||||
| 넣는 것 | 빼는 것 (범위 밖) |
|
mm, z 위 +, 지표 z 0. 각도는 X축에서 반시계, 도. 모델 XY 는 슬래브 평면 틀이다(평면도는 −30.3° 돌아 있어 views 의 `rot_deg`). 도면 DXF 좌표는 공개용으로 평행이동된 값이다.
|
||||||
|
|
||||||
|
## 3. 데이터 형식
|
||||||
|
`const MODEL = {순수 JSON};` — `units` "mm", `parts[]`, `drawing{src, viewBox, fit, section, plan, views[]}`.
|
||||||
|
부재 공통 `id·kind·type·name·color`. type 별:
|
||||||
|
|
||||||
|
| type | 필드 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 벽체, 중간 슬래브 19장, 19B 패드, 하부 슬래브, 바닥콘크리트 | 상부 분기구·환기구·관로, 지표→1B 구간의 계단·설비 |
|
| disc | cx cy r z0 z1 (holes) |
|
||||||
| 계단 18런(1B→19B) | 슬래브 개구부(계단·케이블 slot·펌프 배출구 구멍), 방호 난간 |
|
| ring | cx cy r_in r_out z0 z1 |
|
||||||
| TBM 터널 세그먼트(속 빈 원통) + 터널 안 잡석·패드 2단 | 집수정 사다리, 집수조·침사조·월류벽, 배관 |
|
| pipe | x y z angle_deg length r_in r_out |
|
||||||
| 등각 초기 화면, 드래그 회전, 휠 확대, 벽 반투명 | 프리셋 뷰, 클릭 선택·속성 패널, 표시/숨김, 축·그리드, 단면, 치수 라벨, PNG 저장 |
|
| fill | x y z angle_deg length r v0 v1 half_width |
|
||||||
|
| stair | z_low z_high risers first_riser riser tread x_top dir y0 y1 |
|
||||||
## 3. 좌표계와 단위
|
| prism | polygon z0 z1 (holes) |
|
||||||
- 데이터의 길이는 모두 **mm**, 각도는 **도**.
|
| zone | r x_min x_max z0 z1 alpha |
|
||||||
- 원점 = 수직구 축 중심 × 지표면(구조물 상단). **Z 위로 +**, 땅속은 음수.
|
| ladder | x0 x1 yc width z0 z1 rung (angle_deg) |
|
||||||
- X = 평면도 오른쪽, Y = 평면도 위쪽. 수평 각도는 **X축에서 반시계**(시계의 3시 = 0°, 12시 = 90°, 4시 = 330°).
|
| obox | cx cy angle_deg s0 s1 t0 t1 z0 z1 |
|
||||||
|
| railing | z h paths |
|
||||||
## 4. 부재와 치수
|
|
||||||
### 4.1 벽체·바닥
|
## 4. 도면 → 데이터 (`tools/`)
|
||||||
| 부재 | 형태 | 치수 |
|
`bash tools/make_model.sh`: DWG → DXF(GNU LibreDWG) → `views.json` 의 그림 상자만 → `extract_from_dxf.py`(+ 도면에 없는 값은 `design_rules.json`) → `model_extracted.js`, `dxf_to_svg.py` → `drawing_gen.svg`. 규칙의 근거는 코드 주석과 이슈.
|
||||||
|---|---|---|
|
|
||||||
| 벽체 | 속 빈 원통 | 외경 11,000(r 5,500), 내경 9,700(r 4,850), z −70,200 ~ 0 |
|
## 5. 검사 원칙
|
||||||
| 하부 슬래브(BOTTOM, 20B) | 원판 | r 4,850, z −70,200 ~ −69,000 (두께 1,200, 벽 안쪽) |
|
- `check.sh` 가 합격 판정이다. 검사를 먼저 커밋하고 그 뒤 구현한다.
|
||||||
| 바닥콘크리트 | 원판 | r 5,500(= 벽 외경, 슬러리월 안에서 타설), z −70,300 ~ −70,200 (두께 100) |
|
- 기준값은 도면에서 가져온다. 코드에서 역산하지 않는다.
|
||||||
|
- 변조 시험: 도면을 바꾸면 결과도 그만큼 바뀌어야 한다(하드코딩 방지).
|
||||||
### 4.2 중간 슬래브 (원판, r 4,850, 두께 300)
|
|
||||||
슬래브 상면 z. 하면 = 상면 − 300.
|
## 6. 공개 규칙
|
||||||
|
커밋하는 도면은 `private/drawing.dxf`·`drawing.svg`·`drawing_gen.svg` 뿐. 원본 DWG·지명·과업명·실제 좌표는 넣지 않는다.
|
||||||
| 층 | 상면 z | 층 | 상면 z | 층 | 상면 z |
|
|
||||||
|---|---|---|---|---|---|
|
## 7. 금지
|
||||||
| 1B | −7,400 | 8B | −30,500 | 15B | −53,600 |
|
화면 코드: 라이브러리·CDN·import·외부 URL. 도구: 표준 라이브러리만(LibreDWG 명령 제외).
|
||||||
| 2B | −10,700 | 9B | −33,800 | 16B | −56,900 |
|
|
||||||
| 3B | −14,000 | 10B | −37,100 | 17B | −60,200 |
|
|
||||||
| 4B | −17,300 | 11B | −40,400 | 18B | −63,500 |
|
|
||||||
| 5B | −20,600 | 12B | −43,700 | 19B | −66,200 |
|
|
||||||
| 6B | −23,900 | 13B | −47,000 | | |
|
|
||||||
| 7B | −27,200 | 14B | −50,300 | | |
|
|
||||||
|
|
||||||
규칙: 1B = −7,400(지표에서 5,400+500+1,300+200). 1B~18B 는 3,300 간격. 18B→19B 2,700. 19B→하부 슬래브 상면 2,800. 총높이 70,300.
|
|
||||||
|
|
||||||
### 4.3 19B 패드
|
|
||||||
원판, 벽 안쪽면에서 100 떨어짐 → r 4,750(지름 9,500), z −66,200 ~ −66,100 (두께 100).
|
|
||||||
|
|
||||||
### 4.4 계단 (18런)
|
|
||||||
- 런 i 는 i 층 슬래브(위)에서 i+1 층 슬래브(아래)로 내려간다. i = 1~18 (마지막 런은 18B→19B).
|
|
||||||
- 단 높이 200, **맨 아래 첫 단만 100**. 디딤판 깊이 300. 폭 1,000.
|
|
||||||
- 층고 3,300 → 단 수 17(= 100 + 200×16), 디딤판 16장, 런 길이 4,800
|
|
||||||
- 층고 2,700(런 18) → 단 수 14(= 100 + 200×13), 디딤판 13장, 런 길이 3,900
|
|
||||||
- 계단은 디딤판마다 **블록**(아래 슬래브 상면부터 그 디딤판 높이까지 꽉 찬 상자)으로 쌓는다.
|
|
||||||
- 위치 (🔶 확인 필요 — 코치 추론):
|
|
||||||
- 런의 윗끝 = 위 슬래브의 계단 개구부 가장자리. 개구부(4,200)는 X 가운데 −2,100 ~ +2,100 으로 본다.
|
|
||||||
- 홀수 런: 윗끝 x = −2,100, 내려가며 +X 방향, Y 띠 −1,000 ~ 0.
|
|
||||||
- 짝수 런: 윗끝 x = +2,100, 내려가며 −X 방향, Y 띠 0 ~ +1,000.
|
|
||||||
- 블록 k(아래부터 1, 2, …, 단 수−1)의 윗면 z = 아래 슬래브 상면 + 100 + 200×(k−1). 맨 위 단(200)은 위 슬래브로 올라선다.
|
|
||||||
- 블록 k 의 X 구간: 윗끝 x 에서 (단 수−1−k)×300 ~ (단 수−k)×300 만큼 내려가는 방향으로 떨어진 곳.
|
|
||||||
- 개구부를 그리지 않으므로 맨 위 블록이 위 슬래브 두께에 100 겹친다. MVP 에서는 받아들인다.
|
|
||||||
|
|
||||||
### 4.5 TBM 터널
|
|
||||||
- 방향 330°(평면도 4시). 축 높이 z −65,050.
|
|
||||||
- 세그먼트: 속 빈 원통, 두께 200, 내경 2,600(r 1,300), 외경 3,000(r 1,500).
|
|
||||||
- 원통은 **벽 안쪽면(r 4,850)에서 시작**해 벽을 관통하고 벽 바깥면에서 20,000 더 나간다 → 길이 20,650. 시작점 = (4,850·cos330°, 4,850·sin330°, −65,050).
|
|
||||||
- 근거(수석): 세그먼트가 벽체 개구부의 거푸집 역할을 해 18B 슬래브와 19B 벽체부를 동시 타설한다. 그래서 세그먼트 천장 외면(−63,550)이 18B 슬래브 두께(−63,800 ~ −63,500) 안에 들어가는 것은 실제 형태다.
|
|
||||||
- 높이 검산: 세그먼트 바닥 외면 −66,550(= 19B 슬래브 하면 −66,500 − 50) → 내면 −66,350 → 잡석 상면 −66,225 → 패드 상면 −66,100 = **19B 패드 상면**.
|
|
||||||
- 터널 안 채움(누운 원통 속을 높이까지 채움). 높이 v 는 터널 축에서 잰 값(위 +).
|
|
||||||
|
|
||||||
| 채움 | v 범위 | 폭 제한 |
|
|
||||||
|---|---|---|
|
|
||||||
| 잡석 | −1,300 ~ −1,175 (125) | 세그먼트 안쪽 벽까지 |
|
|
||||||
| 패드 2단(아래) | −1,175 ~ −1,100 (75) 🔶 | 세그먼트 안쪽 벽까지 |
|
|
||||||
| 패드 1단(위) | −1,100 ~ −1,050 (50) 🔶 | 축 중심 폭 800 |
|
|
||||||
|
|
||||||
🔶 확인 필요: 패드 합계 125 를 「양측 트렌치 깊이 50」에 맞춰 75 + 50 으로 나눈 것은 코치 추론.
|
|
||||||
|
|
||||||
## 5. 데이터 파일 `model.js`
|
|
||||||
- 첫 줄은 `const MODEL = ` 로 시작하고 그 뒤는 **순수 JSON**(따옴표 있는 키, 주석·끝 쉼표 없음), 마지막은 `;`. check.sh 가 이 꼴을 python 으로 읽는다.
|
|
||||||
- 최상위: `units`("mm"), `parts`(배열).
|
|
||||||
- 모든 부재 공통 필드: `id`(겹치지 않음), `name`(한글 이름), `kind`, `type`, `color`("#rrggbb"), `alpha`(0~1, 없으면 1).
|
|
||||||
- `kind` 값: `wall`, `slab`, `pad`, `bottom`, `lean`, `stair`, `tunnel`, `fill`.
|
|
||||||
|
|
||||||
| type | 쓰는 kind | 필드 |
|
|
||||||
|---|---|---|
|
|
||||||
| `ring` (세운 속 빈 원통) | wall | `cx, cy, z0, z1, r_out, r_in` |
|
|
||||||
| `disc` (세운 원판) | slab, pad, bottom, lean | `cx, cy, z0, z1, r` (z0 아래, z1 위) |
|
|
||||||
| `pipe` (누운 속 빈 원통) | tunnel | `x, y, z`(축 시작점), `angle_deg, length, r_out, r_in` |
|
|
||||||
| `stair` (계단 런) | stair | `z_low, z_high, risers, first_riser, riser, tread, x_top, dir(+1/−1), y0, y1` |
|
|
||||||
| `fill` (누운 원통 속 채움) | fill | `x, y, z, angle_deg, length`(pipe 와 같은 축), `r`(세그먼트 안쪽 반지름), `v0, v1, half_width`(0 이면 제한 없음) |
|
|
||||||
|
|
||||||
부재 수: wall 1 · slab 19 · pad 1 · bottom 1 · lean 1 · stair 18 · tunnel 1 · fill 3 = 45.
|
|
||||||
색은 자유. 단 벽은 `alpha` 0.25.
|
|
||||||
|
|
||||||
## 6. 화면
|
|
||||||
- `index.html` 을 브라우저로 **더블클릭해서 열면** 바로 보인다(서버·인터넷 불필요). 캔버스 하나가 창을 채운다.
|
|
||||||
- 처음 화면: **등각 정사 투영**, 모델 전체(지하 70m 원통과 20m 터널)가 화면에 들어온다.
|
|
||||||
- 마우스 드래그: 궤도 회전(좌우 = Z축 둘레, 위아래 = 기울기). 휠: 확대/축소.
|
|
||||||
- 면은 방향에 따라 밝기가 다르다(면 음영). 먼 면부터 그린다(화가 알고리즘).
|
|
||||||
- 원은 32각형. 벽만 반투명(0.25), 나머지는 불투명.
|
|
||||||
|
|
||||||
## 7. 그리기 방식 (함수 이름 목록 — 구현은 하이쿠)
|
|
||||||
- 부재 → 면: `facesRing`, `facesDisc`, `facesPipe`, `facesStair`, `facesFill`, `facesBox`
|
|
||||||
- 모든 면 모으기: `buildFaces`
|
|
||||||
- 카메라: `rotatePoint`, `project`
|
|
||||||
- 그리기: `shadeColor`, `sortFaces`, `draw`
|
|
||||||
- 입력: `onMouseDown`, `onMouseMove`, `onMouseUp`, `onWheel`
|
|
||||||
- 라이브러리·외부 파일 불러오기 금지. 파일은 `index.html`, `viewer.js`, `model.js` 셋(+ README.md).
|
|
||||||
|
|
||||||
## 8. 합격 기준
|
|
||||||
### 8.1 check.sh (기계 판정)
|
|
||||||
`bash check.sh` → 항목마다 `[PASS]`/`[FAIL]`, 전부 PASS 면 종료 코드 0. 항목 이름 앞에 기능 ID(F01…)가 붙는다.
|
|
||||||
- 파일 셋이 있고 `index.html` 이 `model.js` 다음에 `viewer.js` 를 읽는다.
|
|
||||||
- 외부 라이브러리 흔적이 없다(`http://`, `https://`, `import `, `require(`).
|
|
||||||
- `model.js` 가 위 꼴의 JSON 이고, 부재 수·치수가 4장 표와 같다(허용 오차 1mm).
|
|
||||||
|
|
||||||
### 8.2 수석 육안 체크리스트 (check.sh 로 못 재는 것)
|
|
||||||
- [ ] 더블클릭으로 열린다(Chrome, Edge)
|
|
||||||
- [ ] 첫 화면이 등각이고 모델 전체가 보인다
|
|
||||||
- [ ] 드래그 회전·휠 확대가 된다
|
|
||||||
- [ ] 벽 너머로 슬래브·계단·터널이 보인다
|
|
||||||
- [ ] 계단이 층마다 방향을 바꾼다
|
|
||||||
- [ ] 터널이 평면 4시 방향, 19B 높이에 붙어 있다
|
|
||||||
- [ ] 1장의 세 문장을 화면으로 말할 수 있다
|
|
||||||
|
|
||||||
## 9. 나중 (MVP 뒤, 시간이 남으면)
|
|
||||||
프리셋 뷰·리셋 → 클릭 선택·속성 패널 → 부재별 표시/숨김 → 축·그리드·단위 → 슬래브 개구부 → 단면·거리·치수 라벨·PNG.
|
|
||||||
|
|
||||||
## 10. 추가 — 도면 분할 화면과 도면 클릭 연결 (2026-10-20 수석 지시, F13~F15)
|
|
||||||
### 10.1 화면 배치 (F13)
|
|
||||||
- `index.html` 본문: 왼쪽 `#drawPane` · 가운데 세로 막대 `#splitter`(폭 6px) · 오른쪽 `#viewPane`(기존 캔버스).
|
|
||||||
- 처음 비율 50:50. 막대를 끌면 왼쪽 폭이 바뀐다(창 폭의 15%~85%). 바뀔 때마다 캔버스 크기를 오른쪽 칸에 맞추고 다시 그린다.
|
|
||||||
- 도면 파일은 `private/drawing.svg` 고정. `private/` 는 `.gitignore` 로 커밋하지 않는다(제출 압축에만 넣는다). 파일이 없으면 왼쪽 칸에 「도면 파일 없음 — private/drawing.svg 를 넣으세요」.
|
|
||||||
|
|
||||||
### 10.2 도면 표시·확대·이동 (F14)
|
|
||||||
- `#drawPane` 안에 `#drawStage` 하나, 그 안에 `<img id="drawing">`(도면)과 같은 크기로 겹친 인라인 `<svg id="hotspots" viewBox="0 0 443 443">`(클릭 영역). 확대·이동은 `#drawStage` 의 CSS transform 으로 한다.
|
|
||||||
- 처음 화면: 도면 SVG 좌표 상자 `fit`(아래 MODEL.drawing) 이 왼쪽 칸에 꽉 차게(`fitDrawing`).
|
|
||||||
- 휠 = 마우스 위치 기준 확대/축소(0.5~40배), 드래그 = 이동.
|
|
||||||
|
|
||||||
### 10.3 도면 ↔ 부재 연결 (F15)
|
|
||||||
- `model.js` 최상위에 `drawing` 추가:
|
|
||||||
`src` "private/drawing.svg" · `viewBox` [0, 0, 443, 443] · `fit` [118, 160, 245, 250](u0, v0, u1, v1) · `section` {x0 139.0, y0 174.08, mm_per_unit 1000} · `plan` {cx 227.51, cy 177.1, mm_per_unit 1000}
|
|
||||||
- 좌표 (u = 도면 가로, v = 도면 세로, 아래로 +):
|
|
||||||
- 단면 A-A: u = x0 + X/1000, v = y0 − z/1000 (도면 선으로 확인: 벽 바깥 133.5/144.5, 안쪽 134.15/143.85, 1B 상면 181.48)
|
|
||||||
- 평면도: u = cx + x/1000, v = cy − y/1000
|
|
||||||
- 부재별 클릭 영역(다각형, 도면 단위). 아래 순서대로 만들고, 겹치면 나중 것이 위:
|
|
||||||
1. stair: 단면, u = x0 ± 4.85, v = y0 − (z_high − 300)/1000 ~ y0 − z_low/1000
|
|
||||||
2. wall: 단면 두 띠, u = x0 − 5.5 ~ x0 − 4.85 와 x0 + 4.85 ~ x0 + 5.5, v = y0 − z1/1000 ~ y0 − z0/1000
|
|
||||||
3. tunnel: 평면, 축 시작점에서 angle_deg 방향으로 length, 폭 ±r_out 인 사각형
|
|
||||||
4. slab, pad, bottom, lean (disc): 단면, u = x0 ± r/1000, v = y0 − z1/1000 ~ y0 − z0/1000
|
|
||||||
5. fill: 영역 없음
|
|
||||||
- 동작: 영역 위에 마우스 → 노란 테두리. 클릭 → 그 부재 선택: 도면 영역을 #ffd400(불투명도 0.35)으로 칠하고, 3D 에서 그 부재를 #ffd400·alpha 1 로 그리고, 3D 칸 왼쪽 위에 「선택: 부재 이름」. 영역 밖 클릭 → 선택 해제.
|
|
||||||
- 함수 이름: `onSplitDown`, `onSplitMove`, `onSplitUp`, `fitDrawing`, `onDrawWheel`, `onDrawDown`, `onDrawMove`, `onDrawUp`, `sectionPoly`, `planPoly`, `buildHotspots`, `selectPart`
|
|
||||||
- 3D → 도면 방향(3D 클릭으로 도면 표시)은 하지 않는다(나중).
|
|
||||||
|
|
||||||
## 11. 추가 — 제출 문서 두 개 (2026-10-20 수석 지시, F16~F17)
|
|
||||||
제출물은 git log, 코드, 문서. 문서는 둘로 나눈다. 둘 다 첫머리에 🔒 한 줄. 사실만 쓰고, git log·PROGRESS.md·SPEC.md 에 근거가 있는 내용만 쓴다.
|
|
||||||
|
|
||||||
### 11.1 `구동설명서.md` — 무엇을 어떤 기획으로 만들었고 어떻게 돌리나 (F16)
|
|
||||||
제목 순서(## 단계 제목에 아래 낱말을 그대로 넣는다):
|
|
||||||
1. 목적 — 무엇을 누구에게 보여 주는가(SPEC 1장 세 문장)
|
|
||||||
2. 기획 — 도면 선정 → 범위 결정 → 설계 면담으로 치수·규칙 확정(SPEC 3~5장 요지) → SPEC·FEATURES·check.sh → 기능 하나씩 커밋. 결정된 것 중 핵심 5개 이상(예: 지표 = z 0, 터널 패드 = 19B 패드 높이, 계단 첫 단 100)
|
|
||||||
3. 범위 — 넣은 것 / 뺀 것 (SPEC 2장, 9장)
|
|
||||||
4. 구동 — 더블클릭으로 연다, 도면은 `private/drawing.svg` 에 둔다, 브라우저
|
|
||||||
5. 조작 — 3D 드래그·휠, 가운데 막대, 도면 확대·이동, 도면 클릭 → 3D 강조
|
|
||||||
6. 작업 방식 — WSL2, Claude Code(Haiku 5.5)가 코드 작성·수정·커밋, 합격 판정은 check.sh, 설계 면담·검토와 F02 이후 하이쿠 호출은 수석 위임으로 상위 모델 코치 세션이 맡음, 기능 1개 = 커밋 1개
|
|
||||||
7. 한계 — 계단은 위에서 보면 슬래브에 가려 옆에서 본다, 슬래브 개구부 없음, 3D → 도면 방향 없음 등
|
|
||||||
8. 이력 — `git log --oneline` 결과 표(해시·첫 줄)
|
|
||||||
|
|
||||||
### 11.2 `README.md` — 코드 설명 (F17, 기존 README 를 고쳐 쓴다)
|
|
||||||
제목 순서: 파일 구조 · 실행 · 데이터(model.js) · 코드(viewer.js) · 그리기 방식 · 도면 연결 · 검사
|
|
||||||
- 데이터: type 별 필드 표(SPEC 5장), drawing 항목(SPEC 10.3)
|
|
||||||
- 코드: SPEC 7장·10.3 의 함수 이름을 **모두** 적고 각각 한 줄 설명
|
|
||||||
- 그리기 방식: 회전·정사 투영·면 음영·먼 면부터(화가 알고리즘)·벽 반투명
|
|
||||||
- 도면 연결: 단면·평면 좌표 변환식
|
|
||||||
- 검사: `bash check.sh`, `bash check.sh F0n`
|
|
||||||
|
|
||||||
## 12. 변경 — 구동설명서를 HTML 위키로 (2026-10-20 수석 지시, F18)
|
|
||||||
- 11.1 의 `구동설명서.md` 를 HTML 여러 쪽으로 옮기고, md 파일은 지운다(내용은 그대로 옮기고 git 기록에는 남는다).
|
|
||||||
- 폴더 `guide/`: 목차 `index.html` + 8쪽. 파일 이름(쪽 제목): `purpose.html`(목적) · `plan.html`(기획) · `scope.html`(범위) · `run.html`(구동) · `control.html`(조작) · `method.html`(작업 방식) · `limits.html`(한계) · `history.html`(이력). 모양은 `guide/guide.css` 하나.
|
|
||||||
- 모든 쪽(목차 포함):
|
|
||||||
- 맨 위에 「← 3D 화면으로 돌아가기」 버튼 — `../index.html` 로 가는 링크
|
|
||||||
- 🔒 한 줄, `<h1>` 쪽 제목
|
|
||||||
- 위키처럼 본문 낱말에 다른 쪽 링크를 건다(예: 기획 쪽의 「범위」 → scope.html). 쪽마다 다른 쪽 링크 2개 이상
|
|
||||||
- 아래에 「목차」 링크와 이전/다음 쪽 링크
|
|
||||||
- 3D 화면(`index.html`): 오른쪽 3D 칸 **오른쪽 위**에 「구동 설명서」 버튼 `<a id="guideLink" href="guide/index.html">`. 3D 드래그를 방해하지 않게 작게.
|
|
||||||
- 라이브러리·외부 URL 금지는 그대로. 파일 목록에 `guide/` 추가.
|
|
||||||
|
|
||||||
## 13. 추가 — 선명한 도면과 모든 그림 연결 (2026-10-20 수석 지시, F19~F20)
|
|
||||||
### 13.1 선명한 도면 (F19)
|
|
||||||
- 원인: 도면 SVG 의 선 굵기가 0.75 도면단위로 박혀 있어 확대할수록 굵어지고, `#drawStage` 를 CSS scale 로 키워 그림이 뭉개진다.
|
|
||||||
- `tools/prep_drawing.py`(python3, 표준 라이브러리만): `python3 tools/prep_drawing.py <원본.svg> private/drawing.svg`. 원본은 고치지 않고, 출력 SVG 는 0 이 아닌 stroke-width 를 모두 1 로 바꾸고 루트에 `<style>` 로 모든 요소에 `vector-effect: non-scaling-stroke` 를 건다(화면에서 1px 선). 원본 파일 이름·경로를 코드나 문서에 적지 않는다.
|
|
||||||
- 확대·이동은 CSS scale 대신 `#drawing` 과 `#hotspots` 의 **실제 width/height** 를 배율에 맞춰 바꾸고(브라우저가 벡터를 다시 그림), 위치만 translate 로 옮긴다. 함수 `applyDrawView`.
|
|
||||||
|
|
||||||
### 13.2 모든 그림 연결 (F20)
|
|
||||||
- `MODEL.drawing.views` 배열을 추가한다(10.3 의 section·plan 은 그대로 둔다). 값은 아래 표 그대로.
|
|
||||||
|
|
||||||
| id | type | 그림 | 기준 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| secA | section | 단면 A-A | x0 139.0, y0 174.08 |
|
|
||||||
| secB | section | 단면 B-B | x0 179.01, y0 174.08 |
|
|
||||||
| secA2 | section_break | 단면 A-A (4B~15B 생략) | x0 263.41, y0_upper 173.04, y0_lower 140.04, z_upper_min −17600, z_lower_max −53600 |
|
|
||||||
| secB2 | section_break | 단면 B-B (4B~15B 생략) | x0 284.23, 나머지 secA2 와 같음 |
|
|
||||||
| plan1 | plan | 평면도 | cx 227.51, cy 177.1, tunnel true |
|
|
||||||
| plan2 | plan | 평면도 2 | cx 227.51, cy 197.1, tunnel false |
|
|
||||||
| slabOdd | slabplan | 중간슬래브 (홀수층) | cx 227.51, cy 226.31, kind slab, z1 = 1B·3B·…·17B 상면 |
|
|
||||||
| slabEven | slabplan | 중간슬래브 (짝수층) | cx 252.27, cy 226.31, kind slab, z1 = 2B·4B·…·16B 상면 |
|
|
||||||
| slab18 | slabplan | 중간슬래브 (18B) | cx 277.02, cy 226.31, kind slab, z1 [−63500] |
|
|
||||||
| slab19 | slabplan | 중간슬래브 (19B) | cx 227.51, cy 247.32, kind slab, z1 [−66200], tunnel true |
|
|
||||||
| slab20 | slabplan | 하부슬래브 (20B) | cx 252.27, cy 247.32, kind bottom, z1 [−69000] |
|
|
||||||
|
|
||||||
- 그림별 클릭 영역(모두 도면 단위, 각 view 의 기준으로 계산):
|
|
||||||
- section: 10.3 규칙 그대로(stair → wall → slab·pad·bottom·lean). x0·y0 만 view 값.
|
|
||||||
- section_break: section 과 같되, 부재의 z 범위가 전부 z_upper_min 이상이면 y0_upper, 전부 z_lower_max 이하이면 y0_lower 로 계산하고, 그 밖(생략 구간에 걸친 것)은 영역 없음. 벽은 위 조각(0 ~ z_upper_min)과 아래 조각(z_lower_max ~ 바닥) 두 개로 나눈다.
|
|
||||||
- plan: 고리(반지름 4.85 ~ 5.5, 32각형 두 개를 evenodd 로) → 벽. tunnel true 면 10.3 의 터널 사각형도(그 view 의 cx·cy 기준).
|
|
||||||
- slabplan: 안쪽 원판(반지름 4.85) → z1 상면이 일치하는 kind 부재 **전부를 한 번에** 선택. 고리 → 벽. slab19 는 19B 패드도 함께, tunnel true 면 터널 사각형.
|
|
||||||
- 선택은 여러 부재를 한 번에 할 수 있다: `selectPart` 가 부재 id 배열을 받는다. 「선택:」 글자는 view 이름과 부재 이름(여럿이면 「1B, 3B, … 17B」처럼).
|
|
||||||
- 함수: `viewHotspots`(view 하나의 영역 목록), `buildHotspots` 는 views 를 돈다.
|
|
||||||
- 참고(구현하지 않음): 슬래브 평면에 있는 개구부(계단·케이블 slot·펌프 배출구·전기 배선구)는 3D 에 없다(2장 범위 밖). 그래서 3D 슬래브는 꽉 찬 원판이고, 계단 맨 윗단이 슬래브를 뚫고 보인다. 개구부를 넣으려면 슬래브 평면에서 개구 위치·크기를 읽어 원판 윗면·아랫면을 구멍 난 다각형(evenodd)으로 그려야 한다 — 다음 단계 후보.
|
|
||||||
|
|
||||||
## 14. 변경 — 구동설명서는 사용 설명서 (2026-10-20 수석 지시)
|
|
||||||
- `guide/` 는 누구나 이 웹을 작동할 수 있게 하는 **사용 설명서**다. 수석 지시로 코치 세션이 직접 쓴다. 파일 이름·목차·돌아가기 버튼 규칙(12장)과 check.sh 검사 항목은 그대로 지킨다.
|
|
||||||
|
|
||||||
## 15. 추가 — 슬래브 구멍, 계단 방향 정정, 카메라 이동, 사람 애니메이션 (2026-10-20 수석 지시, F21~F23)
|
|
||||||
### 15.1 슬래브 구멍 (F21)
|
|
||||||
- 슬래브 평면 도면에서 읽은 구멍(슬래브 중심 기준 mm, X 오른쪽·Y 위쪽). 노란 선(방호 난간)이 비어 있는 쪽이 계단 입구.
|
|
||||||
- disc 부재에 `holes` 필드: 다각형 목록, 다각형은 [x, y] 점 목록(mm). 구멍이 없으면 필드를 생략하거나 빈 목록.
|
|
||||||
|
|
||||||
| 구멍 | 다각형 (mm) |
|
|
||||||
|---|---|
|
|
||||||
| T_odd (계단 + 위 slot) | (−2100,0) (2100,0) (2100,1000) (1250,1000) (1250,2200) (−1250,2200) (−1250,1000) (−2100,1000) |
|
|
||||||
| T_even (계단 + 아래 slot) | (−2100,0) (2100,0) (2100,−1000) (1250,−1000) (1250,−2200) (−1250,−2200) (−1250,−1000) (−2100,−1000) |
|
|
||||||
| slot_up | 사각형 x −1250~1250, y 1000~2200 |
|
|
||||||
| slot_dn | 사각형 x −1250~1250, y −2200~−1000 |
|
|
||||||
| stair_even | 사각형 x −2100~2100, y −1000~0 |
|
|
||||||
| pump | 사각형 x −750~750, y 3650~4790 |
|
|
||||||
| pump_pad | 사각형 x −750~750, y 3650~4750 |
|
|
||||||
| elec | 사각형 x 2080~3070, y 3530~3750 (벽에 붙은 1000×300, 벽 안쪽까지만) |
|
|
||||||
|
|
||||||
| 부재 | holes |
|
|
||||||
|---|---|
|
|
||||||
| 홀수층 슬래브 1B·3B·…·17B | T_odd, slot_dn, pump, elec |
|
|
||||||
| 짝수층 슬래브 2B·4B·…·16B | T_even, slot_up, pump, elec |
|
|
||||||
| 18B 슬래브 | stair_even, pump, elec (slot 없음 — 수석 확인 Q1) |
|
|
||||||
| 19B 슬래브 | pump (집수정 사다리도 이 구멍으로 — 수석 확인 Q2) |
|
|
||||||
| 19B 패드 | pump_pad |
|
|
||||||
| 하부 슬래브·바닥콘크리트 | 없음 |
|
|
||||||
|
|
||||||
- 그리기: 원판 윗면·아랫면 = 바깥 32각형 + 구멍 다각형을 한 면으로 묶어 evenodd 로 칠한다. 구멍 둘레마다 세로 옆면을 더한다. 함수 `facesDiscHoles`.
|
|
||||||
|
|
||||||
### 15.2 계단 방향 정정 (F21, 4.4 의 🔶 추론을 도면으로 바로잡음)
|
|
||||||
- 홀수 런(1·3·…·17, 홀수층 슬래브에서 내려감): x_top **+2100**, dir **−1**, Y 띠 **0 ~ +1000**
|
|
||||||
- 짝수 런(2·4·…·18, 짝수층·18B 에서 내려감): x_top **−2100**, dir **+1**, Y 띠 **−1000 ~ 0**
|
|
||||||
- 블록 계산식(4.4)은 그대로.
|
|
||||||
|
|
||||||
### 15.3 카메라 이동 (F22)
|
|
||||||
- 3D 는 보는 중심 `CAM.target`[x, y, z](처음 = 모델 중심)을 기준으로 그린다.
|
|
||||||
- 왼쪽 드래그 = 회전(그대로). **오른쪽 드래그 또는 Shift + 왼쪽 드래그 = 화면 이동**(target 을 화면 평면에서 옮김). 캔버스에서 오른쪽 클릭 메뉴는 막는다.
|
|
||||||
- 휠 = **마우스 아래 점을 고정한 채** 확대·축소, 배율 0.5~**40**.
|
|
||||||
- 더블클릭 또는 3D 칸 오른쪽 위 `<button id="resetView">처음 화면</button>` = 처음 등각 화면(yaw·pitch·배율·target 초기값).
|
|
||||||
- 함수 `panView`, `zoomAt`, `resetView`.
|
|
||||||
|
|
||||||
### 15.4 사람 애니메이션 (F23)
|
|
||||||
- 사람 = 상자 4개(mm): 다리 2개 150×150×800(좌우 100 간격), 몸통 400×250×600(z 800~1400), 머리 220×220×220(z 1450~1670). 색 #ff8c1a, 불투명. 데이터 파일에는 넣지 않고 viewer.js 가 그린다.
|
|
||||||
- 경로 `buildWalkPath`: 계단 데이터(stair 18개)로 만든다. 런마다
|
|
||||||
1. 위 슬래브 위, 입구 앞 300 지점 (x_top − dir·300, 띠 중심 y, z_high)
|
|
||||||
2. 디딤판마다 가운데 점: k = 단 수−1 … 1 순서로 x = x_top + dir·300·(단 수 − 1 − k + 0.5), z = z_low + 100 + 200·(k − 1)
|
|
||||||
3. 아래 슬래브에 내려선 점 (x_top + dir·300·단 수, 띠 중심 y, z_low)
|
|
||||||
4. 다음 런 입구 앞 점으로 옆걸음(같은 높이)
|
|
||||||
- 1B 런 입구에서 19B 까지 내려가고, 같은 길로 1B 까지 올라오기를 쉬지 않고 반복(속도 1000 mm/s, 점 사이 직선 보간). 함수 `walkerAt`(시각 → 위치), `facesPerson`, `animate`(requestAnimationFrame).
|
|
||||||
- 3D 칸 오른쪽 위 `<button id="walkToggle">` — 움직일 때 「사람 ❚❚」, 멈췄을 때 「사람 ▶」. 처음에는 움직인다.
|
|
||||||
- 멈춰 있을 때는 지금처럼 필요할 때만 다시 그린다. 느리면 초당 30번으로 줄인다.
|
|
||||||
|
|
||||||
## 16. 추가 — 바닥부: 월류벽·집수조·침사조·침사구·사다리 (2026-10-20 수석 사전 승인, F24~F25)
|
|
||||||
도면 단면 A-A 하부와 19B·20B 슬래브 평면에서 읽음(mm, 슬래브 중심 기준).
|
|
||||||
- **정정**: 15.1 의 「19B 집수정 사다리는 펌프 배출구로(Q2)」는 도면과 다르다. 19B 동쪽 벽 쪽에 **침사구 1000×1000** 구멍이 따로 있고, 사다리는 그 아래 동쪽 벽에 붙어 있다. (수석 부재 중 도면 근거로 코치가 바로잡음)
|
|
||||||
|
|
||||||
### 16.1 부재 (F24)
|
|
||||||
| 부재 | kind | type | 값 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 월류벽 (넘침 홈 양옆 2, 홈 아래 1) | weir | prism | ① 다각형 사각형 X 1400~1600, Y −4580~−500, z −69000~−67000 ② Y 500~4580, 같은 z ③ Y −500~500, z −69000~−67500 (홈 깊이 500) |
|
|
||||||
| 집수조 (펌프 쪽, 서쪽) | pit | zone | r 4850, x_min −4850, x_max 1400, z −69000~−67000, 반투명 0.25 |
|
|
||||||
| 침사조 (터널 쪽, 동쪽) | pit | zone | r 4850, x_min 1600, x_max 4850, z −69000~−67000, 반투명 0.25 |
|
|
||||||
| 사다리 | ladder | ladder | x0 4668, x1 4708(기둥 두께), yc 0, width 400, z0 −68700, z1 −66200, rung 300 |
|
|
||||||
| 19B 슬래브 구멍 추가 | — | — | 침사구 사각형 X 3850~4820, Y −500~500 |
|
|
||||||
| 19B 패드 구멍 추가 | — | — | 침사구 사각형 X 3850~4720, Y −500~500 |
|
|
||||||
|
|
||||||
- `prism`: `polygon`([x, y] 점 목록), `z0`, `z1` — 다각형을 세로로 늘인 기둥(윗면·아랫면·옆면). 함수 `facesPrism`.
|
|
||||||
- `zone`: 원(반지름 r) 안에서 x_min ≤ X ≤ x_max 인 부분을 z0~z1 로 세운 덩어리(활꼴 기둥). 함수 `facesZone`.
|
|
||||||
- `ladder`: 기둥 2개(X x0~x1, Y = yc ± width/2 에 두께 40), 가로대(rung 간격, 두께 25)를 상자로. 함수 `facesLadder`.
|
|
||||||
- 색은 자유(물칸은 서로 다른 옅은 색). 🔶 사다리 폭 400·가로대 300 은 도면에 없어 코치가 정한 값(산업안전보건기준 사다리 폭 30cm 이상 참고).
|
|
||||||
|
|
||||||
### 16.2 도면 연결 (F25)
|
|
||||||
- 단면 A-A 두 그림(view id `secA`, `secA2`)에서: 월류벽·집수조·침사조·사다리를 단면 규칙(u = x0 + X/1000, v = y0 − z/1000)으로 X 범위 × z 범위 사각형 영역. 순서 집수조 → 침사조 → 월류벽 → 사다리(나중 것이 위).
|
|
||||||
- 하부슬래브(20B) 평면(`slab20`)에서: 위 네 부재를 평면 규칙(u = cx + X/1000, v = cy − Y/1000)으로. 집수조·침사조는 원과 세로선으로 자른 활꼴 다각형.
|
|
||||||
- 19B 슬래브 평면(`slab19`)의 침사구 자리를 누르면 사다리를 선택.
|
|
||||||
- 함수 `bottomHotspots`.
|
|
||||||
|
|
||||||
## 17. 추가 — 상부 구조·환기구·방호 난간·배관 (2026-10-20 수석 사전 승인, F26~F29)
|
|
||||||
목표: 2D 도면에 있는 것을 모두 3D 로(수석 지시). 단면 A-A 상부, 단면 B-B(환기 덕트를 펼친 단면), 평면도 1·2, 슬래브 평면 노란 선에서 읽음. 🔶 = 도면에 수치가 없어 코치가 정한 값.
|
|
||||||
|
|
||||||
### 17.1 상부 슬래브·맨홀 목·벽 상단 (F26)
|
|
||||||
- **정정**: 벽체 윗면은 지표가 아니라 상부 슬래브 밑면 **z −2000** (벽 위로는 흙 1500 과 맨홀 목). 4.1 의 벽체 z1 0 → −2000.
|
|
||||||
- 상부 슬래브: kind `topslab`, type disc, r 5500(벽 바깥면까지), z −2000 ~ −1500, holes: 원 (0, 0, 450), 원 (2223, 3803, 450).
|
|
||||||
- holes 항목은 다각형 외에 `{"circle": [x, y, r]}` 도 된다(16각형으로 그림). prism 에도 `holes` 를 쓸 수 있다. 함수 `holePolygon`.
|
|
||||||
- 맨홀 목 2개: kind `neck`, type prism, z −1500 ~ 0, 각각 1300 정사각형을 30° 돌린 다각형 + 가운데 원 구멍 r 450.
|
|
||||||
- 목 1: [[238, 888], [-888, 238], [-238, -888], [888, -238]], holes [{"circle": [0, 0, 450]}]
|
|
||||||
- 목 2: [[2461, 4691], [1335, 4041], [1985, 2915], [3111, 3565]], holes [{"circle": [2223, 3803, 450]}]
|
|
||||||
- **정정**: 전기 배선구 구멍은 벽을 따라 돈 1000×300 사각형이다(난간 선으로 확인). 15.1 의 elec 를 다각형 [[2079, 4079], [2922, 3528], [3072, 3753], [2229, 4304]] 로 바꾼다(홀수층·짝수층·18B).
|
|
||||||
|
|
||||||
### 17.2 분기구·환기구 (F27)
|
|
||||||
- `obox`(돌린 상자): `cx, cy, angle_deg, s0, s1, t0, t1, z0, z1`. 점 = (cx, cy) + s·(cosθ, sinθ) + t·(−sinθ, cosθ). 함수 `facesObox`.
|
|
||||||
- 분기구 kind `branch`, cx = cy = 0, 안 2200×2100, 벽 350, 바닥 450:
|
|
||||||
|
|
||||||
| 이름 | angle | s | t | z |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 바닥 | 167 | 4850 ~ 8350 | -1450 ~ 1450 | -4550 ~ -4100 |
|
|
||||||
| 지붕 | 167 | 4850 ~ 8350 | -1450 ~ 1450 | -2000 ~ -1650 |
|
|
||||||
| 옆벽 1 | 167 | 4850 ~ 8350 | 1100 ~ 1450 | -4100 ~ -2000 |
|
|
||||||
| 옆벽 2 | 167 | 4850 ~ 8350 | -1450 ~ -1100 | -4100 ~ -2000 |
|
|
||||||
| 끝벽 | 167 | 8000 ~ 8350 | -1100 ~ 1100 | -4100 ~ -2000 |
|
|
||||||
|
|
||||||
- 환기구(강제) 덕트·환기탑 kind `vent`, cx = cy = 0, angle 0 (s = X, t = Y). 덕트 안 3000×2100, 남쪽으로 내려가 동쪽으로 꺾여 환기탑(안 3000 정사각형, 지표 z 0 까지)에 닿는다:
|
|
||||||
|
|
||||||
| 이름 | angle | s | t | z |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 덕트 바닥 1 | 0 | -1850 ~ 1850 | -10500 ~ -4610 | -4600 ~ -4100 |
|
|
||||||
| 덕트 바닥 2 | 0 | 1850 ~ 7504 | -10500 ~ -6800 | -4600 ~ -4100 |
|
|
||||||
| 덕트 지붕 1 | 0 | -1850 ~ 1850 | -10500 ~ -4610 | -2000 ~ -1650 |
|
|
||||||
| 덕트 지붕 2 | 0 | 1850 ~ 7504 | -10500 ~ -6800 | -2000 ~ -1650 |
|
|
||||||
| 덕트 서벽 | 0 | -1850 ~ -1500 | -10500 ~ -4610 | -4100 ~ -2000 |
|
|
||||||
| 덕트 동벽 | 0 | 1500 ~ 1850 | -6800 ~ -4610 | -4100 ~ -2000 |
|
|
||||||
| 덕트 북벽 | 0 | 1500 ~ 7504 | -7150 ~ -6800 | -4100 ~ -2000 |
|
|
||||||
| 덕트 남벽 | 0 | -1850 ~ 7504 | -10500 ~ -10150 | -4100 ~ -2000 |
|
|
||||||
| 환기탑 바닥 | 0 | 7504 ~ 11205 | -10500 ~ -6799 | -4600 ~ -4100 |
|
|
||||||
| 환기탑 서벽 위 | 0 | 7504 ~ 7854 | -10149 ~ -7149 | -2000 ~ 0 |
|
|
||||||
| 환기탑 동벽 | 0 | 10855 ~ 11205 | -10500 ~ -6799 | -4100 ~ 0 |
|
|
||||||
| 환기탑 북벽 | 0 | 7504 ~ 11205 | -7149 ~ -6799 | -4100 ~ 0 |
|
|
||||||
| 환기탑 남벽 | 0 | 7504 ~ 11205 | -10500 ~ -10149 | -4100 ~ 0 |
|
|
||||||
|
|
||||||
### 17.3 방호 난간·배관·위쪽 사다리 (F28)
|
|
||||||
- 방호 난간 kind `railing`, type `railing`: `z`(설치 슬래브 윗면), `h` 1100 🔶, `paths`(꺾은선 목록, 각 [x, y] 점). 꺾은선을 따라 높이 h 의 얇은 면(난간 판)을 세운다. 함수 `facesRailing`.
|
|
||||||
- 홀수층 9장(1B·3B·…·17B 윗면): paths [[[2140, 4350], [1940, 4050], [2950, 3390], [3150, 3690]], [[1350, -900], [1350, -2300], [-1350, -2300], [-1350, -900], [1350, -900]], [[2100, -100], [-2200, -100], [-2200, 1100], [-1350, 1100], [-1350, 2300], [1350, 2300], [1350, 1100], [2100, 1100]], [[-850, 4780], [-850, 3550], [850, 3550], [850, 4780]]]
|
|
||||||
- 짝수층 8장(2B·…·16B 윗면): paths [[[-850, 4780], [-850, 3550], [850, 3550], [850, 4780]], [[-2100, 100], [2200, 100], [2200, -1100], [1350, -1100], [1350, -2300], [-1350, -2300], [-1350, -1100], [-2100, -1100]], [[1350, 2300], [1350, 900], [-1350, 900], [-1350, 2300], [1350, 2300]], [[2140, 4350], [1940, 4050], [2950, 3390], [3150, 3690]]]
|
|
||||||
- 19B(z −66100, 패드 위): paths [[[-850, 4780], [-850, 3550], [850, 3550], [850, 4780]]]
|
|
||||||
- 18B: 도면에 난간 없음.
|
|
||||||
- 펌프 배출관(STS Ø100) kind `pipe`, type ring: cx 0, cy 4220, r_out 57, r_in 50, z −68800 ~ −2000 🔶(도면 L=64.0m 는 경로 길이로 보고 집수조 바닥 근처부터 상부 슬래브 밑까지).
|
|
||||||
- 위쪽 사다리 kind `ladder_up`, type ladder:
|
|
||||||
- 서쪽(1B → 분기구 바닥): x0 −4720, x1 −4680, yc 1080, width 400, z0 −7400, z1 −4100, rung 300
|
|
||||||
- 가운데(1B → 맨홀 → 지표): x0 −20, x1 20, yc 0, width 360, z0 −7400, z1 0, rung 300 🔶
|
|
||||||
|
|
||||||
### 17.4 도면 연결 (F29)
|
|
||||||
- 단면 A-A(secA, secA2 위쪽): topslab 은 disc 규칙, neck·branch·vent·ladder_up 은 꼭짓점들의 X 범위 × z 범위 사각형.
|
|
||||||
- 평면도 1·2(plan1, plan2): 원판 안(r 4850)을 먼저 topslab 영역으로, 그 위에 neck 다각형, branch·vent obox 바닥 윤곽(꼭짓점 4개), 그 위에 기존 벽 고리·터널.
|
|
||||||
- 함수 `upperHotspots`.
|
|
||||||
|
|
||||||
## 18. 변경 — 사람 두 명, 콘크리트 계단, 구동설명서 3쪽 (2026-10-21 수석 지시, F30~F32)
|
|
||||||
### 18.1 사람 두 명 (F30)
|
|
||||||
- 지하시설물에 들어갈 때는 2명 이상이 조를 이루어 이동한다. 사람을 두 명으로 하고, 두 번째 사람은 첫 사람과 같은 경로를 **2초 뒤에** 따라온다(속도 1000 mm/s → 약 2m 간격). 경로 끝에서 돌아설 때 잠깐 스쳐 지나가는 것은 받아들인다.
|
|
||||||
- 색: 첫 사람 #ff8c1a, 두 번째 #e07010. 함수 `teamAt`(시각 → 두 위치). 「사람 ▶/❚❚」 버튼은 두 사람을 함께 멈춘다.
|
|
||||||
|
|
||||||
### 18.2 콘크리트 계단 (F31)
|
|
||||||
- 철근콘크리트 계단: 경사판(계단 슬래브) 위에 단면이 직각삼각형인 단이 올라간 꼴이고, **밑면은 경사지며 계단 아래는 빈 공간**이다(수석 제공 측면도로 비례 확인: 안쪽 모서리 선에서 수직 200 아래에 밑면, 위끝은 위 슬래브 밑보다 100 낮음, 아래끝은 마지막 디딤판보다 약 450 앞에서 아래 슬래브에 닿음).
|
|
||||||
- 런마다 단면 다각형(s = x_top 에서 내려가는 방향 거리, 실제 x = x_top + dir·s)을 만들어 Y 띠(y0~y1) 폭만큼 늘인다. 함수 `stairProfile`(런 → 점 목록), `facesStair` 가 이것으로 면을 만든다(옆면 2개 + 둘레 변마다 사각형).
|
|
||||||
1. (0, z_high) 에서 시작
|
|
||||||
2. j = 0 … n−2: 챌판 (j·300, z_high − 200·(j+1)) 로 내려가고, 디딤판 ((j+1)·300, z_high − 200·(j+1)) 로 간다
|
|
||||||
3. 첫 단 챌판: ((n−1)·300, z_low)
|
|
||||||
4. 아래 슬래브 윗면을 따라 (s_b, z_low), s_b = (z_high − z_low − 400) × 1.5
|
|
||||||
5. 경사 밑면: (0, z_high − 400)
|
|
||||||
6. 다시 (0, z_high) 로 닫는다
|
|
||||||
- 디딤판 높이·위치는 4.4·15.2 와 같다(사람 경로 그대로). 색은 그대로.
|
|
||||||
|
|
||||||
### 18.3 구동설명서 3쪽 (F32)
|
|
||||||
- `guide/` 는 목차(index) + **목적(purpose)·구동(run)·조작(control)** 3쪽만 남긴다. plan·scope·limits·method·history 쪽은 지운다(git 기록에는 남는다).
|
|
||||||
- 🔒 줄은 구동설명서에 넣지 않는다(수석 지시, 외부에 내보낼 문서). README·SPEC 의 표기는 그대로.
|
|
||||||
- 사람 문장: 「주황색 사람 두 명 — 지하시설물에 들어갈 때는 2명 이상이 조를 이루어 이동해야 한다 — 이 시설물 계획 중 유지관리 동선계획을 따라 이동하는 과정을 보여 준다.」
|
|
||||||
- 「3D 를 눌러 도면을 찾는 방향은 아직 없다」 문장은 뺀다. 지운 쪽을 가리키는 링크·문구는 남은 쪽으로 바꾸거나 지운다. 도면 연결 표는 조작 쪽으로 옮긴다.
|
|
||||||
- 쪽마다: 맨 위 「← 3D 화면으로 돌아가기」, h1, 다른 두 쪽 링크, 목차·이전/다음 링크. 수석 지시로 코치 세션이 쓰고, 하이쿠가 커밋한다.
|
|
||||||
|
|
||||||
## 19. 추가 — 도면 추출 도구와 대조 검사 (2026-10-21 수석 지시, F33~F34)
|
|
||||||
목적: 「도면 → 데이터」 단계를 저장소 안 코드로 옮긴다. 범용 변환기가 아니라 **이 도면에 대한 반자동 변환**: 도면에서 기계적으로 읽을 수 있는 값은 도구가 읽고, 사람이 정한 규칙(패드 높이, 계단 첫 단 등)은 SPEC 의 설계 결정으로 남는다.
|
|
||||||
|
|
||||||
### 19.1 추출 도구 (F33)
|
|
||||||
- `python3 tools/extract_from_svg.py <도면.svg> model_extracted.js` — 표준 라이브러리만. 출력은 model.js 와 같은 꼴(`const MODEL = ` + 순수 JSON + `;`), `units` "mm".
|
|
||||||
- **도면 좌표를 코드에 숫자로 적지 않는다**(그림 위치·기준점은 모두 도면에서 찾는다). 규칙:
|
|
||||||
1. 축척: 단면도·평면도 모두 1 도면단위 = 1000 mm. 평면은 X = (u − 중심u)·1000, Y = −(v − 중심v)·1000. 단면은 X = (u − 축u)·1000, z = −(v − 지표v)·1000.
|
|
||||||
2. 그림 찾기: `<text>` 의 글자(공백 무시)와 transform 의 평행이동 값으로 그림 이름 위치를 얻는다. 「단면A-A」 중 가장 왼쪽 것이 전체 단면이다. 슬래브 평면은 「중간슬래브(홀수층)」「(짝수층)」「(18B)」「(19B)」 글자 바로 아래에 있는 원(반지름 5.5 와 4.85 두 개)의 중심.
|
|
||||||
3. 단면 A-A(전체): 글자 아래에 있는 하늘색 rgb(0,255,255) 세로선 중 가장 긴 두 쌍이 벽 — 바깥 두 선 간격 = 외경, 안 두 선 간격 = 내경, 축u = 바깥 두 선의 가운데. 지표v = 그 근처 녹색 rgb(0,255,0) 수평선(지반선)의 v.
|
|
||||||
4. 슬래브 윗면: 단면 A-A 의 하늘색 수평선 중 길이가 내경과 같은(±0.05) 선. 바로 0.3 아래에 같은 길이 선이 또 있으면 위 선만 윗면이다. 결과는 위에서부터 19개(1B~19B).
|
|
||||||
5. 슬래브 구멍: 슬래브 평면 원(반지름 4.85) 안의 검은색 rgb(0,0,0)·회색 rgb(128,128,128) 꺾은선 중 닫힌 사각형은 그대로, 세 변만 있는 꺾은선(벽에 붙은 구멍)은 두 끝점을 이어 닫은 다각형을 구멍으로 한다. 다른 후보를 감싸는 후보(난간 윤곽, 18B 처럼 회색으로 그려진 경우)는 뺀다. 계단 구멍은 5b 로 뽑는다.
|
|
||||||
6. 방호 난간: 슬래브 평면 원 안의 노란색 rgb(255,255,0) 꺾은선을 그대로 paths 로.
|
|
||||||
5b. 계단 구멍(난간으로만 그려짐): 노란 난간 꺾은선 중 두 끝점이 서로 다르고 둘 다 벽(반지름 4.85)에서 0.3 넘게 떨어진 것이 계단 입구 난간이다. 꺾은선을 두 끝점으로 닫은 다각형에서, 닫는 변(입구)은 그대로 두고 나머지 변을 0.1(=100 mm) 안쪽으로 옮긴 다각형을 구멍으로 한다. 홀수층·짝수층 평면에서 하나씩 나온다.
|
|
||||||
7. 평면 → 층: 홀수층 평면 = 1B·3B·…·17B, 짝수층 = 2B·…·16B, 18B, 19B (4 의 윗면 목록 순서로).
|
|
||||||
- 색은 요소 자신 또는 가장 가까운 조상 `<g>` 의 stroke 를 따른다.
|
|
||||||
- 출력 부재: 벽 1(type ring, r_out·r_in, z1 −2000, z0 −70200 — z 는 고정값 허용), 슬래브 19(type disc, z1 = 윗면, z0 = 윗면 − 300, r = 내경/2, holes = 5 의 구멍), 난간(type railing, 층마다 1, z = 슬래브 윗면, h 1100, paths = 6). 각 부재 `name` 에 층 이름.
|
|
||||||
- 함수(파이썬): `load_svg`, `find_views`, `section_walls`, `slab_tops`, `plan_holes`, `plan_railings`, `build_model`.
|
|
||||||
|
|
||||||
### 19.2 대조 검사와 비교 화면 (F34)
|
|
||||||
- `compare.html`: index.html 과 같은 화면인데 `model.js` 대신 `model_extracted.js` 를 읽는다(도면 연결 데이터가 없으면 왼쪽은 안내 글자). 3D 칸 위에 「추출 데이터로 그린 화면」 글자. 추출 결과와 수작업 모델을 눈으로 비교하는 용도.
|
|
||||||
- check.sh 가 도구를 직접 돌려 model.js 와 대조한다(벽 반지름 ±10, 슬래브 윗면 ±60, 구멍은 추출된 각 구멍이 model.js 의 어느 구멍 범위 안(±40)에 들고, 난간 꺾은선 수·꼭짓점이 ±40 안에서 같다). 또 도면 좌표를 통째로 옮긴 사본으로 다시 돌려 결과가 같아야 한다(숫자를 코드에 박으면 실패). 그리고 도면 내용을 바꾼 사본 두 개로 돌려 결과가 그만큼 바뀌어야 한다 — 1B 슬래브 윗면 선만 0.5 내린 사본(1B 만 약 500 낮아짐), 홀수층 아래 slot 하나를 지운 사본(홀수층 9장에서 구멍이 하나씩 줄어듦). 공식이나 정답 값을 코드에 넣으면 실패한다.
|
|
||||||
- `model_extracted.js` 는 도구의 출력이며 커밋한다(도면 자체는 들어 있지 않다).
|
|
||||||
|
|
||||||
## 20. 변경 — 추출 입력을 DXF 로 (19장 대체, 2026-10-21 수석 지시, F33~F34)
|
|
||||||
- 수석이 원본 DWG 를 주었고, 코치가 GNU LibreDWG `dwg2dxf` 로 DXF 로 바꾼 뒤 **우리 도면 세트의 그림(전체 단면·평면도·생략 단면·슬래브 평면)만** 남겼다(표제란·다른 도면 제외). 파일 `private/drawing.dxf`(ASCII, git 제외). 19장의 SVG 규칙은 이 장으로 대체한다(SVG 는 왼쪽 도면 표시에만 쓴다).
|
|
||||||
- 이 SVG 규칙은 도면에 맞지 않았다(18B 윗면이 구멍에 잘려 18개만 잡힘, T자 구멍의 출처 불명확) — 하이쿠가 하드코딩 없이 보고해 드러남.
|
|
||||||
|
|
||||||
### 20.1 추출 도구 (F33)
|
|
||||||
- `python3 tools/extract_from_dxf.py <도면.dxf> model_extracted.js` — 표준 라이브러리만(DXF 는 「그룹 코드 줄 + 값 줄」의 텍스트다). 출력 꼴은 19.1 과 같다. **도면 좌표·정답 값·층 높이 공식을 코드에 적지 않는다.**
|
|
||||||
- DXF 읽기: ENTITIES 구역의 LINE·LWPOLYLINE(코드 70 의 1 비트 = 닫힘)·POLYLINE(+VERTEX…SEQEND, 머리의 10/20 은 기준점이라 쓰지 않음)·CIRCLE·TEXT. 레이어 = 코드 8, 색 = 코드 62(없으면 레이어 색).
|
|
||||||
- 규칙(단위: 모델 1 = 1 m → ×1000 mm, Y 위 +):
|
|
||||||
1. 전체 단면: 레이어 `구조물` 의 세로 선분 중 길이 60 m 넘는 것들. X 가 가장 작은 네 선 = 단면 A-A 의 벽(바깥 둘·안 둘): 외경 = 바깥 두 선 간격, 내경 = 안 두 선 간격, 축 X = 바깥 두 선 가운데.
|
|
||||||
2. 지표: 색 3(녹색) 선 중 단면 A-A 의 축 X 를 지나는 것의 그 자리 Y. z = (Y − 지표 Y)·1000.
|
|
||||||
3. 슬래브 윗면: 레이어 `구조물` 의 수평 선분 중 **왼쪽 안쪽 벽 X 에서 시작**하고(±0.01), 0.3(±0.01) 아래에 역시 왼쪽 안쪽 벽 근처(0.2 안)에서 시작하는 `구조물` 수평 선분이 있는 것. 위에서부터 19개 = 1B~19B (구멍에 잘린 층도 왼쪽 조각이 남는다).
|
|
||||||
4. 슬래브 평면: 레이어 `S-TITLE` 글자 「중간슬래브(홀수층)」「(짝수층)」「(18B)」「(19B)」(공백 무시) 바로 아래에서 가장 가까운, 반지름 4.85 와 5.5 원이 겹친 중심.
|
|
||||||
5. 구멍: 그 평면의 안쪽 원 안에 있는 레이어 `오프닝`·`구조물 계단` 객체로 만든다. (가) 꼭짓점 4개 이상인 LWPOLYLINE/POLYLINE 하나는 그대로 후보(닫혀 있지 않으면 두 끝점을 이어 닫는다 — 벽에 붙은 세 변 구멍). (나) 꼭짓점 3개인 열린 LWPOLYLINE 은 같은 레이어의 **수평·수직** LINE 중 그 끝점에서 이어지는 것을 차례로 붙여 닫히면 후보. 기울어진 LINE(대각선)과 끝점에 붙지 않는 LINE(디딤판 선)은 쓰지 않는다. 같은 모양 중복은 하나만, 다른 후보를 감싸는 후보(난간 윤곽)는 뺀다.
|
|
||||||
6. 방호 난간: 그 평면 원 안의 레이어 `계단`, 색 2 인 꼭짓점 3개 이상 꺾은선 → paths (닫힌 것은 첫 점을 끝에 한 번 더).
|
|
||||||
7. 평면 → 층: 홀수층 = 1B·3B·…·17B, 짝수층 = 2B·…·16B, 18B, 19B (3 의 순서).
|
|
||||||
- 출력 부재: 19.1 과 같음(벽 1, 슬래브 19 + holes, 난간 층마다 1).
|
|
||||||
- 함수(파이썬): `load_dxf`, `section_walls`, `ground_y`, `slab_tops`, `find_slab_plans`, `plan_holes`, `plan_railings`, `build_model`.
|
|
||||||
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 채점이 모두 통과함을 미리 확인했다(그 구현은 주지 않는다). 막히면 규칙 결함일 수 있으니 숫자를 넣지 말고 보고한다.
|
|
||||||
|
|
||||||
### 20.2 대조 검사 (F34)
|
|
||||||
- 19.2 와 같은 대조(벽 ±5, 윗면 ±5, 구멍 ⊆ model.js 구멍 ±10, 난간 ±10)와 compare.html.
|
|
||||||
- 계단 구멍(폭 4 m 넘는 구멍) 18개(홀수 9·짝수 8·18B 1).
|
|
||||||
- 변조 사본 셋: 좌표 전체 (+7, +5) m 이동 → 같은 결과 / 1B 슬래브(윗면·아랫면 선)만 0.5 m 내림 → 1B 만 500 낮아짐 / 홀수층 아래 slot 지움 → 구멍 9개 감소.
|
|
||||||
|
|
||||||
## 21. 추가 — 계단 추출 (2026-10-21 수석 지시, F35) — 계단이 생기면 사람 두 명도 추출 모델에서 걷는다
|
|
||||||
도면(DXF)에서 읽는다. 20.1 의 함수에 더한다. 함수 `section_stairs`, `stair_parts`.
|
|
||||||
1. 단면 계단 윤곽: 레이어 `구조물`, 색 8, **닫힌** 꺾은선, 꼭짓점 6개 이상, 단면 A-A 바깥 벽 두 선 사이.
|
|
||||||
2. 단 높이·디딤판: 윤곽들의 세로 변 길이(mm) 중 가장 많이 나온 값 = riser, 가장 작은 값 = first_riser, 가로 변 중 가장 많이 나온 값 = tread.
|
|
||||||
3. 런 i(1~18): z_high = i 층 윗면, z_low = i+1 층 윗면(20.1 ③). risers = (z_high − z_low − first_riser) / riser + 1 (반올림).
|
|
||||||
4. 방향 dir: 가장 낮은 꼭짓점의 z 가 z_low 인 단면 윤곽에서, 그 가장 낮은 꼭짓점들이 윤곽의 왼쪽 끝에 붙어 있으면 −1, 오른쪽 끝이면 +1. 단면에 윤곽이 없는 런(18)은 바로 위 런의 반대.
|
|
||||||
5. x_top, y0, y1: i 층 슬래브 평면의 계단 구멍(20.1 ⑤ 의 구멍 중 폭 4 m 넘는 것). dir −1 이면 구멍의 오른쪽(X 최대) 가장자리, +1 이면 왼쪽(X 최소) 가장자리가 x_top. y0·y1 = 그 구멍의 Y 범위.
|
|
||||||
6. 출력: kind·type `stair` 18개, 필드는 model.js 와 같다(z_low, z_high, risers, first_riser, riser, tread, x_top, dir, y0, y1, 이름·색은 자유).
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 F35 채점(대조·변조 시험)이 통과함을 미리 확인했다(그 구현은 주지 않는다).
|
|
||||||
|
|
||||||
## 22. 정정 — 평면도 좌표틀 (2026-10-21 수석 지시, F36)
|
|
||||||
- 발견(코치, 도면 DXF 재확인): 슬래브 평면(홀수·짝수·18B·19B·20B)과 단면 A-A 는 같은 틀이다(계단 구멍 긴 변 = X). 평면도 1·2 는 이 틀을 **−30.3°** 돌려 그렸다(평면도의 계단 구멍 긴 변·터널 중심선이 −30.3°). 4.5 와 17장의 터널·채움·분기구·환기구·상부 맨홀·목은 평면도에서 읽어 그 틀 그대로 넣었기 때문에, 모델 안에서 안쪽 부재와 30.3° 어긋났다. 예: 19B 평면에서 터널은 +X(계단과 나란히)인데 모델은 330°.
|
|
||||||
- 원칙: **모델 좌표 = 슬래브 평면 틀.** 평면도에서 온 점 (x, y) 는 +30.3° 돌린다: (x cos30.3° − y sin30.3°, x sin30.3° + y cos30.3°). 그렇게 돌리면 맨홀 2 가 (0, 4405) = 펌프 구멍 바로 위, 목 두 개가 축에 맞는 1300 정사각형이 된다.
|
|
||||||
- 새 값(나머지 필드는 그대로):
|
|
||||||
|
|
||||||
| 부재 | 바꿀 것 |
|
|
||||||
|---|---|
|
|
||||||
| tunnel_TBM | x 4850, y 0, angle_deg 0 (19B 평면의 터널 선이 X 축과 나란하고 Y ±1300·±1500) |
|
|
||||||
| fill 3개 | x 4850, y 0, angle_deg 0 (터널과 같은 축) |
|
|
||||||
| topslab | holes [{"circle": [0, 0, 450]}, {"circle": [0, 4405, 450]}] |
|
|
||||||
| neck_1 | polygon [[-650, -650], [650, -650], [650, 650], [-650, 650]], holes [{"circle": [0, 0, 450]}] |
|
|
||||||
| neck_2 | polygon [[-650, 3755], [650, 3755], [650, 5055], [-650, 5055]], holes [{"circle": [0, 4405, 450]}] |
|
|
||||||
| branch 5개 | angle_deg 167 → 197.3 (s, t, z 그대로) |
|
|
||||||
| vent 13개 | angle_deg 0 → 30.3 (s, t, z 그대로) |
|
|
||||||
| ladder_up_W | x0 −4622, x1 −4582 (단면 A-A 의 사다리 중심 X −4602), yc −1260 🔶 (분기구 입구 쪽, 폭 400 이 벽 안에 드는 한도) |
|
|
||||||
|
|
||||||
- 17.1 의 목 다각형은 돌린 방향도 틀렸다(위 표로 대체). 슬래브 구멍·계단·난간·패드·바닥부·배관·가운데 사다리는 이미 슬래브 평면 틀이라 그대로.
|
|
||||||
- 도면 연결: `MODEL.drawing.views` 의 plan1·plan2 에 `"rot_deg": -30.3` 을 더한다. 평면 그림에 놓을 때 모델 (X, Y) 를 rot_deg 만큼 돌린 뒤(X' = X cos − Y sin, Y' = X sin + Y cos) u = cx + X'/k, v = cy − Y'/k. rot_deg 가 없는 그림은 0. `planXY` 와 `planPoly`(터널 사각형)에 넣는다(원은 그대로).
|
|
||||||
- 결과: 3D 에서 터널이 계단과 나란히 +X 로 나가 침사구 쪽 벽으로 들어오고, 평면도 클릭 영역은 지금처럼 도면 그림 위에 놓인다.
|
|
||||||
|
|
||||||
## 23. 추가 — 터널 추출 (2026-10-21 수석 지시, F37)
|
|
||||||
도면(DXF)에서 TBM 터널과 터널 안 채움 3개를 읽는다. 20.1·21장 함수에 더한다. 함수 `section_tunnel`, `plan_tunnel`, `tunnel_parts`.
|
|
||||||
1. 단면(높이·지름): 단면 A-A **오른쪽 바깥 벽선**(20.1 ① 의 X 가 가장 큰 선)에서 시작해 바깥으로 1 m 넘게 뻗는 레이어 `구조물` 수평선(기울기 5% 미만, 터널 경사가 조금 있다). 벽 쪽 끝의 z(20.1 ② 방식) 다섯 개를 위에서부터 [바깥 위, 안 위, 패드 윗면, 안 아래, 바깥 아래] 로 본다.
|
|
||||||
- 축 z = (바깥 위 + 바깥 아래)/2, r_out = (바깥 위 − 바깥 아래)/2, r_in = (안 위 − 안 아래)/2.
|
|
||||||
2. 평면(방향): 「중간슬래브(19B)」 평면 중심(20.1 ④)에서 두 끝이 4.8 m 이상 8 m 이하인 레이어 `-평면계획` 색 8 선분. 각 선분을 중심에 가까운 끝 → 먼 끝으로 본 방향의 합 = 터널 방향 d. 축 어긋남 = 그 선분들 가까운 끝의 d 에 수직인 거리 평균(왼쪽 +).
|
|
||||||
3. 터널(kind `tunnel`, type `pipe`): angle_deg = d 의 각도(X축에서 반시계, 0~360), 시작점 = 축 어긋남 + 벽 안쪽 반지름(20.1 ①)만큼 d 방향, 길이 = 벽 두께 + 20000(4.5 의 설계값: 벽 바깥에서 20 m).
|
|
||||||
4. 채움 3개(kind `fill`, 터널과 같은 축·길이, r = r_in). v 는 축에서 잰 높이: 패드 윗면 v = ① 의 패드 윗면 − 축 z. 4.5 의 설계값(도면에 없음): 잡석 두께 125, 패드 1단 두께 50, 1단 폭 800.
|
|
||||||
- 잡석 −r_in ~ −r_in + 125 / 패드 2단 −r_in + 125 ~ 패드 윗면 − 50 / 패드 1단 패드 윗면 − 50 ~ 패드 윗면(half_width 400, 나머지 0).
|
|
||||||
5. 도면 좌표·정답 값(축 높이, 지름, 패드 높이, 방향 각도)은 코드에 적지 않는다. 고정값은 위 설계값(20000, 125, 50, 400)과 20.1 의 허용값만.
|
|
||||||
- 대조(F37): 터널 x·y·z·r·길이 ±10, 방향 ±0.5°, 채움 v·폭 ±5. 변조 사본 둘: 단면 A-A 터널 선만 0.5 m 내림 → 터널 축 500 낮아짐 / 19B 평면 터널 선을 좌우로 뒤집음 → 방향 180°.
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 F37 채점이 통과함을 미리 확인했다(그 구현은 주지 않는다).
|
|
||||||
|
|
||||||
## 24. 추가 — 분기구·환기구 추출 (2026-10-21 수석 지시, F38)
|
|
||||||
도면(DXF)에서 분기구(kind `branch`)와 환기구(덕트·환기탑, kind `vent`)를 obox(17.2) 로 읽는다. 함수 `plan_center`, `frame_angle`, `section_heights`, `branch_parts`, `vent_parts`.
|
|
||||||
- 평면 모양은 평면도 1, 높이는 단면(분기구 = 단면 A-A, 환기구 = 단면 B-B「환기 덕트를 펼친 단면」)에서 읽는다. 평면도는 슬래브 평면 틀과 각도 φ 만큼 다르다(22장) — φ 도 도면에서 구한다.
|
|
||||||
|
|
||||||
### 24.1 평면도 1 과 φ
|
|
||||||
1. 평면도 중심 C: 레이어 `S-TITLE` 글자 「평면도」(공백 무시) 아래에서 가장 가까운 반지름 4.85 원의 중심.
|
|
||||||
2. 레이어 `-평면계획` 색 1, 꼭짓점 3개이고 가운데 꼭짓점이 C(±0.01)인 꺾은선 → C 에서 나가는 두 다리.
|
|
||||||
3. 터널 다리 = 그 다리와 나란하고(±0.01 rad) 다리에서 r_out(23장, ±0.02 m) 떨어진 `-평면계획` 색 8 선분이 2개 이상 있는 다리. 다른 다리 = 분기구 다리.
|
|
||||||
4. φ = 23장 터널 angle_deg − 터널 다리의 각도. 평면도의 점·방향은 모두 C 기준 mm 로 바꾼 뒤 φ 만큼 돌려 모델 좌표로 쓴다(obox 는 cx·cy 를 돌리고 angle_deg 에 φ 를 더하면 된다).
|
|
||||||
|
|
||||||
### 24.2 단면 높이 (`section_heights`)
|
|
||||||
- 단면 B-B 의 벽 네 선 = 20.1 ① 처럼 길이 60 m 넘는 `구조물` 세로선을 X 순으로 놓았을 때 5~8번째(1~4번째가 A-A).
|
|
||||||
- 벽 개구부 X 표시: 한쪽(왼쪽 = 바깥 왼·안 왼, 오른쪽 = 안 오·바깥 오) 벽 두 면 사이를 잇는 길이 1 m 넘는 `구조물` 사선, 지표 아래 10 m 안. 그 z 범위의 아래끝 = **바닥 윗면**, 위끝 = **지붕 아랫면**. X 가 있는 쪽이 그 구조물 쪽.
|
|
||||||
- 헌치: 그 쪽 벽 바깥면(±0.01)에 한 끝이 있고 다른 끝이 벽 바깥으로 나간 짧은 `구조물` 사선(길이 0.1~0.5 m). 바깥 끝이 지붕 아랫면보다 위이고 아래로 내려가는 것의 바깥 끝 z = **지붕 윗면**, 바닥 윗면보다 아래이고 위로 올라가는 것의 바깥 끝 z = **바닥 아랫면**.
|
|
||||||
- 도면 값(참고): 분기구 바닥 −4,550 ~ −4,100, 환기구 바닥 −4,600 ~ −4,100, 둘 다 지붕 −2,000 ~ −1,650. 이 숫자를 코드에 쓰지 않는다.
|
|
||||||
|
|
||||||
### 24.3 분기구 (`branch_parts`)
|
|
||||||
- 분기구 다리 방향 = s, 왼쪽 수직 = t. 평면도의 닫히지 않은 `구조물` 꺾은선(꼭짓점 4개 이상) 중 모든 꼭짓점이 s > 4.55 m, |t| < 2 m, s < 다리 길이 + 0.5 m 인 것 둘: |t| 최대가 작은 것 = 안 윤곽(t_in, 끝 s_in = 최대 s), 큰 것 = 바깥 윤곽(t_out, s_out).
|
|
||||||
- obox 5개(cx = cy = 0, angle = 다리 각도 + φ, s 는 벽 안쪽면 4850 부터): 바닥·지붕(t ±t_out, s ~ s_out), 옆벽 둘(t_in ~ t_out 양쪽, 높이 바닥 윗면 ~ 지붕 아랫면), 끝벽(s_in ~ s_out, t ±t_in).
|
|
||||||
|
|
||||||
### 24.4 환기구 (`vent_parts`)
|
|
||||||
- 중심선: 레이어 `환기구`, 꼭짓점 3개, 첫 꼭짓점이 C 인 꺾은선 → 꺾인 점 V1, 끝점 T. 다리 1 = C→V1, 다리 2 = V1→T.
|
|
||||||
- 덕트 벽선: 닫히지 않은 꼭짓점 3개 `구조물` 꺾은선 중 가운데 꼭짓점이 V1 에서 3 m 안인 것 4개. 다리 1 에서 잰 |t| 의 작은 둘 평균 = t_in, 큰 둘 평균 = t_out.
|
|
||||||
- 환기탑: T 를 품는 닫힌 `구조물` 꺾은선 — 꼭짓점 4개 = 바깥 사각형, 더 많은 것 = 안 윤곽. 다리 2 축으로 잰 범위(s, t)를 쓴다.
|
|
||||||
- 덕트 obox(높이는 24.2 의 B-B 값):
|
|
||||||
- 다리 1: 바닥·지붕 s 4850 ~ L1 + t_out, t ±t_out. 꺾이는 안쪽 벽(다리 2 가 나가는 쪽) s 4850 ~ L1 − t_out, 바깥 벽 s 4850 ~ L1 + t_out (t_in ~ t_out).
|
|
||||||
- 다리 2(V1 에서): 바닥·지붕 s t_out ~ 탑 바깥 사각형의 가까운 변, t ±t_out. 안쪽 벽 s t_in ~ 탑 변, 바깥 벽 s −t_out ~ 탑 변.
|
|
||||||
- 환기탑 obox: 바닥 = 바깥 사각형(덕트 바닥 높이). 벽 넷 = 바깥 사각형과 안 윤곽 범위 사이의 띠, 높이 바닥 윗면 ~ 지표 z 0. 단 덕트가 들어오는 쪽 벽은 지붕 아랫면 ~ 0 만(B-B 에서 탑 벽이 덕트 지붕 위에만 있음). 탑에는 지붕이 없다.
|
|
||||||
- 덕트·탑을 obox 로 나누는 방법은 위와 다르게 해도 된다 — 채점은 **입체(차지하는 공간)** 로 비교한다.
|
|
||||||
|
|
||||||
### 24.5 대조 (F38)
|
|
||||||
- 분기구·환기구 각각: 높이 5곳(−4575, −4300, −3000, −1800, −1000) × 100 mm 격자(수직구 바깥 r 5520 넘는 곳)에서 「추출 부재 안인가」가 model.js 와 같은지. 경계 ±20 mm 는 봐준다. 어긋난 점이 안쪽 점의 1% 이하.
|
|
||||||
- 변조 사본 둘: 평면도 1 의 환기구(중심선·덕트·탑)를 C 기준 좌우로 뒤집음 → 추출 환기구도 뒤집혀 나옴 / 단면 A-A 의 분기구 지붕 헌치 끝을 0.2 m 내림 → 분기구 지붕 윗면만 200 낮아짐.
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 F38 채점이 통과함을 미리 확인했다(그 구현은 주지 않는다).
|
|
||||||
|
|
||||||
## 25. 추가 — 나머지 부재 추출 (2026-10-21 수석 지시, F39)
|
|
||||||
도면(DXF)에서 상부 슬래브·맨홀 목·19B 패드·하부 슬래브·바닥 콘크리트·월류벽·집수조·침사조를 읽고, 19B 슬래브에 침사구 구멍을 더한다. 함수 `top_parts`, `pad_part`, `bottom_parts`, `weir_parts`. id·kind·type·필드는 model.js 와 같게(topslab, neck_1·neck_2, pad_19B, bottom_20B, lean_concrete, weir_L·weir_R·weir_mid, pit_pump·pit_tbm).
|
|
||||||
1. 단면 A-A 의 「벽 전체 수평선」: 레이어 `구조물` 수평선 중 바깥 왼 벽선에서 바깥 오른 벽선까지(양 끝 ±0.01) 걸친 것. 가장 높은 것 = **상부 슬래브 윗면**, 가장 낮은 것 = **바닥 콘크리트 밑면**, 두 번째로 낮은 것 = **하부 슬래브 밑면**(= 바닥 콘크리트 윗면).
|
|
||||||
2. 상부 슬래브 밑면 = 윗면보다 낮은 `구조물` 수평선 중 바깥 벽 사이에 있고 길이가 안쪽 지름의 0.9 배 넘는 것 중 가장 높은 것. 상부 슬래브: type disc, r = 바깥 반지름, holes = 평면도 1(24.1) 안의 반지름 1 m 미만 `구조물` 원(중복 하나로) → C 기준 mm 를 φ 만큼 돌린 `{"circle": [x, y, r]}`.
|
|
||||||
3. 맨홀 목: 평면도 1 의 닫힌 꼭짓점 4개 `구조물` 꺾은선 중 2 의 원 중심을 품는 것(중복 하나로). polygon = 꼭짓점을 φ 만큼 돌린 것, holes = 그 원, z 상부 슬래브 윗면 ~ 지표 0. 구멍 y 가 작은 쪽이 neck_1.
|
|
||||||
4. 하부 슬래브 윗면 = 단면 A-A 에서 안쪽 벽면에 한 끝이 있고 안쪽·아래로 내려가는 짧은 `구조물` 사선(길이 0.1~0.6 m) 아래 끝 중 가장 낮은 것. 하부 슬래브 disc r = 안쪽 반지름, 바닥 콘크리트 disc r = 바깥 반지름.
|
|
||||||
5. 19B 패드: 19B 슬래브 윗면(20.1) 위 0~0.3 m 안에 있는, 안쪽 벽 사이 길이 9 m 넘는 `구조물` 수평선 → 윗면, r = 길이/2. 구멍 = 19B 평면 안쪽 원 안의 **X 표시**(같은 레이어(`오프닝` 또는 `구조물`)의 엇갈린 사선 LINE 두 개, 길이 0.5 m 넘고 X 범위가 같은 것)의 끝점 범위 사각형, 패드 반지름 밖은 ±r 로 자른다. 같은 X 표시 사각형(자르지 않은 것)이 19B 슬래브 구멍에 없으면 더한다(침사구).
|
|
||||||
6. 월류벽: 「하부슬래브(20B)」 평면(제목 아래 가장 가까운 반지름 4.85 원) 안쪽 원 안의 길이 5 m 넘는 `구조물` 세로 LINE 두 개 → x0, x1. 엇갈린 사선 두 개(X 표시)의 Y 범위 → 낮은 칸 y0~y1. 양쪽 칸은 낮은 칸 끝 ~ ±√(안쪽 반지름² − x1²).
|
|
||||||
- 높이: 단면 A-A 에서 안쪽 벽 사이(벽선에서 0.05 m 넘게 떨어진) 길이 1 m 넘는 세로 `구조물` 선 중 아래 끝이 하부 슬래브 윗면 위 0~0.3 m 안인 것들 → 위끝 = 월류벽 윗면. 그 선들 사이에 걸친 수평선 중 윗면보다 낮은 것 = 낮은 칸 윗면. z0 = 하부 슬래브 윗면.
|
|
||||||
7. 집수조·침사조(type zone, alpha 0.25, r = 안쪽 반지름): 월류벽 왼쪽 x −r ~ x0, 오른쪽 x1 ~ r, z 하부 슬래브 윗면 ~ 월류벽 윗면. 단면 A-A 글자 「집수조」가 축 왼쪽이면 왼쪽이 pit_pump, 오른쪽이 pit_tbm.
|
|
||||||
- 도면 값(참고): 상부 슬래브 −2,000~−1,500, 19B 패드 −66,200~−66,100 r 4,750, 하부 슬래브 −70,200~−69,000, 바닥 콘크리트 −70,300~−70,200, 월류벽 x 1,400~1,600·윗면 −67,000·낮은 칸 −67,500. 이 숫자를 코드에 쓰지 않는다.
|
|
||||||
- 대조(F39): model.js 와 r·z ±5, 원 구멍·윤곽 ±10, 패드 구멍 ±40(정답지는 원 경계에 맞춰 손으로 줄였다). 변조 사본 둘: 20B 평면 월류벽 선을 중심 쪽으로 0.3 m 옮김 → 월류벽·조 경계가 300 옮겨짐 / 평면도 맨홀 2(원·목)를 0.5 m 옮김 → 구멍 2·목 2 만 φ 돌린 만큼 옮겨짐.
|
|
||||||
- 벽체 높이도 이제 도면에서 읽는다(19.1 의 고정값 허용을 끝냄): wall z0 = 하부 슬래브 밑면, z1 = 상부 슬래브 밑면.
|
|
||||||
- 사다리·펌프 배출관·사람 등 도면에 위치가 다 나오지 않는 것(🔶)은 이 장에서 다루지 않는다(다음 장의 설계 규칙).
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 F39 채점이 통과함을 미리 확인했다(그 구현은 주지 않는다).
|
|
||||||
|
|
||||||
## 26. 추가 — 정답지 보강: 환기구 사다리 2개 (2026-10-21 수석 지시 「2D 도면에 있는 것 모두」, F40)
|
|
||||||
- 발견(코치, DXF 재확인): 단면 B-B(환기 덕트를 펼친 단면)에 레이어 `사다리` 로 **벽 사다리**(축에서 +4.6 m, 1B ~ 덕트 바닥 위)와 **환기탑 안 사다리**(펼친 거리 18.75 m 근처, 덕트 바닥 ~ 지표 위)가 그려져 있는데 정답지에 없다. 단면 A-A 의 서쪽 벽 사다리(ladder_up_W)와 짝이다.
|
|
||||||
- `ladder` 에 선택 필드 `angle_deg` 를 더한다: 지금처럼 (x0~x1, yc ± width/2) 로 만든 면의 모든 점을 **원점 (0, 0) 기준으로 angle_deg 만큼** 돌린다(법선도 같이). 없으면 0(지금과 같음). 함수 `facesLadder`.
|
|
||||||
- 새 부재(kind `ladder_vent`, type `ladder`, color "#d0a040"). kind 가 새것이라 도면 클릭 영역(17.4)에는 넣지 않는다:
|
|
||||||
|
|
||||||
| id | name | angle_deg | x0 | x1 | yc | width | z0 | z1 | rung |
|
|
||||||
|---|---|---|---|---|---|---|---|---|---|
|
|
||||||
| ladder_vent_wall | 환기구 벽 사다리 (1B→덕트 바닥) | 300.3 | 4582 | 4622 | 0 | 400 | −7400 | −4100 | 300 |
|
|
||||||
| ladder_vent_tower | 환기탑 사다리 (덕트 바닥→지표) | 30.3 | 10100 | 10140 🔶 | −8650 🔶 | 400 | −4100 | 0 | 300 |
|
|
||||||
|
|
||||||
- 300.3° = 환기 덕트 다리 1 방향(24장, 평면도 −90° + 30.3°). 그 방향으로 4.6 m(B-B 의 사다리 앞 기둥) = 벽 안쪽 4.85 m 에서 0.25 m.
|
|
||||||
- 30.3° 틀의 (x, y) = 평면도의 (x, y). 탑 안 사다리 앞 기둥 = 펼친 거리 18.75 − 덕트 다리 1 길이 8.65 ≈ 10.1 m, yc = 탑 안 가운데(평면도 y −8,650) 🔶.
|
|
||||||
|
|
||||||
## 27. 추가 — 설계 규칙 파일, 추출 모델 = 정답지 전체 (2026-10-21 수석 지시, F41)
|
|
||||||
목표: 추출 모델(model_extracted.js)이 정답지(model.js)의 부재를 **모두** 갖게 한다. 도면에서 읽는 것은 20~25장, 도면에 위치가 다 나오지 않아 사람이 정한 것(🔶)과 화면용 값은 **설계 규칙 파일**에서 온다.
|
|
||||||
- 파일 `tools/design_rules.json` (하이쿠 작성, 순수 JSON):
|
|
||||||
- `"parts"`: 🔶 부재 6개를 model.js 의 객체 그대로(id·name·kind·type·color 와 모든 수치) — ladder_19B, ladder_up_W, ladder_up_C, ladder_vent_wall, ladder_vent_tower, pump_pipe. 근거는 16.1·17.3·22·26장(사다리 위치·배관 경로는 도면 수치가 없거나 일부만 있어 설계 결정).
|
|
||||||
- `"style"`: kind 별 `{"color": ..., "alpha": ...(있을 때만)}` 와 `"ids"`: id 별 덮어쓰기(채움 3개·집수조·침사조처럼 같은 kind 안에서 색이 다른 것). 값은 model.js 의 색·투명도와 같게.
|
|
||||||
- 도면에서 읽는 부재(벽·슬래브·계단·터널·채움·분기구·환기구·상부 슬래브·목·패드·하부 슬래브·바닥 콘크리트·월류벽·집수조·침사조·난간)의 **치수·좌표는 넣지 않는다.**
|
|
||||||
- 추출 도구: 도면에서 읽은 부재 뒤에 `parts` 를 더하고, 모든 부재에 style(kind → ids 순으로 덮어씀)의 color·alpha 를 넣는다. 모든 부재에 `name` 이 있다(한국어, 자유). 함수 `apply_design_rules`.
|
|
||||||
- id: 도면에서 읽는 부재도 model.js 와 같은 id 를 쓴다(분기구·환기구는 나누는 방법이 달라도 되므로 예외).
|
|
||||||
- 대조(F41): design_rules.json 의 6개 = model.js(±1), 그 밖의 kind 는 넣지 않음, 도면 값 숫자 없음 / 추출 모델의 kind 집합 = model.js, 분기구·환기구 말고 id 집합 = model.js / 같은 id 의 color·alpha = model.js / 모든 부재에 name·color.
|
|
||||||
|
|
||||||
## 28. 추가 — 변환 패키지: DWG → DXF → 도면 범위 (2026-10-21 수석 지시, F42)
|
|
||||||
설계도면은 DWG 로 주고받는다. DWG 는 바이너리라 GNU LibreDWG(`dwg2dxf`)로 DXF(글자 파일)로 바꾸고, 그 안에서 이 과제가 쓰는 그림만 남겨 추출 도구(20~27장)에 넣는다. 지금까지 코치가 리포 밖에서 하던 전처리를 리포 안 도구로 옮긴다.
|
|
||||||
- 입력: `private/drawing.dwg`(원본 DWG 사본, git 제외 — 코치가 넣었다).
|
|
||||||
1. `tools/dwg_to_dxf.sh <입력.dwg> <출력.dxf>`: `dwg2dxf -y -o <출력> <입력>`. dwg2dxf 를 PATH 와 `~/.local/bin` 에서 찾고, 없으면 README 의 설치 절을 가리키는 한 줄을 내고 종료 코드 2.
|
|
||||||
2. `tools/views.json`(사람 입력, 이 장과 함께 커밋): DWG 한 파일에 수직구 두 개와 옛 개정판이 함께 있어 **어느 그림을 쓸지는 사람이 정한다**. `boxes`(그림 상자 4개, 모형 공간 m), `drop_layers`, `drop_block_word`.
|
|
||||||
3. `tools/select_views.py <전체.dxf> <views.json> <출력.dxf>` (표준 라이브러리만, 좌표 숫자 없음 — 상자는 views.json 에서만):
|
|
||||||
- ENTITIES 구역을 객체 묶음으로 나눈다: POLYLINE 은 SEQEND 까지, 코드 66 이 1 인 INSERT 는 ATTRIB…SEQEND 까지 한 묶음.
|
|
||||||
- 뺄 것: 배치 공간(코드 67 이 1), 레이어가 drop_layers 인 것, 블록 이름(INSERT 의 코드 2)에 drop_block_word 가 들어 있는 INSERT, 좌표가 없는 것.
|
|
||||||
- 좌표: 코드 10·11 의 X 와 그 뒤 20·21 의 Y. POLYLINE 은 머리의 10/20(기준점)을 빼고 VERTEX 부터, INSERT 는 ATTRIB 도 포함. CIRCLE·ARC 는 중심 ± 반지름(코드 40)도 넣는다.
|
|
||||||
- 남길 것: 좌표 범위(최소~최대)가 상자 하나 안에 다 들어가는 묶음. 순서는 원본 그대로.
|
|
||||||
- 출력: ENTITIES 앞(HEADER·TABLES·BLOCKS 등)은 원본 그대로, 단 $EXTMIN·$EXTMAX 의 10/20 은 남긴 좌표의 최소·최대. 남긴 묶음, 그 뒤 원본의 나머지. 줄 끝 CRLF.
|
|
||||||
4. `tools/make_model.sh [입력.dwg] [출력 폴더]` (기본 private/drawing.dwg, private/): 1 → `full.dxf`, 3 → `drawing.dxf`, 추출 도구 → `model_extracted.js`(출력 폴더). 출력 폴더가 private/ 이면 리포 루트의 model_extracted.js 도 새로 쓴다.
|
|
||||||
5. README 에 「변환 패키지」 절: 설치(LibreDWG 0.14 소스 빌드 — `PKG_CONFIG=/bin/true ./configure --prefix=$HOME/.local --disable-bindings --disable-docs && make && make install`, WSL Ubuntu), 실행(`bash tools/make_model.sh`), 각 파일의 용도(DWG = 주고받는 원본, DXF = 추출 입력, SVG = 웹 표시(다음 장)).
|
|
||||||
- 대조(F42): make_model.sh 를 임시 폴더로 돌려 나온 drawing.dxf 의 객체가 private/drawing.dxf 와 같고(숫자 1e-6 까지 — LibreDWG 가 z 에 1e-11 잡음을 넣을 때가 있다), 나온 model_extracted.js 의 부재가 리포의 것과 같다. 상자 하나만 남긴 views.json 으로 돌리면 그 상자 안 객체만 남는다.
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 원본 DWG → 3,356 객체가 private/drawing.dxf 와 같게 나옴을 미리 확인했다.
|
|
||||||
|
|
||||||
## 29. 추가 — DXF → SVG 와 도면 연결값을 도면에서 (2026-10-21 수석 지시 「웹에서 DXF 를 못 보이면 SVG 로 변환까지 패키지로」, F43)
|
|
||||||
웹 왼쪽 칸의 도면 SVG 를 DXF 에서 직접 만들고, 그림 연결값(MODEL.drawing)도 추출 도구가 도면에서 계산한다. 화면은 지금과 같아야 한다 — 수석이 준 SVG 는 DXF 의 그림 상자 4개를 **축척 없이 평행이동**해 놓은 배치였다(오차 0.01 m).
|
|
||||||
- `tools/views.json` 에 더함(사람 입력 — 배치): 상자마다 `place` [dx, dy] — 그 상자의 점 (X, Y) 를 SVG (X − dx, dy − Y) 에 놓는다(1 = 1 m). `viewBox` [0, 0, 443, 443], `fit` [118, 160, 245, 250](처음 화면).
|
|
||||||
|
|
||||||
### 29.1 `tools/dxf_to_svg.py <drawing.dxf> <views.json> <출력.svg>` (표준 라이브러리만)
|
|
||||||
- 객체마다 그 좌표 범위가 들어가는 상자(28장과 같은 방법)의 place 로 옮긴다. 상자에 안 들어가는 객체는 그리지 않는다.
|
|
||||||
- 레이어 표(TABLES 의 LAYER: 2 이름, 62 색 — 음수면 꺼짐, 70 의 1 비트 = 얼림)에서 꺼짐·얼림·`Defpoints` 레이어는 그리지 않는다.
|
|
||||||
- 색: 객체 62 가 없거나 256 = 레이어 색, 0 = 블록을 넣은 INSERT 의 색. ACI → RGB: 1 빨강 (255,0,0), 2 노랑, 3 초록, 4 하늘 (0,255,255), 5 파랑, 6 자홍, 7 검정 (0,0,0)(원본 SVG 와 같게), 8 (128,128,128), 9 (192,192,192), 250~255 회색 6단(51, 91, 132, 173, 214, 255), 10~249 는 색상 (i−10)//10 × 15°, i%10 짝수 = 채도 1·홀수 = 0.5, 밝기 [1, 1, .8, .8, .6, .6, .5, .5, .3, .3][i%10] (HSV).
|
|
||||||
- 그리기: LINE·LWPOLYLINE(코드 42 볼록 → 원호)·POLYLINE(VERTEX, 42)·LEADER(꼭짓점 꺾은선)·CIRCLE·ARC(50·51 도, 반시계)·SOLID(10·11·13·12 순 채움)·TEXT·MTEXT·INSERT(BLOCKS 구역의 블록을 기준점 10/20 빼고, 41·42 배율, 50 회전, 10/20 위치로; 겹친 INSERT 도)·DIMENSION(코드 2 의 블록을 그대로). Y 를 뒤집으므로 원호 방향도 뒤집힌다.
|
|
||||||
- 글자: 높이 40, 회전 50, 72/73 이 0 이 아니면 11/21 을 기준점, 72 → text-anchor(0 start·1 middle·2 end·4 middle). %%c → Ø, %%d → °, %%p → ±, %%u 지움. MTEXT 는 3·1 을 이어 붙이고 서식 코드(\P → 공백, \f…; 등, 중괄호)를 지운다. font-family "Dotum,'sans-serif'".
|
|
||||||
- 출력: 루트 `<svg viewBox="0 0 443 443">` + `<style>* { vector-effect: non-scaling-stroke; }</style>`, 색마다 `<g fill="none" stroke="rgb(…)" stroke-width="1">`. 원본처럼 선은 화면 1 px.
|
|
||||||
- `make_model.sh` 가 이것도 돌려 출력 폴더에 `drawing_gen.svg` 를 만든다(private/ 이면 private/drawing_gen.svg).
|
|
||||||
|
|
||||||
### 29.2 도면 연결값 (추출 도구 `drawing_views`)
|
|
||||||
- 추출 도구는 `tools/views.json`(세 번째 인수로 다른 파일을 줄 수 있다)을 읽어 `MODEL.drawing` 을 만든다: src "private/drawing_gen.svg", viewBox·fit = views.json, section {x0, y0, mm_per_unit 1000} = secA 값, plan {cx, cy, mm_per_unit 1000} = plan1 값, views 11개(id·type·label·필드는 13.2·22장과 같다). 모든 좌표 = 도면 점을 그 상자 place 로 옮긴 값(소수 둘째 자리).
|
|
||||||
- secA·secB: 전체 단면 A-A(20.1 ① 벽 1~4)·B-B(24.2 벽 5~8)의 축 X 와 그 축의 지반선(20.1 ②) → x0, y0.
|
|
||||||
- secA2·secB2(section_break): `S-TITLE` 「단면 A-A」·「단면 B-B」(공백 무시) 글자 X 에서 가까운, 길이 10 m 넘는 `구조물` 세로선 X(1 cm 로 반올림, 중복 하나로) 4개 = 그 생략 단면의 벽. 축 = 바깥 두 선 가운데, 지반선(초록, ±0.05 m 로 축을 지나는 선) → x0, y0_upper. 층 글자(「숫자B」, 축에서 8 m 안)마다, 안쪽 왼쪽 벽선에서 시작하고 글자 아래 0.3 m 안에 있는 `구조물` 수평선 Y 로 그 층의 「지표에 해당하는 Y」 = Y − 그 층 윗면 z(20.1 ③)/1000 을 구한다. 지반선과 같은(±0.05) 층들 = 위 구간, 나머지 = 아래 구간: y0_lower = 아래 구간 값의 평균을 같은 place 로 옮긴 것, z_upper_min = 위 구간 가장 깊은 층 윗면 − 300, z_lower_max = 아래 구간 가장 높은 층 윗면.
|
|
||||||
- plan1 = 평면도 1 중심(24.1 ①), plan2 = 그 아래 30 m 안의 가장 가까운 반지름 4.85 원. rot_deg = −φ(24.1, 소수 첫째 자리). tunnel: plan1 true, plan2 false.
|
|
||||||
- 슬래브 평면 5개: 20.1 ④ 의 중심과 「하부슬래브(20B)」 중심. z1 = 홀수층(1·3·…·17B 윗면)·짝수층(2·…·16B)·[18B]·[19B]·[하부 슬래브 윗면], kind slab(20B 만 bottom), tunnel 은 19B 만 true.
|
|
||||||
- 대조(F43): 생성 SVG 가 XML 로 읽히고 viewBox 443, 요소 7000 개 이상·글자 400 개 이상, **원본 SVG(private/drawing.svg)와 선이 지나는 0.5 칸 비교(상자 안)에서 재현율·정밀도 0.85 이상**(원본의 해치는 DXF 에서 기준점이 (0,0) 이라 범위에서 빠져 1 이 되진 않는다) / 추출 drawing = model.js drawing(views 11개 숫자 ±0.05, z1 ±5, 플래그 같음, rot_deg ±0.1, viewBox·fit 같음) / views.json 의 평면도 상자 place 를 10 m 바꾼 사본으로 추출 → plan1·plan2 cx 만 10 바뀜.
|
|
||||||
- 코치는 이 규칙대로만 짠 별도 구현으로 SVG(재현율 0.885·정밀도 0.928)와 views 11개(정답지와 ±0.01)를 미리 확인했다.
|
|
||||||
|
|
||||||
## 30. 변경 — 화면을 도면에서 만든 모델로 전환 (2026-10-21 수석 지시, F44)
|
|
||||||
이제 3D 와 도면 연결이 모두 도면(DWG)에서 만들어진다(20~29장). 첫 화면을 그 모델로 바꾸고, 손으로 만든 정답 모델은 비교용 화면으로 남긴다.
|
|
||||||
- `index.html`: `model_extracted.js` 다음 `viewer.js` 를 읽는다(model.js 는 읽지 않는다). **1.x 의 F01 「model.js 다음 viewer.js」 를 이것으로 바꾼다.** 왼쪽 도면 = MODEL.drawing.src(private/drawing_gen.svg).
|
|
||||||
- `answer.html`(새 파일, 정답지 화면): index.html 과 같은 화면이되 `model.js` 다음 `viewer.js`. 제목에 「정답지」, 맨 위 안내 한 줄: 「손으로 만든 정답 모델 — 도면에서 만든 모델(index.html)을 검사하는 기준」. 왼쪽 도면 = model.js 의 drawing.src(private/drawing.svg, 수석이 준 SVG 처리본).
|
|
||||||
- `viewer.js`: 도면 그림 파일을 `MODEL.drawing.src` 에서 읽는다(없으면 'private/drawing.svg'). 「도면 파일 없음」 안내도 그 경로를 보인다.
|
|
||||||
- compare.html 은 그대로 둔다(F34 — 추출 모델 비교 화면, 이제 index.html 과 같은 데이터).
|
|
||||||
- README: 첫 화면 = 도면에서 만든 모델, answer.html = 정답지, 다시 만들기 = `bash tools/make_model.sh`.
|
|
||||||
- 구동 설명서(코치 작성, 이 장과 함께 커밋): 구동 쪽 — 여는 방법·도면에서 다시 만들기(형식별 용도 표)·잘 안 열릴 때 / 목적 쪽 — 3D 가 도면에서 만들어진다는 한 줄, 사다리 4개(위 2 + 환기구 쪽 2).
|
|
||||||
|
|
||||||
## 31. 추가 — README 「사용 도구」 절 (제출 점검, F45)
|
|
||||||
- 11.2 로 README 를 코드 설명으로 고쳐 쓸 때 빠진 「사용 도구」 절을 되살린다(제출 가산점: WSL2·git).
|
|
||||||
- README 에 `## 사용 도구` 절: WSL2 Ubuntu(그 아래 `wsl.exe -l -v` 출력을 코드 블록에 그대로 — WSL 안에서는 `wsl.exe -l -v`, 글자 사이 빈 칸은 지워도 된다), Claude Code(Haiku 5.5)가 코드 작성·수정·커밋(커밋마다 Co-Authored-By 줄), 설계 문서·검사(SPEC.md·FEATURES.json·check.sh·구동 설명서)는 수석 지시로 코치 세션이 쓰고 하이쿠가 KIT 커밋, GNU LibreDWG 0.14(DWG → DXF), Python 3 표준 라이브러리, git, 브라우저(Chrome·Edge).
|
|
||||||
- 대조(F45): README 에 「사용 도구」, `wsl -l -v` 출력(Ubuntu 와 VERSION 2), Haiku, LibreDWG, check.sh.
|
|
||||||
|
|
||||||
## 32. 변경 — 환경에 따라 못 하는 검사는 SKIP (2026-10-21 수석 지시 「압축에 DXF 를 넣어라, 실패가 나오는 건 싫어」)
|
|
||||||
- 제출 압축에는 `private/drawing.dxf`·`drawing.svg`·`drawing_gen.svg` 가 들어가고 원본 DWG 는 빠진다. 평가자 PC 에는 GNU LibreDWG(dwg2dxf)가 없을 수 있다.
|
|
||||||
- 그래서 F42 의 「원본 DWG 로 make_model.sh 를 돌려 보는」 검사 3개는 dwg2dxf 나 private/drawing.dwg 가 없으면 FAIL 이 아니라 **SKIP**(이유 표시)으로 센다. 둘이 다 있는 개발 환경에서는 지금처럼 돈다. 다른 검사는 그대로.
|
|
||||||
|
|
||||||
## 33. 추가 — WSL2 설치 증빙 파일 (2026-10-22 수석 지시 「WSL2 를 설치했다는 증빙자료가 없다」, F46)
|
|
||||||
- `evidence/wsl2.txt`: 명령 출력 그대로 모은 증빙.
|
|
||||||
- 앞부분 「Windows 쪽」: 코치 세션이 Windows PowerShell 에서 실행해 모은 출력(wsl --version, wsl -l -v, wsl --status, 등록된 배포판과 가상 디스크 ext4.vhdx 를 만든 날 = 설치일). 이 장과 함께 커밋한다.
|
|
||||||
- 뒷부분 「WSL 안쪽」: 하이쿠가 WSL 안에서 직접 실행해 덧붙인다 — 수집 시각(date), `uname -r`(커널 이름에 microsoft-standard-WSL2), `cat /proc/version`, `/etc/os-release` 의 PRETTY_NAME, `pwd`(작업 폴더 /mnt/d/…), `claude --version`, `git --version`, `python3 --version`, `dwg2dxf --version` 첫 줄. 명령마다 `$ 명령` 줄 다음에 출력 그대로.
|
|
||||||
- README 「사용 도구」: 예전 커밋에서 옮겨 적은 출력 대신 `evidence/wsl2.txt` 를 가리키고, 설치일(가상 디스크 만든 날)과 오늘 `wsl -l -v` 출력을 적는다.
|
|
||||||
- 대조(F46): evidence/wsl2.txt 에 `wsl -l -v` 출력(Ubuntu, VERSION 2)·설치일·`microsoft-standard-WSL2`·Ubuntu 버전·claude 버전, README 가 그 파일을 가리킨다.
|
|
||||||
|
|
||||||
## 34. 공개용 정리본 (2026-10-27 수석 지시 — 사내 Gitea `tdc_engineer/drawing3d-web` 에 올림)
|
|
||||||
- 이 저장소는 1차 제출본(커밋 81개, 로컬 보존)을 **공개용으로 정리해 새 이력으로 시작**한 것이다. 옛 이력에는 도면 실제 좌표가 들어 있어 그대로 올리지 않았다. 옛 커밋 기록(메시지)은 `evidence/gitlog_v1.txt`.
|
|
||||||
- 도면 정리(코치, 저장소 밖 도구): 우리가 쓰는 그림 객체와 거기 딸린 블록·레이어·선종류만 남기고, 과업명·지명·외부 참조 이름·저장자·XDATA·OBJECTS 를 지웠다. **모든 좌표를 같은 양만큼 평행이동**해 실제 위치를 알 수 없게 했다(옮긴 양은 기록하지 않는다).
|
|
||||||
- 같은 양을 `tools/views.json`(그림 상자·배치)과 `check.sh`(채점기만 아는 기준점·변조 시험 좌표)에도 적용했다. 추출 결과(3D 데이터 95부재)·도면 연결 11개·생성 SVG 는 옮기기 전과 같다(SVG 바이트 동일, 부재는 1e-9 mm 잡음 하나).
|
|
||||||
- `private/` 에 올리는 것: `drawing.dxf`(정리본), `drawing.svg`(정답지 화면용 그림), `drawing_gen.svg`(첫 화면 그림). 원본 DWG 는 올리지 않으므로 F42 의 DWG 재변환 검사 3건은 SKIP(32장). `.gitignore` 는 `private/` 전체 대신 `private/*.dwg`·`private/full.dxf` 등을 뺀다 — F13 의 「.gitignore 에 private/」 기준을 이것으로 바꾼다. F13 의 「도면 파일 커밋 금지」 항목도 「커밋된 도면은 정리본 3개뿐」으로 바꾼다(처음 바꿀 때 빠뜨림 — 첫 커밋 뒤 이 항목이 FAIL 이었다).
|
|
||||||
@@ -7,10 +7,10 @@ cd "$(dirname "$0")" || exit 2
|
|||||||
python3 - "$@" <<'PY'
|
python3 - "$@" <<'PY'
|
||||||
import json, math, os, re, sys
|
import json, math, os, re, sys
|
||||||
|
|
||||||
ORDER = ["F01", "F02", "F03", "F04", "F05", "F06", "F07", "F08", "F09", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32", "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45", "F46"]
|
ORDER = ["F01", "F02", "F03", "F04", "F05", "F06", "F07", "F08", "F09", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32", "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45", "F46", "F47"]
|
||||||
upto = sys.argv[1].upper() if len(sys.argv) > 1 else ORDER[-1]
|
upto = sys.argv[1].upper() if len(sys.argv) > 1 else ORDER[-1]
|
||||||
if upto not in ORDER:
|
if upto not in ORDER:
|
||||||
print("사용법: bash check.sh [F01~F46]")
|
print("사용법: bash check.sh [F01~F47]")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
LIMIT = ORDER.index(upto)
|
LIMIT = ORDER.index(upto)
|
||||||
TOL = 1.0
|
TOL = 1.0
|
||||||
@@ -233,12 +233,9 @@ miss = [w for w in want if not any(w in h for h in heads)]
|
|||||||
report("F16", bool(guide) and not miss, "구동설명서에 제목 목적·구동·조작 (SPEC 18.3)" + (f" — 없음: {', '.join(miss)}" if miss else ""))
|
report("F16", bool(guide) and not miss, "구동설명서에 제목 목적·구동·조작 (SPEC 18.3)" + (f" — 없음: {', '.join(miss)}" if miss else ""))
|
||||||
rd = readme or ""
|
rd = readme or ""
|
||||||
rheads = [l for l in rd.splitlines() if l.startswith("## ")]
|
rheads = [l for l in rd.splitlines() if l.startswith("## ")]
|
||||||
rwant = ["파일 구조", "실행", "데이터", "코드", "그리기", "도면 연결", "검사"]
|
rwant = ["실행", "파일 구조", "검사", "사용 도구"]
|
||||||
rmiss = [w for w in rwant if not any(w in h for h in rheads)]
|
rmiss = [w for w in rwant if not any(w in h for h in rheads)]
|
||||||
report("F17", "🔒" in rd and not rmiss, "README.md 에 🔒 와 ## 제목 7개(파일 구조~검사)" + (f" — 없음: {', '.join(rmiss)}" if rmiss else ""))
|
report("F17", "🔒" in rd and not rmiss, "README.md 는 짧은 안내서: 🔒 와 ## 제목(실행·파일 구조·검사·사용 도구) (이슈 #1)" + (f" — 없음: {', '.join(rmiss)}" if rmiss else ""))
|
||||||
allf = sorted({n for fid2, names in FUNCS.items() if ORDER.index(fid2) <= LIMIT for n in names} | {"facesBox"})
|
|
||||||
fmiss = [n for n in allf if n not in rd]
|
|
||||||
report("F17", not fmiss, f"README.md 에 함수 이름 {len(allf)}개 전부" + (f" — 없음: {', '.join(fmiss)}" if fmiss else ""))
|
|
||||||
|
|
||||||
# ---- F18: 구동설명서 HTML 위키 (SPEC 12장) ----
|
# ---- F18: 구동설명서 HTML 위키 (SPEC 12장) ----
|
||||||
KEEP = ["index", "purpose", "run", "control"]
|
KEEP = ["index", "purpose", "run", "control"]
|
||||||
@@ -1083,6 +1080,19 @@ report("F46", re.search(r"Ubuntu\s+\S+\s+2\b", ev) is not None and "설치일" i
|
|||||||
report("F46", "microsoft-standard-WSL2" in ev and "PRETTY_NAME" in ev and re.search(r"\$ claude --version\s*\n\s*\S", ev) is not None, "evidence/wsl2.txt WSL 안쪽: microsoft-standard-WSL2 커널·Ubuntu 버전·claude 버전")
|
report("F46", "microsoft-standard-WSL2" in ev and "PRETTY_NAME" in ev and re.search(r"\$ claude --version\s*\n\s*\S", ev) is not None, "evidence/wsl2.txt WSL 안쪽: microsoft-standard-WSL2 커널·Ubuntu 버전·claude 버전")
|
||||||
report("F46", "evidence/wsl2.txt" in (readme or ""), "README 사용 도구가 evidence/wsl2.txt 를 가리킨다")
|
report("F46", "evidence/wsl2.txt" in (readme or ""), "README 사용 도구가 evidence/wsl2.txt 를 가리킨다")
|
||||||
|
|
||||||
|
# ---- F47: md 가이드 최소화 (이슈 #1) ----
|
||||||
|
try:
|
||||||
|
trk47 = [t for t in subprocess.run(["git", "ls-files"], capture_output=True, text=True).stdout.split("\n") if t]
|
||||||
|
except OSError:
|
||||||
|
trk47 = []
|
||||||
|
md47 = [t for t in trk47 if t.lower().endswith(".md")]
|
||||||
|
sz47 = sum(os.path.getsize(t) for t in md47 if os.path.exists(t))
|
||||||
|
report("F47", bool(trk47) and len(md47) <= 3 and sz47 <= 8192, f"저장소 md 3개 이하·합계 8KB 이하 (실제 {len(md47)}개 {sz47} B: {', '.join(md47)})")
|
||||||
|
gone47 = [t for t in ("FEATURES.json", "PROGRESS.md") if t in trk47 or os.path.exists(t)]
|
||||||
|
report("F47", bool(trk47) and not gone47, "FEATURES.json·PROGRESS.md 없음 — 할 일은 이슈, 경과는 커밋" + (f" — 남음: {gone47}" if gone47 else ""))
|
||||||
|
cl47 = read("CLAUDE.md") or ""
|
||||||
|
report("F47", "Gitea 이슈" in cl47 and "그 이슈만" in cl47 and "(#이슈번호)" in cl47, "CLAUDE.md 에 이슈 규칙(작업 목록은 이슈·그 이슈만 읽기·커밋에 #번호)")
|
||||||
|
|
||||||
print(f"결과: PASS {count['PASS']} / FAIL {count['FAIL']} / SKIP {count['SKIP']} (검사 범위 F01~{upto})")
|
print(f"결과: PASS {count['PASS']} / FAIL {count['FAIL']} / SKIP {count['SKIP']} (검사 범위 F01~{upto})")
|
||||||
sys.exit(0 if count["FAIL"] == 0 else 1)
|
sys.exit(0 if count["FAIL"] == 0 else 1)
|
||||||
PY
|
PY
|
||||||
Reference in new issue
Block a user