```
docs(outerline): 외벽선 확정(handleFix) 흐름 및 PDF 자동 확정 가이드 문서 추가 ``` - 외벽선 확정 (`handleFix`) 로직의 상세 흐름과 확정 후 캔버스/Recoil 불변 조건을 문서화합니다. - PDF 도면 분석을 통한 외벽선 자동 확정 기능 구현 시 참고할 설계 가이드와 체크리스트를 포함합니다. - `handleFix` 본체와 `useEffect`, `ccwCheck` 등 관련 로직의 역할 및 주의사항을 명확히 합니다. ```
This commit is contained in:
parent
dbaebf9e2b
commit
2e7c8769aa
284
docs/외벽선 확정(handleFix) 흐름.md
Normal file
284
docs/외벽선 확정(handleFix) 흐름.md
Normal file
@ -0,0 +1,284 @@
|
||||
# 외벽선 확정 (`handleFix`) 흐름
|
||||
|
||||
> Q.CAST Floor Plan — **지붕덮개 → 외벽선 그리기** 모달의 **「외벽선 확정」** 버튼이 호출하는 로직 정리.
|
||||
> PDF/Gemini 도면 분석 후 캔버스에 외벽선을 그린 뒤, 옵션에 따라 이 확정 단계를 자동 실행할 때 참고한다.
|
||||
|
||||
코드 단일 진실 공급원:
|
||||
|
||||
| 역할 | 경로 |
|
||||
|---|---|
|
||||
| UI (버튼) | `src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx` |
|
||||
| 핵심 로직 | `src/hooks/roofcover/useOuterLineWall.js` — `handleFix`, `ccwCheck`, `points` `useEffect` |
|
||||
| 상태 | `src/store/outerLineAtom.js` — `outerLinePointsState`, `outerLineFixState` |
|
||||
| 라인 생성 | `src/hooks/useLine.js` — `addLine` (QLine + `attributes.planeSize`) |
|
||||
| PDF 임포트 대응 구현 | `src/hooks/pdf-import/usePdfImport.js` — `applyOuterline` |
|
||||
| 확정 후 다음 단계 | `src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx` |
|
||||
|
||||
---
|
||||
|
||||
## 0. 한눈에 보기
|
||||
|
||||
```
|
||||
[외벽선 그리기 메뉴] → WallLineSetting 모달
|
||||
↓ (클릭/Enter)
|
||||
SweetAlert 확인 (modal.cover.outline.fix.confirm)
|
||||
↓ confirm
|
||||
handleFix() — 닫힘 정점 추가 + isFix.current = true
|
||||
↓ (points useEffect)
|
||||
outerLine QLine 재생성 + outerLinePoint circle
|
||||
↓ isFix.current === true
|
||||
outerLineFix 플래그 ON → 모달 닫기 → ccwCheck → 지붕형상 설정 모달 오픈
|
||||
```
|
||||
|
||||
**확정 직후 캔버스/Recoil이 만족해야 하는 불변 조건** (PDF 자동 확정도 동일):
|
||||
|
||||
1. `canvas` 객체 중 `name === 'outerLine'` 인 QLine ≥ 2 (닫힌 다각형)
|
||||
2. `name === 'outerLinePoint'` 인 fabric.Circle (정점, `visible: false`)
|
||||
3. `canvas.outerLineFix === true` **및** Recoil `outerLineFixState === true`
|
||||
4. 외벽선 정점 순서는 **반시계(CCW)** — `ccwCheck` 또는 동등 전처리
|
||||
5. QLine 은 반드시 `useLine.addLine` 경유 — `attributes.planeSize`, `fontSize` 없으면 지붕형상·처마/케라바·저장 복원 깨짐
|
||||
6. 좌표에 **축소 scale 을 곱하지 않음** — `QLine.length × 10 = 표시 mm` 불변식 유지
|
||||
|
||||
---
|
||||
|
||||
## 1. UI 진입점
|
||||
|
||||
### 1-1. 모달·버튼
|
||||
|
||||
- 컴포넌트: `WallLineSetting` (`WithDraggable`, class `r`)
|
||||
- 메뉴 진입: `useMenu` → `currentMenu === EXTERIOR_WALL_LINE` → `addPopup(..., <WallLineSetting />)`
|
||||
- 버튼 DOM: `div.grid-btn-wrap > button.btn-frame.modal.act`
|
||||
- i18n 키: `modal.cover.outline.fix` (ko: 「외벽선 확정」)
|
||||
|
||||
### 1-2. 클릭 핸들러
|
||||
|
||||
확정 버튼은 **즉시** `handleFix` 를 부르지 않고, 확인 다이얼로그를 거친다.
|
||||
|
||||
```jsx
|
||||
// WallLineSetting.jsx
|
||||
swalFire({
|
||||
type: 'confirm',
|
||||
text: getMessage('modal.cover.outline.fix.confirm'),
|
||||
confirmFn: handleFix,
|
||||
})
|
||||
```
|
||||
|
||||
동일 확인은 `useOuterLineWall.enterCheck` (Enter 키) 에서도 사용한다.
|
||||
|
||||
> **자동 확정 구현 시**: 사용자 확인이 필요 없으면 `swalFire` 를 생략하고 `handleFix` 또는 아래 §5 의 「확정 후처리」만 직접 호출하면 된다.
|
||||
|
||||
---
|
||||
|
||||
## 2. `handleFix` 본체
|
||||
|
||||
파일: `useOuterLineWall.js`
|
||||
|
||||
### 2-1. 사전 조건·검증
|
||||
|
||||
| 단계 | 조건 | 실패 시 |
|
||||
|---|---|---|
|
||||
| 1 | `points.length >= 3` | `return` (무발화) |
|
||||
| 2 | 모든 꼭짓점이 첫 점 기준 직각(각도 % 90 === 0) | `return` — 축정렬 직사각형만으로 닫힌 경우 확정 불가 |
|
||||
|
||||
검증 통과 후:
|
||||
|
||||
```javascript
|
||||
setPoints((prev) => [...prev, { x: prev[0].x, y: prev[0].y }]) // 폐곡선: 마지막 = 첫 점
|
||||
isFix.current = true
|
||||
```
|
||||
|
||||
- `points` 는 Recoil `outerLinePointsState`
|
||||
- `isFix.current` 는 ref — **다음 `points` useEffect 한 번**에서만 소비된다 (자동으로 false 로 리셋되지 않음; 훅 unmount 전까지 true 유지)
|
||||
|
||||
### 2-2. `handleFix` 가 직접 하지 **않는** 일
|
||||
|
||||
다음은 모두 **`useEffect([points])`** 와 **`ccwCheck`** 에 위임된다.
|
||||
|
||||
- Fabric `outerLine` / `outerLinePoint` 생성
|
||||
- `outerLineFixState` / `canvas.outerLineFix` 설정
|
||||
- 마우스·키보드 이벤트 해제
|
||||
- `WallLineSetting` 모달 닫기
|
||||
- `RoofShapeSetting` 모달 열기
|
||||
|
||||
---
|
||||
|
||||
## 3. `points` 변경 시 useEffect (확정의 실질 후처리)
|
||||
|
||||
`points` 가 바뀔 때마다 실행된다. 확정 **이전**에도 그리기 미리보기 역할을 한다.
|
||||
|
||||
### 3-1. 공통 (매 `points` 변경)
|
||||
|
||||
1. 기존 `outerLine`, `helpGuideLine` 제거 (`removeLine`)
|
||||
2. `startPoint` 제거
|
||||
3. `points.length === 0` → `setOuterLineFix(true)` 후 return (초기화 경로)
|
||||
4. `points.length === 1` → 시작점 빨간 circle (`startPoint`)
|
||||
5. `points.length >= 2`:
|
||||
- `setOuterLineFix(false)` — **그리는 중에는 미확정**
|
||||
- `outerLinePoint` circle 생성 (`visible: false`)
|
||||
- 인접 정점 쌍마다 `drawLine` → `addLine` (`name: 'outerLine'`, `strokeWidth: 3`, …)
|
||||
- 3점 이상이면 닫힘 가이드 (`helpGuideLine`, 회색 점선)
|
||||
|
||||
### 3-2. 확정 분기 (`isFix.current === true`)
|
||||
|
||||
```javascript
|
||||
if (isFix.current) {
|
||||
removeAllMouseEventListeners()
|
||||
removeAllDocumentEventListeners()
|
||||
canvas?.renderAll()
|
||||
setOuterLineFix(true)
|
||||
canvas.outerLineFix = true
|
||||
closePopup(id) // WallLineSetting 닫기
|
||||
ccwCheck() // §4
|
||||
addPopup(propertiesId, 1, <RoofShapeSetting id={propertiesId} />)
|
||||
}
|
||||
```
|
||||
|
||||
**수동 플로우 확정 후 사용자가 보는 화면**: 외벽선 그리기 모달 종료 → **지붕형상 설정** 모달 자동 오픈.
|
||||
|
||||
---
|
||||
|
||||
## 4. `ccwCheck` — 정점 순서 보정
|
||||
|
||||
확정 직후 호출. 외벽선이 **시계 방향**이면 **반시계**로 뒤집는다.
|
||||
|
||||
1. `canvas.getObjects()` 에서 `name === 'outerLine'` 필터
|
||||
2. `outerLines.length < 2` → `wall.line.not.found` 알림 후 return
|
||||
3. Shoelace signed area 로 CCW 판별 (`signedArea > 0` → CW)
|
||||
4. CW 이면 각 라인을 `[x2,y2,x1,y1]` 로 **역방향 재생성** (`addLine` + 기존 line `remove`)
|
||||
|
||||
PDF 임포트(`usePdfImport.ensureCounterClockwise`)는 **정점 배열** 단계에서 동일 목적을 수행한다. 캔버스에 이미 그려진 뒤라면 `ccwCheck` 와 결과가 같아야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 5. PDF 자동 확정 — `handleFix` 와의 대응
|
||||
|
||||
현재 `usePdfImport.applyOuterline` 은 **수동 확정 직후 캔버스 상태**까지는 맞추지만, **`handleFix` 전체를 호출하지는 않는다**.
|
||||
|
||||
| 수동 `handleFix` 후 | `applyOuterline` 현재 | 자동 확정 옵션 추가 시 |
|
||||
|---|---|---|
|
||||
| SweetAlert 확인 | PDF 덮어쓰기 확인만 | 옵션으로 생략/유지 선택 |
|
||||
| `points` 에 닫힘점 추가 | `closedPoints = [...pts, pts[0]]` + `setOuterLinePoints` | 동일 |
|
||||
| `useEffect` 로 QLine 재그리기 | `drawOuterLines` (`addLine` 동일 옵션) | 동일 |
|
||||
| `outerLinePoint` circle | `drawOuterLinePoints` | 동일 |
|
||||
| `canvas.outerLineFix` + `outerLineFixState` | ✅ 설정함 | ✅ 필수 |
|
||||
| `ccwCheck` | `ensureCounterClockwise` (정점 단계) | 라인 그린 뒤 CW 이면 `ccwCheck` 또는 정점 보정 |
|
||||
| `closePopup(WallLineSetting)` | 해당 모달 없음 | 불필요 |
|
||||
| `RoofShapeSetting` 모달 오픈 | ❌ **안 함** | 옵션 `autoFix: true` 시 `addPopup` 으로 직행 |
|
||||
| `fitViewport` + `canvasZoomState` | ✅ PDF 전용 | 수동 확정에는 없음 — PDF 유지 |
|
||||
| 마우스 리스너 제거 | 그리기 모드 미사용 | 불필요 |
|
||||
|
||||
`PlacementShapeSetting` PDF 분기는 메뉴만 `ROOF_SHAPE_SETTINGS` 로 보내고, **지붕형상 모달 자동 오픈은 하지 않는다** — 자동 확정 옵션은 별도 구현이 필요하다.
|
||||
|
||||
참고: `CLAUDE.md` PDF 임포트 절 — 「수동 외벽선 확정(`useOuterLineWall.handleFix`) 직후와 동일한 상태」.
|
||||
|
||||
---
|
||||
|
||||
## 6. 자동 확정 구현 가이드 (권장)
|
||||
|
||||
### 6-1. `handleFix` 를 그대로 호출하지 **말** 것
|
||||
|
||||
`handleFix` 는 `useOuterLineWall` 훅 내부에 묶여 있고, 전제 조건이 다르다.
|
||||
|
||||
- Recoil `outerLinePointsState` 가 **미닫힌** 정점 배열이어야 함 (마지막 ≠ 첫 점)
|
||||
- `WallLineSetting` 모달이 마ounted 되어 `propertiesId` 로 `RoofShapeSetting` 을 열어야 함
|
||||
- 직각-only 다각형이면 조용히 실패
|
||||
|
||||
PDF 경로는 이미 **닫힌 폴리곤 + 라인 객체**가 있으므로, 훅 밖에서 후처리하는 편이 안전하다.
|
||||
|
||||
### 6-2. 권장: 공통 「확정 후처리」 추출 (미래 리팩터)
|
||||
|
||||
아래를 `finalizeOuterLine({ canvas, points, options })` 같은 유틸/훅으로 묶으면 수동·PDF·자동 옵션이 한 경로를 탄다.
|
||||
|
||||
```text
|
||||
입력: closedPoints (첫점=끝점 포함 가능, drawOuterLines 기준 인접쌍)
|
||||
1. clearExisting outerLine / outerLinePoint (필요 시)
|
||||
2. drawOuterLines(closedPoints) // useLine.addLine
|
||||
3. drawOuterLinePoints(closedPoints)
|
||||
4. CCW 보정 (정점 또는 ccwCheck)
|
||||
5. canvas.outerLineFix = true; setOuterLineFix(true)
|
||||
6. canvas.renderAll()
|
||||
7. [옵션] fitViewport
|
||||
8. [옵션] addPopup(RoofShapeSetting)
|
||||
```
|
||||
|
||||
### 6-3. 최소 변경 (현재 코드베이스)
|
||||
|
||||
`applyOuterline` 의 `proceed()` 끝에 옵션 분기만 추가:
|
||||
|
||||
```javascript
|
||||
// options.autoOpenRoofShape === true 일 때
|
||||
const roofShapeId = uuidv4()
|
||||
addPopup(roofShapeId, 1, <RoofShapeSetting id={roofShapeId} pos={{ x: 50, y: 230 }} />)
|
||||
setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS) // 메뉴 탭 동기화
|
||||
```
|
||||
|
||||
`usePopup` / `useMenu` 의존은 `usePdfImport` 또는 호출측(`PdfImportModal`, `PlacementShapeSetting`)에서 주입.
|
||||
|
||||
### 6-4. 검증 체크리스트 (자동 확정 후)
|
||||
|
||||
- [ ] `canvas.getObjects().filter(o => o.name === 'outerLine').length >= 3`
|
||||
- [ ] 각 QLine 에 `attributes.planeSize` 존재
|
||||
- [ ] `canvas.outerLineFix === true`
|
||||
- [ ] `useRoofShapeSetting` 마운트 시 `wall.line.not.found` 미발생
|
||||
- [ ] 치수 라벨(mm)이 실측과 일치 (scale 미적용)
|
||||
- [ ] 외벽선 그리기 메뉴 재진입 시 `setOuterLineFix(false)` 로 **재편집 모드**로 떨어지지 않도록 — PDF 플로우는 `ROOF_SHAPE_SETTINGS` 메뉴 유지 (`PlacementShapeSetting` 참고)
|
||||
|
||||
---
|
||||
|
||||
## 7. 관련 Recoil / Canvas 플래그
|
||||
|
||||
| 키 | 타입 | 확정 전 (그리기 중) | 확정 후 |
|
||||
|---|---|---|---|
|
||||
| `outerLinePointsState` | `{x,y}[]` | 미닫힘 정점들 | 닫힘(마지막=첫점) 또는 동일 좌표의 closed ring |
|
||||
| `outerLineFixState` | `boolean` | `false` (2점 이상) | `true` |
|
||||
| `canvas.outerLineFix` | `boolean` (canvas 커스텀) | `false` | `true` — 직렬화/가드에 사용 (`common.js` `'outerLineFix'`) |
|
||||
|
||||
`canvasAtom` 의 selector 는 `outerLineFix === false` 이거나 points 비어 있으면 특정 UI를 잠근다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 확정 이후 파이프라인
|
||||
|
||||
```
|
||||
handleFix 완료
|
||||
→ RoofShapeSetting (useRoofShapeSetting)
|
||||
→ WALL/ROOF 폴리곤 생성, pitchText, 처마/케라바 타입
|
||||
→ (이후) 보조선, 처마·케라바 편집, 배치면 …
|
||||
```
|
||||
|
||||
`useRoofShapeSetting` 진입 가드:
|
||||
|
||||
```javascript
|
||||
if (!canvas.outerLineFix || outerLines.length === 0) {
|
||||
swalFire({ text: getMessage('wall.line.not.found') })
|
||||
closePopup(id)
|
||||
}
|
||||
```
|
||||
|
||||
자동 확정 없이 `outerLineFix` 만 켜 두면 메뉴는 지붕형상으로 가도 **모달을 열어 주지 않으면** 사용자는 수동으로 메뉴를 눌러야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 유사 이름 주의 (`handleFix` 동명이인)
|
||||
|
||||
| 훅 | 용도 |
|
||||
|---|---|
|
||||
| **`useOuterLineWall.handleFix`** | **본 문서 대상** — 외벽선 그리기 확정 |
|
||||
| `usePropertiesSetting.handleFix` | 외벽선 **속성**(처마/케라바) 확정 — 다른 모달 |
|
||||
| `usePlacementShapeDrawing.handleFix` | 배치면 그리기 확정 |
|
||||
| `useRoofLinePropertySetting.handleFix` | 지붕선 속성 확정 |
|
||||
| `useAuxiliaryDrawing.handleFix` | 보조선 확정 |
|
||||
|
||||
DOM class `btn-frame modal act` + 「외벽선 확定/외벽선 확정」은 **`WallLineSetting`** 과 **`PlacementShapeDrawing`** 등에서 공유 메시지 키를 쓸 수 있다. DOM path `modal-pop-wrap r` + `useOuterLineWall` 이면 **본 문서** 대상이다.
|
||||
|
||||
---
|
||||
|
||||
## 10. 참고 문서·코드
|
||||
|
||||
- `docs/PDF Gemini 평면도 분석 기능 구현 계획.md` — PDF → 캔버스 반영 전체
|
||||
- `docs/Canvas Status.md` — §2.1 외벽선 그리기, §2.2 지붕형상 설정
|
||||
- `CLAUDE.md` — PDF 임포트 불변식 (scale, `addLine`, `outerLineFix`)
|
||||
|
||||
---
|
||||
|
||||
*작성: 2026-06-19 — PDF 자동 외벽선 확정 기능 설계용*
|
||||
Loading…
x
Reference in New Issue
Block a user