diff --git a/CLAUDE.md b/CLAUDE.md index d7cc201b..62323ed8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,7 +56,7 @@ A modal under `components/floor-plan/modal/` pairs with a hook under `h **Plan persistence** (serialize/restore) is in `src/lib/canvas.js` and `src/util/canvas-util.js`. Recent commits (`a5e794b3`, `2aef32e0`) added a three-layer guard (entry / cache / save) and `SizeSetting` input guards against 0-area apertures/shadows/dormers — when touching plan load/save or size inputs, do not weaken or remove these guards without understanding the failure mode (`InvalidStateError` from corrupted plans). -**PDF 도면 임포트** — 배치면 초기설정 모달의 PDF 입력 모드 → `src/app/api/gemini/floor-plan/route.js`(Gemini 분석, 세션 인증 + 사용자별 분당 5회 레이트리밋) → `src/hooks/pdf-import/usePdfImport.js`. 임포트는 수동 외벽선 확정(`useOuterLineWall.handleFix`) 직후와 동일한 상태를 만들어야 한다: `useLine.addLine` 경유 outerLine 생성(attributes/planeSize/fontSize), `outerLinePoint` 정점 circle, `canvas.outerLineFix = true` + `outerLineFixState`, 이후 지붕형상 설정 메뉴 직행. **좌표에 축소 scale 을 곱하지 말 것** — 'QLine 길이×10 = 표시 mm' 불변식이 깨진다. 화면맞춤은 `viewportTransform` 줌 + `canvasZoomState` 동기화로만 처리한다. +**PDF 도면 임포트** — 배치면 초기설정 모달의 PDF 입력 모드 → `src/app/api/gemini/floor-plan/route.js`(Gemini 분석, 세션 인증 + 사용자별 분당 5회 레이트리밋) → `src/hooks/pdf-import/usePdfImport.js`. 임포트는 **외벽선 자동 확정(`autoOuterLineFixState`, 기본 ON) 옵션**에 따라 분기한다(`PlacementShapeSetting.handleSaveBtn` → `applyOuterline(mmPoints, { autoFix })`). **ON**: 수동 외벽선 확정(`useOuterLineWall.handleFix`) 직후와 동일한 상태를 만든다 — `useLine.addLine` 경유 outerLine 생성(attributes/planeSize/fontSize), `outerLinePoint` 정점 circle, `canvas.outerLineFix = true` + `outerLineFixState`. **OFF**: 외벽선만 그려둔 미확정(`outerLineFix=false`) 상태로 둔다. **메뉴/모달 전환**: `handleSaveBtn` 은 `setSelectedMenu('outline')` + `setCurrentMenu(ON→ROOF_SHAPE_SETTINGS / OFF→EXTERIOR_WALL_LINE)` 만 하면 된다 — `MenuDepth01` 이 `currentMenu` 변화를 감지해 `handleMenu` 로 해당 모달(RoofShapeSetting / WallLineSetting)을 자동으로 띄운다. **직접 `addPopup` 하지 말 것**(MenuDepth01 의 `closeAll` 과 충돌). ⚠️ `basicSettingSave({ skipSideEffects:true })` 는 메뉴를 건드리지 않아야 한다 — `setMenuByRoofSize` 가 `if (!skipSideEffects)` 밖에 있으면 async 저장(`await post`) 직후 `currentMenu` 를 `EXTERIOR_WALL_LINE` 로 덮어써, ON 인데도 WallLineSetting 으로 되돌아가 `useOuterLineWall` 이 `outerLineFix` 를 풀어버린다(2026-06-21 버그). **좌표에 축소 scale 을 곱하지 말 것** — 'QLine 길이×10 = 표시 mm' 불변식이 깨진다. 화면맞춤은 `viewportTransform` 줌 + `canvasZoomState` 동기화로만 처리한다. ### State — Recoil, split by domain diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index ea009012..13b2b15d 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -242,7 +242,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla return false } - const applied = await applyOuterline(payload.outerline, { unit: payload.unit || 'mm' }) + const applied = await applyOuterline(payload.outerline, { unit: payload.unit || 'mm', autoFix: autoOuterLineFix }) if (!applied) { swalFire({ text: getMessage('pdf.import.error.apply'), type: 'alert' }) return false @@ -466,10 +466,16 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla // roofSizeSet에 따라 메뉴 설정 if (inputMode === INPUT_MODE.PDF) { - // PDF 임포트는 외곽선이 이미 확정(outerLineFix) 상태 — 외벽선 그리기 메뉴로 보내면 - // useOuterLineWall 이 라인을 재드로잉 모드로 강등시키므로 지붕형상 설정으로 직행한다. setSelectedMenu('outline') - setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS) + if (autoOuterLineFix) { + // 자동 확정 ON: 외곽선이 이미 확정(outerLineFix=true)된 상태이므로 지붕형상 설정으로 보낸다. + // currentMenu 변경 시 MenuDepth01 이 handleMenu 로 RoofShapeSetting 모달을 자동으로 띄운다. + setCurrentMenu(MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS) + } else { + // 자동 확정 OFF: 외벽선은 미확정(outerLineFix=false) 상태로 그려둔다 — 외벽선 그리기 단계로 보내면 + // MenuDepth01 이 WallLineSetting 을 띄워, 사용자가 검토 후 직접 [외벽선 확정] 할 수 있다. points 는 유지된다. + setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + } } else if (currentRoof?.roofSizeSet === '2') { setSelectedMenu('surface') setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 49c7814a..39abb4d5 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -571,11 +571,11 @@ export function useCanvasSetting(executeEffect = true) { roofSizeSet: String(params.roofSizeSet), }) - /** 메뉴 설정 */ - //setMenuByRoofSize(params.roofSizeSet) + /** 메뉴 설정 — skipSideEffects 호출측(PlacementShapeSetting.handleSaveBtn)은 메뉴를 직접 제어하므로 제외한다. + * if 밖에서 항상 호출하던 과거 코드는 async 저장 직후(await post 이후) 메뉴를 EXTERIOR_WALL_LINE 로 덮어써, + * PDF 자동 확정 시 외벽선 그리기 팝업으로 되돌아가 확정이 풀리는 버그를 유발했다. */ + setMenuByRoofSize(params.roofSizeSet) } - //메뉴를 찾아가지 못하는 오류 발생으로 변경 - setMenuByRoofSize(params.roofSizeSet) /** 배치면초기설정 조회 */ fetchBasicSettings(params.planNo, 'basicSettingSave') diff --git a/src/hooks/pdf-import/usePdfImport.js b/src/hooks/pdf-import/usePdfImport.js index 57dab43e..87f536b9 100644 --- a/src/hooks/pdf-import/usePdfImport.js +++ b/src/hooks/pdf-import/usePdfImport.js @@ -189,7 +189,7 @@ export function usePdfImport() { return false } - const { unit = 'mm' } = options + const { unit = 'mm', autoFix = true } = options if (unit !== 'mm') { logger.warn('[usePdfImport] 지원하지 않는 단위:', unit) swalFire({ text: getMessage('pdf.import.error.unsupported.unit'), type: 'alert' }) @@ -227,11 +227,13 @@ export function usePdfImport() { drawOuterLines(closedPoints) drawOuterLinePoints(closedPoints) - // 수동 확정(useOuterLineWall.handleFix) 직후와 동일한 상태로 만든다 — 이 플래그가 없으면 - // 지붕형상 설정(useRoofShapeSetting/useRoofShapePassivitySetting) 진입이 'wall.line.not.found' 로 차단된다. - // WALL/ROOF 폴리곤은 수동 플로우와 동일하게 지붕형상 설정 적용 시점에 생성된다. - canvas.outerLineFix = true - setOuterLineFix(true) + // 외벽선 자동 확정(autoFix) 옵션에 따라 확정 여부를 분기한다. + // - ON: 수동 확정(useOuterLineWall.handleFix) 직후와 동일하게 outerLineFix 를 켠다. 이 플래그가 없으면 + // 지붕형상 설정(useRoofShapeSetting/useRoofShapePassivitySetting) 진입이 'wall.line.not.found' 로 차단된다. + // WALL/ROOF 폴리곤은 수동 플로우와 동일하게 지붕형상 설정 적용 시점에 생성된다. + // - OFF: 외벽선만 그려둔 미확정 상태로 두어, 사용자가 외벽선 그리기 단계에서 직접 확정하도록 한다. + canvas.outerLineFix = autoFix + setOuterLineFix(autoFix) fitViewport(closedPoints) canvas.renderAll()