Compare commits
5
Commits
f33e00bce4
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
367356187b | ||
|
|
4d21c3bb08 | ||
|
|
3308c2759b | ||
|
|
e5336f6016 | ||
|
|
bdd296597a |
No files matched your search
@@ -1,37 +1,26 @@
|
|||||||
# 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`·`checks/`·`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장 목록을 쓴다.
|
다른 하이쿠가 다른 폴더(git worktree)에서 다른 이슈를 할 수 있다. 내 폴더·내 브랜치만 건드린다. main 에 합치기(merge)·push 는 지시가 있을 때만.
|
||||||
- 길이 단위는 mm, z 위로 +. 각도는 X축에서 반시계, 도.
|
|
||||||
- 면 = 점 배열 + 색 + alpha. 먼 면부터 그린다.
|
|
||||||
|
|
||||||
## 막히면
|
## 막히면
|
||||||
- 같은 문제를 두 번 고쳐도 안 되면 멈추고 수석에게 무엇이 안 되는지 보고한다.
|
같은 문제를 두 번 고쳐도 안 되면 멈추고 무엇이 안 되는지 보고한다. 모르는 치수는 지어내지 않는다.
|
||||||
- 모르는 치수는 지어내지 않는다. SPEC 에 없으면 수석에게 묻는다.
|
|
||||||
-499
@@ -1,499 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "F01",
|
|
||||||
"description": "골격: index.html + viewer.js + model.js 빈 파일, 캔버스가 창을 채운다",
|
|
||||||
"steps": [
|
|
||||||
"index.html 에 canvas 하나, model.js 다음 viewer.js 순서로 script 태그",
|
|
||||||
"model.js 는 SPEC 5장 꼴로 units 는 mm, parts 는 빈 배열",
|
|
||||||
"viewer.js 는 캔버스를 창 크기에 맞추고 배경만 칠한다",
|
|
||||||
"bash check.sh F01"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F02",
|
|
||||||
"description": "스파이크 1: 캔버스에 삼각형 하나를 칠한다",
|
|
||||||
"steps": [
|
|
||||||
"viewer.js 안에 고정 좌표 삼각형 하나를 path 로 채운다",
|
|
||||||
"브라우저로 열어 삼각형이 보이는지 수석이 확인",
|
|
||||||
"bash check.sh F02"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F03",
|
|
||||||
"description": "스파이크 2: 시험용 큐브 하나를 정사 투영, 면 음영, 먼 면부터 그리기로 그린다",
|
|
||||||
"steps": [
|
|
||||||
"facesBox, rotatePoint, project, shadeColor, sortFaces, draw 를 만든다",
|
|
||||||
"시험 큐브 1개(viewer.js 안 고정값)를 그린다",
|
|
||||||
"면마다 밝기가 다르고 뒤쪽 면이 앞쪽 면을 덮지 않는지 수석이 확인",
|
|
||||||
"bash check.sh F03"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F04",
|
|
||||||
"description": "스파이크 3: 등각 초기 화면 + 마우스 드래그 궤도 회전",
|
|
||||||
"steps": [
|
|
||||||
"카메라 초기값을 등각(좌우 45도, 기울기 약 35도)으로",
|
|
||||||
"onMouseDown, onMouseMove, onMouseUp 으로 좌우 = Z축 둘레, 위아래 = 기울기",
|
|
||||||
"드래그할 때마다 draw 다시 호출",
|
|
||||||
"bash check.sh F04"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F05",
|
|
||||||
"description": "휠 확대/축소",
|
|
||||||
"steps": [
|
|
||||||
"onWheel 로 배율 변경, 너무 작거나 크지 않게 한계",
|
|
||||||
"bash check.sh F05"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F06",
|
|
||||||
"description": "model.js 데이터: 벽, 중간 슬래브 19장, 19B 패드, 하부 슬래브, 바닥콘크리트 (SPEC 4.1~4.3, 5장)",
|
|
||||||
"steps": [
|
|
||||||
"SPEC 4.1~4.3 표의 숫자를 그대로 parts 에 적는다(mm)",
|
|
||||||
"const MODEL = 뒤는 순수 JSON (따옴표 키, 끝 쉼표 없음)",
|
|
||||||
"bash check.sh F06"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F07",
|
|
||||||
"description": "disc 그리기: 슬래브·패드·하부 슬래브·바닥콘크리트를 32각형 원판으로, 화면에 모델 전체가 들어오게",
|
|
||||||
"steps": [
|
|
||||||
"facesDisc, buildFaces 를 만들고 시험 큐브는 지운다",
|
|
||||||
"처음 배율을 모델 크기에 맞춘다",
|
|
||||||
"bash check.sh F07"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F08",
|
|
||||||
"description": "ring 그리기: 벽체를 32각형 속 빈 원통으로, alpha 0.25 반투명",
|
|
||||||
"steps": [
|
|
||||||
"facesRing (바깥면, 안쪽면, 위아래 고리면)",
|
|
||||||
"alpha 를 globalAlpha 로 적용, 벽 너머 슬래브가 보이는지 수석이 확인",
|
|
||||||
"bash check.sh F08"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F09",
|
|
||||||
"description": "TBM 터널: pipe 데이터(SPEC 4.5) + 누운 속 빈 원통 그리기",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 tunnel 1개: 시작점, 330도, 길이 20650, r_out 1500, r_in 1300",
|
|
||||||
"facesPipe",
|
|
||||||
"평면 4시 방향, 19B 높이에 붙었는지 수석이 확인",
|
|
||||||
"bash check.sh F09"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F10",
|
|
||||||
"description": "계단 18런: stair 데이터(SPEC 4.4) + 디딤판 블록 그리기",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 stair 18개 (첫 단 100, 단 200, 디딤판 300, 폭 1000)",
|
|
||||||
"facesStair: 블록마다 facesBox 를 부른다",
|
|
||||||
"층마다 방향이 바뀌는지 수석이 확인",
|
|
||||||
"bash check.sh F10"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F11",
|
|
||||||
"description": "터널 안 채움: fill 3개(잡석, 패드 2단, 패드 1단) 데이터 + 그리기 (일정이 밀리면 가장 먼저 뺀다)",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 fill 3개 (SPEC 4.5 표)",
|
|
||||||
"facesFill: 원과 수평선으로 자른 단면을 터널 축 방향으로 늘인다",
|
|
||||||
"bash check.sh F11"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F12",
|
|
||||||
"description": "README.md: 구동 방법 3줄, 조작법, 모델 설명(구조물·단위), 사용 도구(WSL2·Claude Code Haiku), wsl -l -v 한 줄, 🔒 표기",
|
|
||||||
"steps": [
|
|
||||||
"README.md 작성 (코치가 읽고 수석에게 의견)",
|
|
||||||
"bash check.sh"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F13",
|
|
||||||
"description": "좌우 분할 화면: 왼쪽 도면 칸, 끌어서 움직이는 가운데 막대, 오른쪽 3D (SPEC 10.1)",
|
|
||||||
"steps": [
|
|
||||||
"index.html 에 #drawPane, #splitter, #viewPane",
|
|
||||||
"onSplitDown/onSplitMove/onSplitUp 으로 왼쪽 폭 15%~85%",
|
|
||||||
"폭이 바뀌면 캔버스를 오른쪽 칸에 맞추고 draw",
|
|
||||||
"도면이 없으면 안내 글자",
|
|
||||||
"bash check.sh F13"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F14",
|
|
||||||
"description": "도면 표시·확대·이동: #drawStage 안 img + 겹친 svg, 처음 fit 상자에 맞춤 (SPEC 10.2)",
|
|
||||||
"steps": [
|
|
||||||
"#drawStage, img#drawing, svg#hotspots(viewBox 0 0 443 443)",
|
|
||||||
"fitDrawing, onDrawWheel(마우스 위치 기준 0.5~40배), onDrawDown/Move/Up(이동)",
|
|
||||||
"bash check.sh F14"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F15",
|
|
||||||
"description": "도면 클릭 → 3D 강조: MODEL.drawing 데이터 + 부재별 클릭 영역 + 선택 표시 (SPEC 10.3)",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 drawing 추가(SPEC 10.3 값 그대로)",
|
|
||||||
"sectionPoly, planPoly, buildHotspots 로 영역 만들기",
|
|
||||||
"selectPart: 도면 영역 칠하기, 3D 부재 #ffd400, 「선택: 이름」",
|
|
||||||
"bash check.sh F15"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F16",
|
|
||||||
"description": "구동설명서.md: 목적·기획·범위·구동·조작·작업 방식·한계·이력 (SPEC 11.1)",
|
|
||||||
"steps": [
|
|
||||||
"SPEC 11.1 순서대로 구동설명서.md 작성, 첫머리 🔒",
|
|
||||||
"근거는 SPEC.md·PROGRESS.md·git log 에서만",
|
|
||||||
"bash check.sh F16"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F17",
|
|
||||||
"description": "README.md 를 코드 설명으로 고쳐 쓰기: 파일 구조·실행·데이터·코드·그리기 방식·도면 연결·검사 (SPEC 11.2)",
|
|
||||||
"steps": [
|
|
||||||
"SPEC 11.2 순서대로 README.md 다시 쓰기, 첫머리 🔒",
|
|
||||||
"SPEC 7장·10.3 함수 이름 전부와 한 줄 설명",
|
|
||||||
"bash check.sh F17"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F18",
|
|
||||||
"description": "구동설명서를 guide/ HTML 위키(목차 + 8쪽)로 옮기고 3D 칸 오른쪽 위에 「구동 설명서」 버튼 (SPEC 12장)",
|
|
||||||
"steps": [
|
|
||||||
"구동설명서.md 내용을 guide/ 8쪽으로 나눠 옮기고 md 는 지운다",
|
|
||||||
"모든 쪽 맨 위 「← 3D 화면으로 돌아가기」(../index.html), 🔒, h1, 다른 쪽 링크 2개 이상, 목차·이전/다음",
|
|
||||||
"index.html 에 a#guideLink(guide/index.html) 를 3D 칸 오른쪽 위에",
|
|
||||||
"bash check.sh F18"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F19",
|
|
||||||
"description": "선명한 도면: tools/prep_drawing.py 로 1px 선 SVG 만들기 + 실제 크기로 확대 (SPEC 13.1)",
|
|
||||||
"steps": [
|
|
||||||
"tools/prep_drawing.py 작성",
|
|
||||||
"수석이 준 원본 경로로 실행해 private/drawing.svg 다시 만들기(커밋 안 됨)",
|
|
||||||
"applyDrawView: width/height 로 확대, translate 로 이동",
|
|
||||||
"README.md 코드 절에 새 함수 이름과 한 줄 설명 추가",
|
|
||||||
"bash check.sh F19"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F20",
|
|
||||||
"description": "모든 그림 연결: MODEL.drawing.views 11개 + 그림별 클릭 영역 + 여러 부재 한 번에 선택 (SPEC 13.2)",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 drawing.views (SPEC 13.2 표 그대로)",
|
|
||||||
"viewHotspots, buildHotspots 가 views 를 돈다",
|
|
||||||
"selectPart 가 id 배열을 받는다",
|
|
||||||
"README.md 코드 절에 새 함수 이름과 한 줄 설명 추가",
|
|
||||||
"bash check.sh F20"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F21",
|
|
||||||
"description": "슬래브 구멍(holes) + 계단 방향 정정 (SPEC 15.1~15.2)",
|
|
||||||
"steps": [
|
|
||||||
"model.js: 슬래브·19B 패드에 holes (15.1 표 그대로)",
|
|
||||||
"model.js: 계단 18런 x_top·dir·y0·y1 을 15.2 대로",
|
|
||||||
"facesDiscHoles: 윗면·아랫면 evenodd + 구멍 옆면",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F21"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F22",
|
|
||||||
"description": "카메라 이동: 오른쪽 드래그 팬, 마우스 기준 확대 0.5~40배, 더블클릭·처음 화면 버튼 (SPEC 15.3)",
|
|
||||||
"steps": [
|
|
||||||
"CAM.target 기준으로 project",
|
|
||||||
"panView, zoomAt, resetView",
|
|
||||||
"index.html 에 button#resetView, 오른쪽 클릭 메뉴 막기",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F22"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F23",
|
|
||||||
"description": "사람 애니메이션: 1B~19B 계단을 쉬지 않고 오르내림, 사람 ▶/❚❚ 버튼 (SPEC 15.4)",
|
|
||||||
"steps": [
|
|
||||||
"buildWalkPath, walkerAt, facesPerson, animate",
|
|
||||||
"index.html 에 button#walkToggle",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F23"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F24",
|
|
||||||
"description": "바닥부 부재: 월류벽(prism 3)·집수조·침사조(zone)·사다리(ladder), 19B 슬래브·패드 침사구 구멍 (SPEC 16.1)",
|
|
||||||
"steps": [
|
|
||||||
"model.js 에 16.1 표 그대로",
|
|
||||||
"facesPrism, facesZone, facesLadder",
|
|
||||||
"README.md 데이터·코드 절에 새 type 과 함수",
|
|
||||||
"bash check.sh F24"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F25",
|
|
||||||
"description": "바닥부 도면 연결: 단면 A-A·20B 평면·19B 침사구 (SPEC 16.2)",
|
|
||||||
"steps": [
|
|
||||||
"bottomHotspots 를 buildHotspots 에 연결",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F25"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F26",
|
|
||||||
"description": "상부 슬래브·맨홀 목 2개·벽 윗면 −2000·전기 배선구 구멍 정정 (SPEC 17.1)",
|
|
||||||
"steps": [
|
|
||||||
"model.js: 벽 z1 −2000, topslab, neck 2, elec 다각형",
|
|
||||||
"holePolygon(원 구멍), prism holes",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F26"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F27",
|
|
||||||
"description": "분기구(obox 5)·환기구 덕트와 환기탑(obox 13) (SPEC 17.2)",
|
|
||||||
"steps": [
|
|
||||||
"model.js: 17.2 표 그대로",
|
|
||||||
"facesObox",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F27"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F28",
|
|
||||||
"description": "방호 난간 18장·펌프 배출관·위쪽 사다리 2개 (SPEC 17.3)",
|
|
||||||
"steps": [
|
|
||||||
"model.js: 17.3 그대로",
|
|
||||||
"facesRailing",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F28"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F29",
|
|
||||||
"description": "상부 부재 도면 연결: 단면 A-A·평면도 1·2 (SPEC 17.4)",
|
|
||||||
"steps": [
|
|
||||||
"upperHotspots 를 buildHotspots 에 연결",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F29"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F30",
|
|
||||||
"description": "사람 두 명이 2초 간격으로 줄지어 이동 (SPEC 18.1)",
|
|
||||||
"steps": [
|
|
||||||
"teamAt: 첫 사람 t, 두 번째 t−2초",
|
|
||||||
"facesPerson 를 두 번(색 #ff8c1a, #e07010)",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F30"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F31",
|
|
||||||
"description": "콘크리트 계단: 경사판 밑면, 계단 아래 빈 공간 (SPEC 18.2)",
|
|
||||||
"steps": [
|
|
||||||
"stairProfile: 18.2 의 점 순서 그대로",
|
|
||||||
"facesStair: 단면을 Y 띠 폭만큼 늘인 면(블록 방식 버림)",
|
|
||||||
"README.md 코드 절에 새 함수",
|
|
||||||
"bash check.sh F31"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F32",
|
|
||||||
"description": "구동설명서를 목차 + 목적·구동·조작 3쪽으로 정리, 🔒 줄 빼기 (SPEC 18.3, 코치 작성·하이쿠 커밋)",
|
|
||||||
"steps": [
|
|
||||||
"코치가 쓴 guide/ 변경을 그대로 커밋, 지울 쪽은 git rm",
|
|
||||||
"bash check.sh F32"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F33",
|
|
||||||
"description": "도면 추출 도구 tools/extract_from_dxf.py → model_extracted.js (SPEC 20.1, DXF 입력)",
|
|
||||||
"steps": [
|
|
||||||
"20.1 규칙대로 작성, 표준 라이브러리만, 도면 좌표·정답 값 금지",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js",
|
|
||||||
"README.md 에 도구 설명",
|
|
||||||
"bash check.sh F33"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F34",
|
|
||||||
"description": "대조 검사·변조 시험 통과 + compare.html (SPEC 20.2)",
|
|
||||||
"steps": [
|
|
||||||
"check.sh F34 의 FAIL 을 도구 쪽에서 고친다(model.js·check.sh 는 고치지 않는다)",
|
|
||||||
"compare.html",
|
|
||||||
"bash check.sh F34"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F35",
|
|
||||||
"description": "계단 18런 추출 → 추출 모델에서도 사람 두 명이 걷는다 (SPEC 21장)",
|
|
||||||
"steps": [
|
|
||||||
"section_stairs, stair_parts (21장 규칙대로, 도면 좌표·정답 값 금지)",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js 로 다시 만들기",
|
|
||||||
"compare.html 에서 계단·사람이 보이는지(수석·코치 화면 확인)",
|
|
||||||
"README.md 도구 설명 갱신",
|
|
||||||
"bash check.sh F35"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F36",
|
|
||||||
"description": "정답지 좌표틀 정정 — 평면도 출처 부재를 슬래브 평면 틀로 (SPEC 22장)",
|
|
||||||
"steps": [
|
|
||||||
"model.js 를 22장 표대로 고친다(표에 없는 값은 건드리지 않는다)",
|
|
||||||
"plan1·plan2 에 rot_deg -30.3",
|
|
||||||
"viewer.js planXY·planPoly 에 rot_deg 회전",
|
|
||||||
"bash check.sh F36"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F37",
|
|
||||||
"description": "터널·채움 추출 → 추출 모델에도 TBM 터널 (SPEC 23장)",
|
|
||||||
"steps": [
|
|
||||||
"section_tunnel, plan_tunnel, tunnel_parts (23장 규칙대로, 도면 좌표·정답 값 금지)",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js 로 다시 만들기",
|
|
||||||
"README.md 도구 설명 갱신",
|
|
||||||
"bash check.sh F37"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F38",
|
|
||||||
"description": "분기구·환기구 추출 → 추출 모델에도 상부 구조 (SPEC 24장)",
|
|
||||||
"steps": [
|
|
||||||
"plan_center, frame_angle, section_heights, branch_parts, vent_parts (24장 규칙대로, 도면 좌표·정답 값 금지)",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js 로 다시 만들기",
|
|
||||||
"README.md 도구 설명 갱신",
|
|
||||||
"bash check.sh F38"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F39",
|
|
||||||
"description": "나머지 부재 추출 — 상부 슬래브·목·패드·하부 슬래브·바닥 콘크리트·월류벽·집수조·침사조 (SPEC 25장)",
|
|
||||||
"steps": [
|
|
||||||
"top_parts, pad_part, bottom_parts, weir_parts (25장 규칙대로, 도면 좌표·정답 값 금지)",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js 로 다시 만들기",
|
|
||||||
"README.md 도구 설명 갱신",
|
|
||||||
"bash check.sh F39"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F40",
|
|
||||||
"description": "정답지 보강 — 환기구 벽 사다리·환기탑 사다리, ladder angle_deg (SPEC 26장)",
|
|
||||||
"steps": [
|
|
||||||
"viewer.js facesLadder 에 angle_deg 회전",
|
|
||||||
"model.js 에 26장 표의 부재 2개 추가(다른 값은 건드리지 않는다)",
|
|
||||||
"index.html 에서 두 사다리가 덕트 입구 벽·환기탑 안에 보이는지(수석·코치 화면 확인)",
|
|
||||||
"bash check.sh F40"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F41",
|
|
||||||
"description": "설계 규칙 파일 tools/design_rules.json + 추출 모델이 정답지 부재 전체를 갖는다 (SPEC 27장)",
|
|
||||||
"steps": [
|
|
||||||
"tools/design_rules.json 작성(🔶 부재 6개·style)",
|
|
||||||
"extract_from_dxf.py 에 apply_design_rules, id 를 model.js 와 같게",
|
|
||||||
"python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js",
|
|
||||||
"README.md 도구 설명 갱신",
|
|
||||||
"bash check.sh F41"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F42",
|
|
||||||
"description": "변환 패키지 — tools/dwg_to_dxf.sh, tools/select_views.py, tools/make_model.sh (SPEC 28장)",
|
|
||||||
"steps": [
|
|
||||||
"dwg_to_dxf.sh, select_views.py(28장 규칙, 좌표 숫자 금지), make_model.sh",
|
|
||||||
"bash tools/make_model.sh 로 private/ 다시 만들기(결과가 지금과 같아야 한다)",
|
|
||||||
"README.md 「변환 패키지」 절",
|
|
||||||
"bash check.sh F42"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F43",
|
|
||||||
"description": "DXF → SVG(tools/dxf_to_svg.py) + 도면 연결값을 도면에서 (SPEC 29장)",
|
|
||||||
"steps": [
|
|
||||||
"tools/dxf_to_svg.py (29.1)",
|
|
||||||
"make_model.sh 에 SVG 단계",
|
|
||||||
"추출 도구 drawing_views (29.2)",
|
|
||||||
"bash tools/make_model.sh 로 private/drawing_gen.svg·model_extracted.js 다시 만들기",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F43"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F44",
|
|
||||||
"description": "화면 전환 — index.html = 도면에서 만든 모델, answer.html = 정답지 (SPEC 30장)",
|
|
||||||
"steps": [
|
|
||||||
"index.html 이 model_extracted.js 를 읽게",
|
|
||||||
"answer.html 새로",
|
|
||||||
"viewer.js 가 MODEL.drawing.src 로 도면을 읽게",
|
|
||||||
"README.md 갱신",
|
|
||||||
"bash check.sh F44"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F45",
|
|
||||||
"description": "README 「사용 도구」 절 (SPEC 31장)",
|
|
||||||
"steps": [
|
|
||||||
"wsl.exe -l -v 출력 확인",
|
|
||||||
"README.md 에 ## 사용 도구 절",
|
|
||||||
"bash check.sh F45"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "F46",
|
|
||||||
"description": "WSL2 설치 증빙 evidence/wsl2.txt (SPEC 33장)",
|
|
||||||
"steps": [
|
|
||||||
"evidence/wsl2.txt 뒤에 「WSL 안쪽」 명령 출력 덧붙이기(33장 목록, 직접 실행)",
|
|
||||||
"README 「사용 도구」 갱신",
|
|
||||||
"bash check.sh F46"
|
|
||||||
],
|
|
||||||
"passes": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
# PROGRESS — 기능 1개 = 커밋 1개
|
|
||||||
|
|
||||||
한 줄 양식: `YYYY-MM-DD HH:MM | 기능 ID | 한 일 | check.sh 결과(PASS n / FAIL n) | 막힌 것`
|
|
||||||
|
|
||||||
- 2026-10-20 | KIT | 설계 키트 5파일 추가(SPEC·FEATURES·CLAUDE·check.sh·PROGRESS) | - | -
|
|
||||||
2026-10-20 13:45 | F01 | 골격: index.html(canvas 1, model.js→viewer.js) · model.js(mm, parts 빈 배열) · viewer.js(창 크기 맞춤, 배경 칠하기) | PASS 3 / FAIL 0 | -
|
|
||||||
2026-10-20 13:51 | F02 | 스파이크 1: viewer.js draw()에 고정 좌표 삼각형 하나를 path 로 채움 | PASS 4 / FAIL 0 | 수석 육안 확인 대기
|
|
||||||
2026-10-20 13:53 | F03 | 스파이크 2: viewer.js 에 facesBox·rotatePoint·project·shadeColor·sortFaces·draw, 등각 카메라, 시험 큐브 1개(한 변 2000) 를 면 음영·먼 면부터 그림 | PASS 5 / FAIL 0 | 수석 육안 확인 대기
|
|
||||||
2026-10-20 13:55 | F04 | 스파이크 3: viewer.js 에 onMouseDown·onMouseMove·onMouseUp 추가, 드래그로 yaw(좌우)·pitch(위아래, ±89도 제한) 를 바꾸고 draw 다시 호출. 등각 초기값은 기존 그대로 | PASS 6 / FAIL 0 | 수석 육안 확인 대기 (드래그 회전)
|
|
||||||
2026-10-20 13:56 | F05 | 휠 확대/축소: viewer.js 에 ZOOM(0.5~5)·onWheel 추가, 휠 한 칸 10%, draw 의 SCALE 에 ZOOM 곱함 | PASS 7 / FAIL 0 | 수석 육안 확인 대기 (휠 확대)
|
|
||||||
2026-10-20 14:24 | F06 | model.js 데이터: 벽체(ring alpha 0.25), 중간 슬래브 19장(1B~19B, 상면 z·두께 300·r 4850), 19B 패드, 하부 슬래브(20B), 바닥콘크리트 — SPEC 4.1~4.3 숫자 그대로, const MODEL = 순수 JSON | PASS 17 / FAIL 0 | 수석 확인 대기 (화면은 아직 모델 안 그림)
|
|
||||||
2026-10-20 14:25 | F07 | disc 그리기: viewer.js 에 facesDisc(32각형 뚜껑 2+옆면 32), buildFaces(disc 만), MODEL_CENTER·MODEL_R 로 처음 배율을 모델 전체에 맞춤, 시험 큐브 제거 | PASS 18 / FAIL 0 | 수석 화면 확인 대기 (모델 전체가 들어오는지)
|
|
||||||
2026-10-20 14:26 | F08 | ring 그리기: viewer.js 에 facesRing(바깥면·안쪽면·윗고리·아랫고리 각 32장), buildFaces 가 ring 도 모음, 벽 alpha 0.25 는 globalAlpha 로 적용 | PASS 19 / FAIL 0 | 수석 화면 확인 대기 (벽 너머 슬래브가 보이는지)
|
|
||||||
2026-10-20 14:27 | F09 | TBM 터널: model.js 에 tunnel 1개(pipe, 축 시작 x 4200.2·y -2425.0·z -65050, 330도, 길이 20650, r_out 1500, r_in 1300), viewer.js 에 facesPipe 추가 후 buildFaces 에 pipe 연결 | PASS 23 / FAIL 0 | 수석 확인 대기 (평면 4시 방향, 19B 높이에 붙었는지)
|
|
||||||
2026-10-20 14:29 | F10 | 계단 18런: model.js 에 stair 18개(SPEC 4.4, 단 수 17·마지막 14, dir 번갈아), viewer.js 에 facesStair 추가 후 buildFaces 에 연결, 디딤판마다 facesBox 블록 | PASS 28 / FAIL 0 | 수석 확인 대기 (층마다 계단 방향이 바뀌는지)
|
|
||||||
2026-10-20 14:31 | F11 | 터널 안 채움: model.js 에 fill 3개(잡석 -1300~-1175, 패드 2단 -1175~-1100, 패드 1단 -1100~-1050 폭 800), viewer.js 에 facesFill(원을 수평선으로 자른 단면을 축 방향으로 늘임) 추가 후 buildFaces 에 연결 | PASS 32 / FAIL 0 | 수석 화면 확인 대기 (터널 안이 19B 패드 높이까지 차는지)
|
|
||||||
2026-10-20 14:31 | F12 | README.md 작성: 구동 방법 3줄(index.html 더블클릭 · explorer.exe · Chrome/Edge), 조작법, 모델 설명(단위·구조물·데이터 파일), 검사 명령, 사용 도구(WSL2·Claude Code Haiku), wsl -l -v, 🔒 표기 | PASS 33 / FAIL 0 | 수석 확인 대기 (README 문장 검토)
|
|
||||||
2026-10-20 15:21 | F13 | 좌우 분할: index.html 에 #drawPane·#splitter·#viewPane, viewer.js 에 onSplitDown/Move/Up (폭 15%~85%), 캔버스는 #viewPane 크기에 맞춤, private/drawing.svg 없으면 안내 글자 | PASS 37 / FAIL 0 | 수석 화면 확인 대기 (막대 끌기, 3D 칸 크기 맞춰지는지)
|
|
||||||
2026-10-20 15:22 | F14 | 도면 확대·이동: index.html 에 #drawStage(img#drawing + svg#hotspots 443), viewer.js 에 fitDrawing·onDrawWheel(마우스 기준 0.5~40배)·onDrawDown/Move/Up, 처음 화면은 MODEL.drawing.fit 없으면 도면 전체 | PASS 39 / FAIL 0 | 수석 화면 확인 대기 (도면 휠·드래그, 처음 화면이 왼쪽 칸에 꽉 차는지)
|
|
||||||
2026-10-20 15:24 | F15 | 도면 클릭 연결: model.js 에 drawing 추가, sectionPoly·planPoly·buildHotspots·selectPart 로 부재별 클릭 영역, 선택 시 도면 노란 칠·3D 노란색·「선택: 이름」, 드래그 뒤 클릭 무시 | PASS 41 / FAIL 0 (F01~F15) | 수석 화면 확인 대기 (도면 영역 위치가 맞는지, 클릭·선택 해제)
|
|
||||||
2026-10-20 15:25 | F16 | 구동설명서.md 작성: 목적·기획(결정 6개)·범위·구동·조작·작업 방식·한계·이력(git log 표), 첫머리 🔒 | PASS 43 / FAIL 0 (F01~F16) | 수석 문서 검토 대기 (구동설명서 기획·한계 문장)
|
|
||||||
2026-10-20 15:26 | F17 | README.md 를 코드 설명으로 고쳐 씀: 파일 구조·실행·데이터·코드(함수 28개 한 줄씩)·그리기 방식·도면 연결·검사 (SPEC 11.2), 첫머리 🔒 | PASS 45 / FAIL 0 (F01~F17) | 수석 문서 검토 대기 (README 코드 설명 문장)
|
|
||||||
2026-10-20 15:29 | F18 | 구동설명서.md 를 _소거대기로 옮김(내용은 guide/ 8쪽에 있음), 3D 칸 「구동 설명서」 버튼 | 결과: PASS 50 / FAIL 0 / SKIP 0 (검사 범위 F01~F18) | 수석 화면 확인 대기 (guide 쪽 내용·버튼 위치)
|
|
||||||
2026-10-20 15:32 | F18 | 구동설명서 보완: guide/ 7쪽 pager 에 목차 링크 추가(목적·기획·범위·구동·조작·작업 방식·한계, 이력은 다음 자리 비움), 작업 방식·기획의 화면 확인 문장을 사실대로 정정(F02 이후 코치 localhost 미리보기, 더블클릭 구동은 수석) | PASS 50 / FAIL 0 (F01~F18) | 없음
|
|
||||||
2026-10-20 19:07 | F19 | tools/prep_drawing.py 작성, 원본으로 private/drawing.svg 재생성(1px 선, 커밋 안 함), viewer.js 의 applyDraw 를 applyDrawView 로 바꿔 width/height 확대 + translate 이동, README 함수 한 줄 추가 | PASS 54 / FAIL 0 (F01~F19, F20 SKIP) | 수석 화면 확인 대기 (도면 선이 확대해도 얇은지)
|
|
||||||
2026-10-20 19:11 | F20 | MODEL.drawing.views 11개(SPEC 13.2 표), viewHotspots·circlePoly·region·pathD 추가, 영역을 path 로 바꾸고 evenodd 로 벽 고리 구멍, selectPart 가 id 배열을 받음, 3D SELECTED 배열화, README 함수 추가 | PASS 56 / FAIL 0 (F01~F20) | 수석 화면 확인 대기 (그림 11장 클릭, 여러 부재 한 번에 선택, 「선택:」 글자)
|
|
||||||
2026-10-20 19:13 | F20 | 보완: 「선택:」 글자를 한 줄(nowrap)·오른쪽 120px 여백으로, 여럿일 때 「처음 둘, … 마지막」(SPEC 13.2) | PASS 56 / FAIL 0 (F01~F20) | 수석 화면 확인 대기 (홀수층 슬래브 선택 시 글자가 설명서 버튼과 안 겹치는지)
|
|
||||||
2026-10-20 19:15 | DOC | guide/ 9쪽을 사용 설명서로 다시 씀(index·purpose·run·control·plan·scope·limits·method·history, 수석 지시로 코치 세션 작성) | 결과: 문서만 변경, check.sh 안 돌림 | 수석 화면 확인 대기 (guide 9쪽)
|
|
||||||
2026-10-20 20:57 | F21 | model.js: 슬래브 19장·패드에 holes(SPEC 15.1 표), 계단 18런 x_top·dir·Y 띠 정정(15.2); viewer.js: facesDiscHoles 추가, buildFaces 가 disc 에 사용, draw 가 holes 를 evenodd 로 채움; README 함수 한 줄 | PASS 59 / FAIL 0 (F01~F21, F22·F23 SKIP) | 수석 화면 확인 대기 (슬래브 구멍 위치, 계단 입구 방향)
|
|
||||||
2026-10-20 20:59 | F22 | viewer.js: CAM.target 기준 project, panView(오른쪽 드래그·Shift+왼쪽), zoomAt(마우스 아래 점 고정, 0.5~40), resetView(더블클릭·처음 화면 버튼), contextmenu 막기, unrotateVector·scaleFor 추가; index.html 에 button#resetView; README 함수 추가 | PASS 62 / FAIL 0 (F01~F22, F23 SKIP) | 수석 화면 확인 대기 (오른쪽 드래그 이동, 휠 확대 40배, 더블클릭 복귀). guide/control.html 의 휠 0.5~5 줄은 아직 옛 값
|
|
||||||
2026-10-20 21:01 | F23 | 사람 애니메이션: buildWalkPath(계단 18런 내려갔다 되짚어 올라오기, 1000mm/s), walkerAt, facesPerson(상자 4개, 진행 방향에 맞춰 돌림, #ff8c1a), animate(rAF, 초당 30번 이하 다시 그림), setWalking; index.html 에 button#walkToggle; README 코드 절에 5개 함수 | PASS 65 / FAIL 0 (F01~F23) | 수석 화면 확인 대기 (사람이 계단을 오르내리는지, ▶/❚❚ 버튼, 다리 간격 100 해석 확인)
|
|
||||||
2026-10-20 21:05 | DOC | guide/ 7쪽(purpose·control·plan·scope·limits·method·history)을 F21~F23 에 맞춰 고침: 슬래브 구멍, 카메라 이동(오른쪽 드래그·Shift+드래그·휠 0.5~40·더블클릭), 사람 애니메이션(▶/❚❚) 반영. 수석 지시로 코치 세션 작성 | 결과: 문서만 변경, check.sh 안 돌림 | 수석 화면 확인 대기 (guide 7쪽 갱신분)
|
|
||||||
2026-10-20 21:12 | F24 | model.js: 월류벽 3·집수조·침사조 zone 2·사다리 1, 19B 슬래브·패드 침사구 구멍 (SPEC 16.1); viewer.js: facesPrism, facesZone, facesLadder 추가, buildFaces 가 prism·zone·ladder 를 받음; README 데이터 표·코드 절 추가 | PASS 70 / FAIL 0 (F01~F24, F25 SKIP) | 수석 화면 확인 대기 (월류벽 홈, 집수조·침사조 반투명 색, 사다리 위치·가로대, 19B 침사구 구멍). 사다리 폭 400·가로대 300 은 도면에 없는 코치 값
|
|
||||||
2026-10-20 21:15 | F25 | viewer.js: xSpan·planXY·bottomPlanPoly·bottomHotspots 추가, sectionPoly 가 xSpan 사용, buildHotspots 가 bottomHotspots 를 viewHotspots 뒤에 이음 (순서 집수조→침사조→월류벽→사다리); README 코드 절에 새 함수 4개 | PASS 71 / FAIL 0 (F01~F25) | 수석 화면 확인 대기 (단면 A-A·20B 평면에서 집수조·침사조 활꼴, 월류벽 홈, 사다리 클릭, 19B 평면 침사구 클릭 → 사다리 선택)
|
|
||||||
2026-10-20 21:19 | F26 | model.js: 벽 z1 −2000, topslab(disc r 5500, 원 구멍 2), neck 2(prism, 맨홀 목·원 구멍), elec 19개 슬래브 다각형으로 정정 / viewer.js: holePolygon(원→16각형), addHoles(facesDiscHoles 에서 분리), facesPrism 에 holes; README 데이터·코드 절 갱신 | PASS 74 / FAIL 0 / SKIP 8 (F01~F26) | 수석 화면 확인 대기 (상부 슬래브 뚜껑 원 구멍 2개, 맨홀 목 2개, 배선구 평행사변형). facesPrism 바깥 면 법선 부호가 반대로 보임(코드 읽기로만 확인, 화면 미확인) — F24 월류벽도 같은 함수
|
|
||||||
2026-10-20 21:20 | F27 | model.js: 분기구 obox 5, 환기 덕트·환기탑 obox 13 (SPEC 17.2 표 그대로, angle 167/0, cx=cy=0) / viewer.js: facesObox(facesBox 를 (s,t,z) 로 만들고 angle_deg 만큼 돌려 cx,cy 에 놓음, 법선도 돈다), buildFaces 에 obox 분기; README 데이터·코드 절 갱신(부재 72개) | PASS 77 / FAIL 0 / SKIP 5 (F01~F27, F28·F29 SKIP) | 수석 화면 확인 대기 (분기구가 167도 방향 옆 굴, 덕트가 남쪽으로 내려가 동쪽으로 꺾여 환기탑 연결, 환기탑 높이 지표 0)
|
|
||||||
2026-10-20 21:22 | F28 | model.js: 방호 난간 18장(railing, z·h 1100·paths), 펌프 배출관(pipe ring, 0,4220), 위쪽 사다리 2개(ladder_up) 추가 (SPEC 17.3) / viewer.js: facesRailing(꺾은선 구간마다 두께 40 판 6장) 추가, buildFaces 에 railing 분기 / README 데이터 표·부재 수 93개·코드 절 갱신 | PASS 81 / FAIL 0 / SKIP 1 (F01~F28, F29 범위 밖) | 수석 화면 확인 대기 (1B~17B 윗면 난간 높이 1100, 19B 패드 위 난간, 배출관 집수조→상부 슬래브 밑, 서쪽·가운데 사다리). 판 두께 40 과 난간 색 #d8c890 은 도면에 없는 코치 값
|
|
||||||
2026-10-20 21:24 | F29 | viewer.js: obox4 추가, xSpan 이 neck·obox 를 다룸, upperHotspots 추가(단면 A-A 사각형·평면도 원판 r 4850 → 맨홀 목 → 상자 바닥 윤곽), buildHotspots 순서 상부→벽·터널→바닥부 / README 함수 절 갱신 | 결과: PASS 82 / FAIL 0 / SKIP 0 (검사 범위 F01~F29) | 수석 화면 확인 대기 (단면 A-A 상부 슬래브·목·분기구·환기구·위쪽 사다리 클릭, 평면도 1·2 에서 원판·목·상자 클릭 후 벽 고리 클릭). 원판 평면 r 4850 은 SPEC 17.4 값(상부 슬래브 r 5500 과 다름), 코치 값 아님
|
|
||||||
2026-10-20 21:26 | F29 | viewer.js 보완: 분기구·환기구는 같은 kind 덩어리 전체 선택(upperHotspots 의 group, KIND_LABEL), 선택 문구를 kind 이름으로 (「선택: 평면도 — 분기구」「… — 환기구(강제)」), 목·사다리는 하나씩 | PASS 82 / FAIL 0 / SKIP 0 (F01~F29) | 수석 화면 확인 대기 (단면 A-A·평면도 1·2 에서 분기구·환기구 누르면 5개·13개 전체가 노랗게 되는지)
|
|
||||||
2026-10-20 21:28 | DOC | guide/ 사용 설명서 6쪽(purpose·plan·scope·limits·method·history) F24~F29 에 맞춰 갱신 (코치 세션 작성, 수석 사전 승인, SPEC 14장) / viewer.js·model.js 변경 없음 | PASS 82 / FAIL 0 / SKIP 0 (F01~F29) | 수석 확인 대기 (guide/ 6쪽 본문 검토)
|
|
||||||
2026-10-21 10:00 | F30 | model.js 변경 없음 / viewer.js: teamAt(t) 추가(첫 사람 t, 두 번째 t−TEAM_LAG 2000ms), facesPerson 에 색 인자(WALK_COLORS #ff8c1a, #e07010), draw 에서 두 사람 그림, walkerAt 음수 시각 접음 / README 사람 애니메이션 절 갱신 | PASS 82 / FAIL 0 / SKIP 4 (F01~F30 검사, F31·F32 범위 밖) | 수석 화면 확인 대기 (사람 두 명이 2초 간격으로 같은 길을 가는지, 시작 직후 두 번째 사람이 경로 끝에서 튀지 않는지)
|
|
||||||
2026-10-21 10:02 | F31 | viewer.js: stairProfile 추가(18.2 점 순서, 디딤판 300·챌판 200, s_b=(z_high−z_low−400)×1.5), facesStair 를 단면 Y 띠 압출(옆면 2 + 둘레 변마다 사각형)로 교체, 블록 방식 삭제 / README 함수 절 갱신(stairProfile 추가, facesBox·facesStair 설명 고침) | PASS 83 / FAIL 0 / SKIP 0 (F01~F31) | 수석 화면 확인 대기 (계단 18런이 경사판 밑면을 가진 콘크리트 계단으로 보이는지, 계단 아래 빈 공간·사람 경로와 어긋나지 않는지). 첫 점(0,z_high)→(0,z_high−200) 과 닫는 변이 s=0 에서 겹치는 것은 SPEC 18.2 점 순서 그대로 둔 것
|
|
||||||
2026-10-21 10:02 | F32 | guide/ 를 목차·목적·구동·조작 3쪽으로 정리: 코치 작성 index·purpose·run·control 그대로 커밋, plan·scope·limits·method·history git rm / README·index.html 변경 없음 (SPEC 18.3) | 결과: PASS 86 / FAIL 0 / SKIP 0 (검사 범위 F01~F32) | 수석 화면 확인 대기 (guide/index.html 목차 → 목적·구동·조작 3쪽 링크, 🔒 줄 없음)
|
|
||||||
2026-10-21 14:49 | F33 | tools/extract_from_dxf.py 추가: DXF 에서 벽·지표·슬래브 윗면 19·평면 구멍·난간 추출 (SPEC 20.1, 도면 좌표 코드에 없음) / model_extracted.js 생성 / README 파일 구조 두 줄 | PASS 89 / FAIL 0 / SKIP 9 (검사 범위 F01~F33) | 수석 확인 대기 (대조는 F34). 19B 침사구 구멍 1개는 20.1 규칙(오프닝·구조물 계단 레이어)에 안 잡힘 — F34 전에 판단 필요. 벽 z 는 19.1 고정값 -70200·-2000 사용
|
|
||||||
2026-10-21 14:50 | F34 | 대조 검사: 도구 쪽 검사 통과(model.js 변경 없음) / compare.html 추가(model_extracted.js 읽음, 「추출 데이터로 그린 화면」 글자) / viewer.js: MODEL.drawing 없을 때 hotspots·도면 로드 건너뜀 | PASS 98 / FAIL 0 / SKIP 0 (F01~F34) | 수석 화면 확인 대기 (compare.html 3D 칸이 index.html 과 같은 모양인지, 왼쪽 안내 글자)
|
|
||||||
2026-10-21 14:53 | F34 | 보완: viewer.js 계단 없으면 사람·버튼 숨김(HAS_WALK), MODEL.drawing 없으면 클릭 영역 안 만듦 / model.js·check.sh·SPEC.md 무변경 | PASS 98 / FAIL 0 / SKIP 0 (F01~F34) | index.html 의 <path> NaN 4개는 HEAD 에도 있던 것(수석 판단 대기, 이번엔 손대지 않음)
|
|
||||||
2026-10-21 14:55 | F29 | 보완: viewer.js xSpan 이 사다리를 kind 로 찾아 위쪽 사다리(kind ladder_up)가 NaN 이 되던 것을 type 으로 고침 (단면 A-A·A-A 생략 그림의 ladder_up_W·ladder_up_C 영역 4개, x0~x1) / FEATURES.json·check.sh·SPEC.md 무변경 | PASS 81 / FAIL 0 / SKIP 17 (검사 범위 F01~F29) | 수석 화면 확인 대기 (F12 콘솔 path NaN 오류 사라졌는지)
|
|
||||||
2026-10-21 19:32 | F35 | tools/extract_from_dxf.py: 계단 18런 추출 section_stairs·stair_profile·stair_dir·stair_parts (SPEC 21장, 도면 좌표·정답 값 코드에 없음) / model_extracted.js 다시 생성 / README 도구 설명 갱신 | 결과: PASS 100 / FAIL 0 / SKIP 0 (검사 범위 F01~F35) | 수석 확인 대기 (compare.html 에 계단·사람 두 명이 보이는지)
|
|
||||||
2026-10-21 20:05 | F36 | 정답지 좌표틀 정정 (SPEC 22장): model.js 표대로 터널·잡석·패드 0°·(4850,0), 상부슬래브 구멍·맨홀 목 2개 축 정렬, 분기구 197.3°, 환기구 30.3°, 위쪽 사다리 W 값 / 평면도 1·2 rot_deg -30.3 / viewer.js planXY·planPoly 회전 | 결과: PASS 103 / FAIL 0 / SKIP 0 (검사 범위 F01~F36) | 수석 화면 확인 대기 (compare·index 평면도 클릭 영역이 도면 그림 위에 놓이는지, 터널이 계단과 나란히 +X 로 가는지)
|
|
||||||
2026-10-21 20:11 | F37 | 터널 추출 section_tunnel·plan_tunnel·tunnel_parts (SPEC 23장, 도면 좌표·정답 값 코드에 없음) / inner_r 추가 / model_extracted.js 다시 생성 / README 도구 설명 갱신 | 결과: PASS 108 / FAIL 0 / SKIP 0 (검사 범위 F01~F37) | 수석 확인 대기 (compare.html 에 터널·잡석·패드가 계단과 나란히 보이는지)
|
|
||||||
2026-10-21 20:24 | F38 | 분기구·환기구 추출 (SPEC 24장): plan_center·frame_angle(φ 30.3 류, 도면에서 구함)·section_heights(A-A 좌측 X, B-B 우측 X)·branch_parts·vent_parts / model_extracted.js 다시 생성 / README 도구 설명 갱신 | 결과: PASS 113 / FAIL 0 / SKIP 0 (검사 범위 F01~F38) | 수석 화면 확인 대기 (compare.html 에 분기구·환기구·환기탑이 계단·터널과 맞물려 보이는지)
|
|
||||||
2026-10-21 20:37 | F39 | 나머지 부재 추출: top_slab_part·neck_parts(상부 슬래브·맨홀 목 2)·pad_and_sump(19B 패드·침사구)·bottom/lean(하부 슬래브·바닥 콘크리트)·weir_parts·pit_parts(월류벽 3·집수조/침사조) / 벽 높이를 도면에서 읽도록 고정값 제거 / 20B 평면 제목 추가 / model_extracted.js 다시 생성 / README 도구 설명 갱신 | 결과: PASS 124 / FAIL 0 / SKIP 0 (검사 범위 F01~F39) | 수석 화면 확인 대기 (compare.html 에 상부 슬래브·목·패드·월류벽·집수조가 맞는지)
|
|
||||||
2026-10-21 20:39 | F40 | 정답지 보강 (SPEC 26장): viewer.js facesLadder 에 angle_deg 회전(원점 기준, 법선 포함) / model.js 에 환기구 사다리 2개(ladder_vent_wall·ladder_vent_tower, kind ladder_vent) / .gitignore 에 __pycache__/ | 결과: PASS 126 / FAIL 0 / SKIP 0 (검사 범위 F01~F40) | 수석 화면 확인 대기 (index.html 에서 환기구 벽 사다리가 덕트 입구 벽에, 환기탑 사다리가 탑 안에 보이는지)
|
|
||||||
2026-10-21 20:42 | F41 | 설계 규칙 파일 tools/design_rules.json (🔶 부재 6개·style·ids) / extract_from_dxf.py 에 load_design_rules·apply_design_rules (부재 뒤에 🔶 붙이고 color·alpha 덮어씀) / model_extracted.js 다시 생성(95개) / README 도구 설명 갱신 | 결과: PASS 132 / FAIL 0 / SKIP 0 (검사 범위 F01~F41) | 수석 화면 확인 대기 (compare.html 에 사다리·배출관이 색까지 model.js 와 같게 보이는지)
|
|
||||||
2026-10-21 20:49 | F42 | 변환 패키지: tools/dwg_to_dxf.sh(dwg2dxf 래퍼)·tools/select_views.py(views.json 상자 안 객체만, 좌표 숫자 없음)·tools/make_model.sh(DWG→full.dxf→drawing.dxf→model_extracted.js, private/ 출력 시 루트 model_extracted.js 갱신) / README 「변환 패키지」 절 / 객체 3356개 확인 | 결과: PASS 138 / FAIL 0 / SKIP 0 (검사 범위 F01~F42) | 수석 확인 대기 (README 변환 패키지 절, make_model.sh 실행 결과)
|
|
||||||
2026-10-21 21:01 | F43 | DXF→SVG 변환 도구 tools/dxf_to_svg.py (SPEC 29.1) / 추출 도구 drawing_views 로 도면 연결값을 도면에서 계산 (SPEC 29.2, views.json 세 번째 인수) / make_model.sh 에 SVG 단계 / drawing_gen.svg·model_extracted.js 다시 생성 / README 갱신 | 결과: PASS 144 / FAIL 0 / SKIP 0 (검사 범위 F01~F43) | 수석 화면 확인 대기 (index.html 왼쪽 도면이 drawing_gen.svg 로 원본과 같은지, README 변환 패키지 절)
|
|
||||||
2026-10-21 21:04 | F44 | 화면 전환: index.html 이 model_extracted.js 를 읽음 / answer.html 신설(정답지, model.js) / viewer.js 가 MODEL.drawing.src 로 도면을 읽고 없으면 그 경로 안내 / README 첫 화면·정답지·다시 만들기 갱신 | 결과: PASS 150 / FAIL 0 / SKIP 0 (검사 범위 F01~F44) | 수석 화면 확인 대기 (index.html 이 도면에서 만든 모델인지, answer.html 이 예전 정답지인지)
|
|
||||||
2026-10-21 21:08 | F45 | README 「사용 도구」 절 복원 (SPEC 31장): wsl -l -v 출력은 8f606f5 에 기록된 값을 그대로 옮김 (WSL 안에서 wsl.exe 호출 불가) / Haiku 5.5·LibreDWG·git·브라우저 / 커밋 메시지 Co-Authored-By 줄 | 결과: PASS 151 / FAIL 0 / SKIP 0 (검사 범위 F01~F45) | 수석 확인 대기 (README 사용 도구 절의 wsl 출력이 지금 Windows 값과 같은지)
|
|
||||||
2026-10-22 09:23 | F46 | WSL2 설치 증빙: evidence/wsl2.txt 에 WSL 안쪽 출력(uname·/proc/version·Ubuntu 26.04·claude 2.1.304·git·python3·dwg2dxf) 덧붙임 / README 사용 도구에 설치일 2026-07-09 와 증빙 파일 연결 | 결과: PASS 154 / FAIL 0 / SKIP 0 (검사 범위 F01~F46) | 수석 확인 대기 (evidence/wsl2.txt 의 Windows 쪽 설치일·WSL 안쪽 출력이 맞는지)
|
|
||||||
@@ -1,184 +1,45 @@
|
|||||||
# drawing3d-web — 코드 설명
|
# drawing3d-web — 코드 설명
|
||||||
|
|
||||||
이 저장소는 1차 과제 제출본을 사내 공개용으로 정리한 것입니다 (도면 실제 좌표·과업명 제거, 좌표 평행이동, 화면·검사 결과는 같음, SPEC 34장).
|
원형 수직구 일반도를 라이브러리 없이 Canvas 2D 로 그리는 3D 뷰어. 왼쪽 도면에서 부재를 누르면 오른쪽 3D 에서 강조된다. 사내 공개용 정리본(SPEC 6장 공개 규칙). 설계 근거는 `SPEC.md`, 구동 설명은 `guide/index.html`.
|
||||||
1차의 커밋 81개 기록은 `evidence/gitlog_v1.txt` 에 있습니다.
|
|
||||||
|
|
||||||
> 🔒 사내교육용 — 열람 외 재배포 등의 활동을 금함 (회사 도면의 치수를 옮겨 적음)
|
> 🔒 사내교육용 — 열람 외 재배포 등의 활동을 금함 (회사 도면의 치수를 옮겨 적음)
|
||||||
|
|
||||||
원형 수직구 일반도를 라이브러리 없이 Canvas 2D 로 그리는 3D 뷰어. 설계 근거는 `SPEC.md`, 구동 설명은 `guide/index.html` (HTML 위키).
|
## 실행
|
||||||
|
|
||||||
|
- `index.html` 을 Chrome 또는 Edge 로 연다. 서버·인터넷은 필요 없다. 첫 화면은 도면에서 만든 모델이고, `answer.html` 은 손으로 만든 정답지 화면이다.
|
||||||
|
- WSL 에서는 이 폴더에서 `explorer.exe index.html`.
|
||||||
|
|
||||||
## 파일 구조
|
## 파일 구조
|
||||||
|
|
||||||
| 파일 | 내용 |
|
- `index.html`, `answer.html`: 화면(도면 칸 · 가운데 막대 · 3D 칸). 스크립트는 데이터 다음 `viewer.js` 순서.
|
||||||
|---|---|
|
- `viewer.js`: 그리기·입력·도면 연결 코드 전부.
|
||||||
| `index.html` | 첫 화면(도면에서 만든 모델). 화면 배치(도면 칸 · 가운데 막대 · 3D 칸)와 스크립트 읽기 순서: `model_extracted.js` 다음에 `viewer.js` |
|
- `model_extracted.js`(첫 화면), `model.js`(정답지): `const MODEL = {...};` 한 줄짜리 순수 JSON.
|
||||||
| `answer.html` | 정답지 화면. `index.html` 과 같은 화면이되 `model.js` 를 읽는다. 손으로 만든 정답 모델이고, 도면에서 만든 모델(`index.html`)을 검사하는 기준이다 |
|
- `tools/`: 변환 도구, 추출 도구 `extract_from_dxf.py`, 설계 규칙 `design_rules.json`, 그림 상자 설정 `views.json`.
|
||||||
| `model.js` | 데이터. `const MODEL = {...};` 한 줄짜리 순수 JSON |
|
- `private/drawing.svg`, `private/drawing_gen.svg`: 도면. 커밋된 도면은 정리본 3개뿐이다(원본 DWG 는 `.gitignore`).
|
||||||
| `viewer.js` | 그리기·입력·도면 연결 코드 전부 |
|
- `evidence/`: WSL2 설치 증빙, 1차 커밋 기록.
|
||||||
| `private/drawing.svg` | 도면 원본. `.gitignore` 로 커밋 제외 |
|
- `SPEC.md`, `CLAUDE.md`, `check.sh`: 규약, 작업 규칙, 합격 판정(수정 금지).
|
||||||
| `check.sh` | 합격 판정 (수정 금지) |
|
|
||||||
| `tools/extract_from_dxf.py` | 도면 DXF(`private/drawing.dxf`)에서 데이터를 뽑는 도구. 벽·슬래브·구멍·난간에 더해 계단 18런(`section_stairs`, `stair_parts`)과 TBM 터널·채움 3개(`section_tunnel`, `plan_tunnel`, `tunnel_parts`)도 뽑는다. 분기구·환기구(덕트·환기탑)는 평면도 중심과 φ(`plan_center`, `frame_angle`), 단면 높이(`section_heights`), 상자 묶음(`branch_parts`, `vent_parts`)으로 뽑는다. 나머지 부재(상부 슬래브·맨홀 목·19B 패드·침사구·하부 슬래브·바닥 콘크리트·월류벽·집수조·침사조)는 `top_slab_part`, `neck_parts`, `pad_and_sump`, `weir_parts`, `pit_parts` 로 뽑는다. 나머지 부재 뒤에 설계 규칙 파일(`tools/design_rules.json`, `load_design_rules`·`apply_design_rules`)의 🔶 부재 6개(사다리 3·환기구 사다리 2·배출관)를 붙이고, 모든 부재의 색·투명도를 kind → id 순으로 덮어쓴다. 표준 라이브러리만 쓴다. `python3 tools/extract_from_dxf.py private/drawing.dxf model_extracted.js` |
|
|
||||||
| `model_extracted.js` | 위 도구가 만든 데이터(`model.js` 와 같은 꼴). 값은 도면에서 읽은 것이라 `model.js` 와 대조해 본다 |
|
|
||||||
| `SPEC.md`, `FEATURES.json`, `PROGRESS.md` | 설계, 기능 목록, 진행 기록 |
|
|
||||||
|
|
||||||
## 실행
|
## 다시 만들기 (변환 패키지)
|
||||||
|
|
||||||
- `index.html` 이 첫 화면이다. 도면에서 만든 모델(`model_extracted.js`)을 보여 준다. `answer.html` 은 손으로 만든 정답지 화면이다.
|
설계도면은 DWG 로 주고받는다. GNU LibreDWG 로 DXF 로 바꾸고, 그림 상자 안 객체만 남겨 모델을 뽑는다.
|
||||||
- `index.html` 을 더블클릭해서 Chrome 또는 Edge 로 연다. 서버·인터넷은 필요 없다.
|
|
||||||
- WSL 에서 열려면 이 폴더에서 `explorer.exe index.html` 을 실행한다.
|
|
||||||
- 다시 만들기: `bash tools/make_model.sh` (DWG 를 읽어 `model_extracted.js` 와 `private/drawing_gen.svg` 를 만든다).
|
|
||||||
- 도면은 모델의 `drawing.src` 경로에서 읽는다(첫 화면은 `private/drawing_gen.svg`, 정답지는 `private/drawing.svg`). 없으면 왼쪽 칸에 그 경로를 보이는 안내 글자가 나온다.
|
|
||||||
|
|
||||||
## 데이터 (`model.js`)
|
|
||||||
|
|
||||||
- 단위는 mm, 각도는 도. 원점은 수직구 축 중심 × 지표면, Z 는 위로 +.
|
|
||||||
- 최상위: `units`("mm"), `parts`(배열), `drawing`(도면 연결, 아래 「도면 연결」 참고).
|
|
||||||
- 모든 부재 공통 필드: `id`, `name`, `kind`, `type`, `color`("#rrggbb"), `alpha`(없으면 1).
|
|
||||||
- `kind`: `wall`, `topslab`, `neck`, `slab`, `pad`, `bottom`, `lean`, `stair`, `tunnel`, `fill`, `weir`, `pit`, `ladder`, `branch`, `vent`, `railing`, `ladder_up`
|
|
||||||
|
|
||||||
| type | 쓰는 kind | 필드 |
|
|
||||||
|---|---|---|
|
|
||||||
| `ring` (세운 속 빈 원통) | wall, pipe(펌프 배출관) | `cx, cy, z0, z1, r_out, r_in` |
|
|
||||||
| `disc` (세운 원판) | topslab, slab, pad, bottom, lean | `cx, cy, z0, z1, r`, `holes`(선택) |
|
|
||||||
| `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 이면 폭 제한 없음) |
|
|
||||||
| `prism` (다각형 기둥) | weir, neck | `polygon`([x, y] 점 목록), `z0, z1`, `holes`(선택) |
|
|
||||||
| `zone` (원 안 활꼴 기둥) | pit | `r, x_min, x_max, z0, z1`(alpha 0.25) |
|
|
||||||
| `ladder` (사다리) | ladder, ladder_up | `x0, x1, yc, width, z0, z1, rung`(가로대 간격) |
|
|
||||||
| `obox` (돌린 직육면체) | branch, vent | `cx, cy, angle_deg, s0, s1, t0, t1, z0, z1`(s·t 는 돌린 축 방향) |
|
|
||||||
| `railing` (방호 난간) | railing | `z`(설치 슬래브 윗면), `h`(높이), `paths`(꺾은선 목록, 각 [x, y] 점) |
|
|
||||||
|
|
||||||
- `holes` 목록의 한 원소는 [x, y] 점 목록(다각형)이나 `{"circle": [x, y, r]}`(16각형으로 그림)이다.
|
|
||||||
- 부재 93개: wall 1 · topslab 1 · neck 2 · slab 19 · pad 1 · bottom 1 · lean 1 · stair 18 · tunnel 1 · fill 3 · weir 3 · pit 2 · ladder 1 · branch 5 · vent 13 · railing 18 · pipe 1 · ladder_up 2.
|
|
||||||
- `drawing`: `src`(도면 파일), `viewBox`([0, 0, 443, 443]), `fit`([u0, v0, u1, v1], 처음 화면에 꽉 찰 도면 상자), `section`(`x0, y0, mm_per_unit`), `plan`(`cx, cy, mm_per_unit`), `views`(그림 11장: `id, type, label` 과 그림마다 기준 값, 아래 「도면 연결」).
|
|
||||||
|
|
||||||
## 코드 (`viewer.js`)
|
|
||||||
|
|
||||||
함수 이름은 SPEC 7장과 10장의 목록을 그대로 쓴다.
|
|
||||||
|
|
||||||
**부재 → 면** (각 면은 점 배열 · 색 · alpha)
|
|
||||||
- `facesBox`: 직육면체 6면을 만든다. 분기구·환기구 등 상자 부재가 쓴다.
|
|
||||||
- `facesDisc`: 세운 원판(32각형)을 뚜껑 2장과 옆면 32장으로 만든다.
|
|
||||||
- `holePolygon`: 구멍 하나를 점 목록으로 바꾼다. `{"circle": [x, y, r]}` 는 16각형이 된다.
|
|
||||||
- `addHoles`: 뚜껑 2장(윗면·아랫면)에 구멍 고리를 넣고(evenodd), 구멍 둘레마다 세로 옆면을 더한다. `facesDiscHoles`·`facesPrism` 이 함께 쓴다.
|
|
||||||
- `facesDiscHoles`: `facesDisc` 에 구멍(`holes`)을 얹는다. 구멍 점을 `cx, cy` 만큼 옮긴 뒤 `addHoles` 를 부른다.
|
|
||||||
- `facesRing`: 세운 속 빈 원통(32각형)을 바깥면·안쪽면·윗고리·아랫고리로 만든다.
|
|
||||||
- `facesPipe`: 누운 속 빈 원통(32각형)을 축 시작점에서 `angle_deg` 방향으로 만든다.
|
|
||||||
- `facesFill`: 원을 수평선 `v0`·`v1` 로 자른 단면을 터널 축 방향으로 늘인다. `half_width` 가 있으면 그 폭 안으로만.
|
|
||||||
- `facesPrism`: 다각형을 `z0`~`z1` 로 세운 기둥(뚜껑 2장과 변마다 옆면)을 만든다. `holes` 가 있으면 `addHoles` 로 구멍을 뚫는다. 월류벽·맨홀 목이 쓴다.
|
|
||||||
- `facesZone`: 원 안에서 `x_min`~`x_max` 만 남긴 활꼴 기둥을 `facesPrism` 으로 만든다. 집수조·침사조가 쓴다.
|
|
||||||
- `facesLadder`: 사다리 기둥 2개와 가로대(`rung` 간격, 두께 25)를 상자로 만든다.
|
|
||||||
- `facesObox`: 돌린 직육면체. `facesBox` 로 (s, t, z) 상자를 만든 뒤 `angle_deg` 만큼 돌리고 (`cx, cy`) 에 놓는다. 분기구·환기구 덕트·환기탑이 쓴다.
|
|
||||||
- `facesRailing`: 방호 난간. 꺾은선 구간마다 두께 40 의 얇은 판을 `z` 부터 `z + h` 까지 세운다(옆 2·끝 2·윗면·아랫면, 판당 6장).
|
|
||||||
- `stairProfile`: 계단 한 런의 단면(경사판·단·밑면)을 (s, z) 점 목록으로 만든다. s 는 `x_top` 에서 내려가는 방향 거리다.
|
|
||||||
- `facesStair`: `stairProfile` 의 단면을 Y 띠(`y0`~`y1`) 폭만큼 늘인다. 옆면 2개와 둘레 변마다 사각형 하나.
|
|
||||||
- `buildFaces`: 모든 부재의 면을 한 줄로 모은다.
|
|
||||||
|
|
||||||
**카메라와 투영**
|
|
||||||
- `rotatePoint`: 세계 좌표를 yaw(Z축 둘레)와 pitch(기울기)로 돌린다.
|
|
||||||
- `project`: 돌린 점을 화면 기준 좌표 [오른쪽, 위, 깊이]로 바꾸고, 캔버스 픽셀로 옮긴다.
|
|
||||||
|
|
||||||
**그리기**
|
|
||||||
- `shadeColor`: `#rrggbb` 색을 밝기 계수만큼 밝게 또는 어둡게 한다.
|
|
||||||
- `sortFaces`: 면 가운데 점의 깊이가 큰 것부터 정렬한다(먼 면부터).
|
|
||||||
- `draw`: 캔버스를 지우고 면을 정렬해 차례로 칠한다. 선택된 부재는 노란색으로 그린다.
|
|
||||||
|
|
||||||
**입력 (3D 칸)**
|
|
||||||
- `onMouseDown`: 드래그 시작 위치를 기억하고, 오른쪽 버튼 또는 Shift 가 눌렸으면 화면 이동으로 정한다.
|
|
||||||
- `onMouseMove`: 드래그 중 회전이면 yaw·pitch 를, 화면 이동이면 `panView` 를 부르고 `draw` 를 다시 부른다.
|
|
||||||
- `onMouseUp`: 드래그를 끝낸다.
|
|
||||||
- `panView`: 드래그한 픽셀만큼 보는 중심(`CAM.target`)을 화면 평면에서 옮긴다.
|
|
||||||
- `unrotateVector`: 화면 기준 벡터를 세계 좌표로 되돌린다(`rotatePoint` 의 역).
|
|
||||||
- `scaleFor`: 배율 값에 맞는 mm 당 픽셀 수를 돌려준다.
|
|
||||||
- `zoomAt`: 마우스 아래 점이 그대로 머물게 배율을 바꾸고 보는 중심을 옮긴다(0.5~40 사이).
|
|
||||||
- `resetView`: 등각 화면·배율 1·보는 중심 = 모델 중심으로 되돌린다(더블클릭, 「처음 화면」 버튼).
|
|
||||||
- `onWheel`: 휠로 `zoomAt` 을 부른다.
|
|
||||||
|
|
||||||
**화면 나누기**
|
|
||||||
- `onSplitDown`: 가운데 막대 끌기를 시작한다.
|
|
||||||
- `onSplitMove`: 왼쪽 칸 폭을 창 폭의 15%~85% 로 바꾸고 3D 캔버스를 다시 맞춘다.
|
|
||||||
- `onSplitUp`: 막대 끌기를 끝낸다.
|
|
||||||
|
|
||||||
**사람 애니메이션**
|
|
||||||
- `buildWalkPath`: 계단 18런의 데이터로 내려가는 길을 만들고, 같은 길을 되짚어 올라오는 한 바퀴 경로(구간마다 길이·진행 방향)를 만든다.
|
|
||||||
- `walkerAt`: 시각(ms)에 맞는 사람의 발 위치와 진행 방향을 돌려준다. 경로 끝에 오면 처음으로 돌아간다.
|
|
||||||
- `teamAt`: 두 사람의 위치를 돌려준다. 두 번째 사람은 첫 사람보다 2초 뒤 시각(`TEAM_LAG`)으로 `walkerAt` 을 부른다.
|
|
||||||
- `facesPerson`: 사람 한 명(다리 2·몸통·머리 상자 4개)을 진행 방향에 맞춰 돌린 면들로 만든다. 색은 인자로 받는다(`WALK_COLORS`: #ff8c1a, #e07010).
|
|
||||||
- `animate`: 움직이는 동안 `requestAnimationFrame` 으로 시간을 더하고 초당 30번 이하로 `draw` 를 부른다.
|
|
||||||
- `setWalking`: 움직임을 켜거나 끄고, 「사람 ❚❚」·「사람 ▶」 글자를 바꾼다.
|
|
||||||
|
|
||||||
**도면 칸**
|
|
||||||
- `applyDrawView`: 배율을 `#drawing` 과 `#hotspots` 의 실제 width/height 로 넣고(벡터를 다시 그린다), 위치만 translate 로 옮긴다.
|
|
||||||
- `fitDrawing`: 처음 화면에서 `drawing.fit` 상자가 왼쪽 칸에 꽉 차게 배율과 위치를 정한다.
|
|
||||||
- `onDrawWheel`: 마우스 아래의 도면 점이 그대로 머물도록 확대·축소한다(0.5~40배).
|
|
||||||
- `onDrawDown`: 도면 드래그(이동)를 시작한다.
|
|
||||||
- `onDrawMove`: 드래그 중 도면을 옮긴다.
|
|
||||||
- `onDrawUp`: 드래그를 끝낸다. 움직였으면 다음 클릭은 선택으로 치지 않는다.
|
|
||||||
|
|
||||||
**도면 ↔ 부재**
|
|
||||||
- `region`: 클릭 영역 하나(부재 id 들과 다각형들)를 만든다. 다각형이 없으면 영역도 없다.
|
|
||||||
- `pathD`: 다각형 점 배열을 SVG `path` 의 `d` 글자로 바꾼다.
|
|
||||||
- `sectionPoly`: 단면 그림 한 장에서 부재 하나의 클릭 영역을 만든다. `section_break` 그림은 생략 구간을 뺀다(벽은 위·아래 두 조각).
|
|
||||||
- `circlePoly`: 평면 그림의 원(32각형)을 만든다.
|
|
||||||
- `planPoly`: 평면 그림에서 벽(고리, 가운데가 빈다)과 터널(사각형)의 클릭 영역을 만든다.
|
|
||||||
- `viewHotspots`: 그림 한 장(`MODEL.drawing.views` 의 한 항목)의 클릭 영역 목록을 만든다. 단면·평면·중간슬래브 그림 세 가지를 나눠 처리한다.
|
|
||||||
- `obox4`: 상자(obox) 바닥 윤곽 네 꼭짓점(mm)을 돌려준다. `facesObox` 와 같은 회전을 쓴다.
|
|
||||||
- `xSpan`: 부재가 덮는 X 범위(mm)를 돌려준다. 계단·집수조·침사조·사다리·월류벽·맨홀 목·상자마다 다르다.
|
|
||||||
- `planXY`: 평면 그림의 점 하나(X, Y mm)를 도면 좌표로 바꾼다.
|
|
||||||
- `bottomPlanPoly`: 하부 슬래브 평면에서 집수조·침사조(원을 X 범위로 자른 활꼴), 월류벽, 사다리의 클릭 영역을 만든다.
|
|
||||||
- `bottomHotspots`: 바닥부 클릭 영역을 만든다. 단면 A-A 두 그림, 하부 슬래브 평면, 19B 평면의 침사구(사다리 선택)를 처리한다.
|
|
||||||
- `upperHotspots`: 상부 부재의 클릭 영역을 만든다. 단면 A-A 두 그림은 상부 슬래브·맨홀 목·분기구·환기구·위쪽 사다리를 X·z 사각형으로, 평면도 1·2 는 원판(r 4850)·맨홀 목·분기구와 환기구 바닥 윤곽을 처리한다.
|
|
||||||
- `buildHotspots`: 11개 그림의 클릭 영역을 `svg#hotspots` 에 `path` 로 그린다. 같은 그림 안에서는 뒤에 나온 영역이 위에 온다. `upperHotspots` → `viewHotspots` → `bottomHotspots` 순으로 이어 붙인다.
|
|
||||||
- `selectPart`: 부재 id 배열을 고른다. 겹치는 도면 영역을 칠하고, 3D 에서 그 부재들을 노란색으로 그리고, 「선택: 그림 이름 — 부재 이름」을 쓴다. 빈 배열이면 해제한다.
|
|
||||||
|
|
||||||
## 그리기 방식
|
|
||||||
|
|
||||||
- 투영은 정사 투영이다. 처음 화면은 등각(좌우 45도, 기울기 35도).
|
|
||||||
- 드래그 = 궤도 회전: 좌우는 Z축 둘레, 위아래는 기울기(89도에서 멈춤).
|
|
||||||
- 면 음영: 법선이 빛 방향에 가까울수록 밝다(계수 0.4~1.0).
|
|
||||||
- 화가 알고리즘: 면마다 가운데 점의 깊이를 재서 먼 면부터 칠한다. 겹침은 이 순서로 해결한다.
|
|
||||||
- 벽은 `alpha` 0.25 로 반투명, 나머지는 불투명. 원은 32각형으로 근사한다.
|
|
||||||
|
|
||||||
## 도면 연결
|
|
||||||
|
|
||||||
도면은 SVG 를 `<img>` 로 띄우고, 같은 크기의 `<svg id="hotspots">` 를 겹친다. 좌표는 mm 를 도면 단위로 바꿔 쓴다. `u` 는 도면 가로, `v` 는 도면 세로(아래로 +).
|
|
||||||
|
|
||||||
- 단면 A-A: `u = x0 + X / 1000`, `v = y0 − z / 1000`
|
|
||||||
- 평면도: `u = cx + x / 1000`, `v = cy − y / 1000`
|
|
||||||
|
|
||||||
`section` 과 `plan` 의 값은 도면 선을 재서 얻었다. 값은 `model.js` 의 `drawing` 에 있다.
|
|
||||||
|
|
||||||
## 변환 패키지 (DWG → DXF → 모델)
|
|
||||||
|
|
||||||
설계도면은 DWG 로 주고받는다. DWG 는 바이너리라 GNU LibreDWG 로 DXF(글자 파일)로 바꾸고, 과제가 쓰는 그림 상자만 남겨 추출 도구에 넣는다.
|
|
||||||
|
|
||||||
- 설치 (WSL Ubuntu, LibreDWG 0.14 소스 빌드): `PKG_CONFIG=/bin/true ./configure --prefix=$HOME/.local --disable-bindings --disable-docs && make && make install`. `dwg2dxf` 가 `~/.local/bin` 에 생긴다.
|
- 설치 (WSL Ubuntu, LibreDWG 0.14 소스 빌드): `PKG_CONFIG=/bin/true ./configure --prefix=$HOME/.local --disable-bindings --disable-docs && make && make install`. `dwg2dxf` 가 `~/.local/bin` 에 생긴다.
|
||||||
- 실행: `bash tools/make_model.sh` (입력 `private/drawing.dwg`, 출력 `private/` 와 리포 루트의 `model_extracted.js`). 웹 왼쪽 도면은 `private/drawing_gen.svg` 를 읽는다.
|
- 실행: `bash tools/make_model.sh` (입력 `private/drawing.dwg`, 출력 `model_extracted.js` 와 `private/drawing_gen.svg`).
|
||||||
- 파일 용도:
|
- 단계: `tools/dwg_to_dxf.sh`(DWG → DXF), `tools/select_views.py`(그림 상자 안만 남김), `tools/dxf_to_svg.py`(DXF → SVG), `tools/extract_from_dxf.py`(DXF → 모델).
|
||||||
- `private/drawing.dwg`: 주고받는 원본 DWG. 커밋하지 않는다.
|
|
||||||
- `private/drawing.dxf`: 추출 입력. 그림 상자 안 객체만 남긴 것.
|
|
||||||
- `private/full.dxf`: DWG 를 바꾼 전체 DXF (중간 산출물).
|
|
||||||
- `tools/views.json`: 그림 상자 4개와 뺄 레이어·블록 이름. 사람이 정한다.
|
|
||||||
- `private/drawing_gen.svg`: 웹 표시용 그림. DXF 에서 `tools/dxf_to_svg.py` 가 만든다(상자 배치는 `views.json`).
|
|
||||||
- `private/drawing.svg`: 예전 수작업 SVG 원본. 비교용으로만 쓴다.
|
|
||||||
- 각 도구: `tools/dwg_to_dxf.sh`(DWG → DXF), `tools/select_views.py`(상자 안 객체만 남김), `tools/dxf_to_svg.py`(DXF → SVG, 표준 라이브러리만), `tools/make_model.sh`(위 도구와 추출 도구를 차례로 돌림). 추출 도구는 `views.json` 을 세 번째 인수로 받아 도면 연결값(`drawing`: 단면·평면도·슬래브 평면의 좌표와 층 높이)을 도면에서 계산한다.
|
|
||||||
|
|
||||||
## 검사
|
## 검사
|
||||||
|
|
||||||
- `bash check.sh` : F01~F17 전체 검사
|
- `bash check.sh` : 전체 검사. `bash check.sh F06` : F06 까지만, 뒤 기능은 SKIP.
|
||||||
- `bash check.sh F06` : F01~F06 만 검사, 뒤 기능은 SKIP
|
- 끝 줄이 `PASS n / FAIL 0` 이면 통과.
|
||||||
- 결과 끝 줄이 `PASS n / FAIL 0` 이면 통과. 육안으로 볼 항목은 SPEC 8.2 에 있다.
|
|
||||||
|
|
||||||
## 사용 도구
|
## 사용 도구
|
||||||
|
|
||||||
- WSL2 (Ubuntu) 에서 작업. 설치일은 Ubuntu 가상 디스크를 만든 날 2026-07-09. 설치 증빙(Windows 쪽 `wsl --version`·`wsl --status`, WSL 안쪽 커널·Ubuntu·claude 버전)은 `evidence/wsl2.txt` 참고. WSL 버전 확인 (Windows 명령 프롬프트): `wsl -l -v`
|
- WSL2 (Ubuntu) 에서 작업. 설치 증빙은 `evidence/wsl2.txt`. 버전 확인(Windows 명령 프롬프트): `wsl -l -v`
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME STATE VERSION
|
NAME STATE VERSION
|
||||||
* Ubuntu Stopped 2
|
* Ubuntu Stopped 2
|
||||||
```
|
```
|
||||||
|
|
||||||
- Claude Code, 모델은 Haiku 5.5 (코드 작성·수정·커밋. 커밋 메시지마다 `Co-Authored-By: Claude Haiku 5.5` 줄)
|
- Claude Code, 모델 Haiku 5.5 (코드 작성·수정·커밋). 설계·검사 문서(`SPEC.md`·`check.sh`·`CLAUDE.md`)는 별도 Claude(Opus) 코치 세션이 썼다.
|
||||||
- 설계 문서·검사(`SPEC.md`, `FEATURES.json`, `check.sh`)와 구동 설명서(`guide/`)는 설계를 맡은 별도 Claude(Opus) 세션이 쓰고, 하이쿠가 커밋한다(커밋 첫 줄 KIT·DOC).
|
- GNU LibreDWG 0.14, Python 3 표준 라이브러리, git, Chrome 또는 Edge.
|
||||||
- GNU LibreDWG 0.14 (DWG → DXF), Python 3 표준 라이브러리, git, 브라우저(Chrome·Edge)
|
|
||||||
@@ -1,615 +1,41 @@
|
|||||||
# 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) |
|
- 이슈 #N 의 검사는 `checks/i<N>_<짧은이름>.py` 한 파일에 `check(ok, 메시지)` 로 쓴다. check.sh 가 번호 순으로 모아 돌린다(F01~F47 은 1차 검사로 check.sh 안). 이슈끼리 파일이 겹치지 않아 여러 이슈를 동시에 할 수 있다.
|
||||||
|
- 기준값은 도면에서 가져온다. 코드에서 역산하지 않는다.
|
||||||
### 4.2 중간 슬래브 (원판, r 4,850, 두께 300)
|
- 변조 시험: 도면을 바꾸면 결과도 그만큼 바뀌어야 한다(하드코딩 방지).
|
||||||
슬래브 상면 z. 하면 = 상면 − 300.
|
|
||||||
|
## 6. 공개 규칙
|
||||||
| 층 | 상면 z | 층 | 상면 z | 층 | 상면 z |
|
커밋하는 도면은 `private/drawing.dxf`·`drawing.svg`·`drawing_gen.svg` 뿐. 원본 DWG·지명·과업명·실제 좌표는 넣지 않는다.
|
||||||
|---|---|---|---|---|---|
|
|
||||||
| 1B | −7,400 | 8B | −30,500 | 15B | −53,600 |
|
## 7. 금지
|
||||||
| 2B | −10,700 | 9B | −33,800 | 16B | −56,900 |
|
화면 코드: 라이브러리·CDN·import·외부 URL. 도구: 표준 라이브러리만(LibreDWG 명령 제외).
|
||||||
| 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 이었다).
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# check.sh — drawing3d-web 합격 판정 (설계 단계에서 작성, 하이쿠는 수정 금지)
|
# check.sh — drawing3d-web 합격 판정 (설계 단계에서 작성, 하이쿠는 수정 금지)
|
||||||
# 사용: bash check.sh → 전체 기능 검사
|
# 사용: bash check.sh → 전체 기능 검사
|
||||||
# bash check.sh F06 → F01~F06 만 검사, 뒤 기능은 SKIP
|
# bash check.sh F06 → F01~F06 만 검사, 뒤 기능·이슈 검사는 SKIP
|
||||||
|
# 이슈 검사: checks/i<번호>_<이름>.py 를 번호 순으로 모아 돌린다 (F01~F47 은 1차 검사)
|
||||||
# 판정: FAIL 이 0 이면 종료 코드 0
|
# 판정: FAIL 이 0 이면 종료 코드 0
|
||||||
cd "$(dirname "$0")" || exit 2
|
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 +234,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"]
|
||||||
@@ -374,7 +372,7 @@ bad_imp = [m for m in re.findall(r"^\s*(?:import|from)\s+([A-Za-z_][\w.]*)", too
|
|||||||
("sys", "re", "math", "json", "os", "argparse", "collections", "itertools", "functools", "pathlib")]
|
("sys", "re", "math", "json", "os", "argparse", "collections", "itertools", "functools", "pathlib")]
|
||||||
report("F33", bool(tool) and not bad_nums and not bad_imp, "tools/extract_from_dxf.py 있음, 도면 좌표·정답 숫자·외부 라이브러리 없음"
|
report("F33", bool(tool) and not bad_nums and not bad_imp, "tools/extract_from_dxf.py 있음, 도면 좌표·정답 숫자·외부 라이브러리 없음"
|
||||||
+ (f" — 숫자: {bad_nums}" if bad_nums else "") + (f" — import: {bad_imp}" if bad_imp else ""))
|
+ (f" — 숫자: {bad_nums}" if bad_nums else "") + (f" — import: {bad_imp}" if bad_imp else ""))
|
||||||
TMP = tempfile.gettempdir()
|
TMP = tempfile.mkdtemp(prefix="d3w_") # 실행마다 따로 — 여러 폴더에서 동시에 검사해도 임시 파일이 섞이지 않게
|
||||||
EX, err = (run_extract(DXF, os.path.join(TMP, "d3w_ex.js")) if have_dxf else (None, "private/drawing.dxf 없음"))
|
EX, err = (run_extract(DXF, os.path.join(TMP, "d3w_ex.js")) if have_dxf else (None, "private/drawing.dxf 없음"))
|
||||||
ex_parts = EX.get("parts", []) if isinstance(EX, dict) else []
|
ex_parts = EX.get("parts", []) if isinstance(EX, dict) else []
|
||||||
ek = lambda k_: [p for p in ex_parts if isinstance(p, dict) and p.get("kind") == k_]
|
ek = lambda k_: [p for p in ex_parts if isinstance(p, dict) and p.get("kind") == k_]
|
||||||
@@ -1083,6 +1081,40 @@ 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 에 이슈 규칙(작업 목록은 이슈·그 이슈만 읽기·커밋에 #번호)")
|
||||||
|
|
||||||
|
# ---- 이슈별 검사: checks/i<번호>_<이름>.py (이슈 #N 의 완료 기준) ----
|
||||||
|
# 이슈마다 파일 하나라 여러 이슈를 동시에 해도 check.sh 는 바뀌지 않는다.
|
||||||
|
# 검사 파일은 위의 값(MODEL·html·viewer·read·near 등)을 쓰고 check(ok, 메시지) 로 판정한다.
|
||||||
|
def issue_no(p):
|
||||||
|
m = re.match(r"i(\d+)_", os.path.basename(p))
|
||||||
|
return int(m.group(1)) if m else 10 ** 6
|
||||||
|
ichecks = sorted(glob.glob("checks/i*.py"), key=lambda p: (issue_no(p), p))
|
||||||
|
for cp in ichecks:
|
||||||
|
def check(ok, msg, cid=(f"#{issue_no(cp)}" if issue_no(cp) < 10 ** 6 else os.path.basename(cp))):
|
||||||
|
tag = "SKIP" if LIMIT < len(ORDER) - 1 else ("PASS" if ok else "FAIL")
|
||||||
|
count[tag] += 1
|
||||||
|
print(f"[{tag}] {cid} {msg}")
|
||||||
|
g = dict(globals())
|
||||||
|
g["check"] = check
|
||||||
|
try:
|
||||||
|
exec(compile(read(cp) or "", cp, "exec"), g)
|
||||||
|
except (Exception, SystemExit) as e:
|
||||||
|
check(False, f"{cp} 실행 오류: {type(e).__name__}: {e}")
|
||||||
|
print(f"이슈 검사 파일 {len(ichecks)}개" + (f": {', '.join(os.path.basename(p) for p in ichecks)}" if ichecks else ""))
|
||||||
|
|
||||||
|
shutil.rmtree(TMP, ignore_errors=True)
|
||||||
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