diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 7907f79e..7d1cebfd 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -650,6 +650,15 @@ export function useModuleBasicSetting(tabNum) { } }, [isManualModuleSetup, isManualModuleLayoutSetup]) + // [MANUAL-CHIDORI-BY-FACE 2026-07-08] 千鳥(치도리) 가부는 지붕재(면)별로 다르다(plvrYn). + // 千鳥 라디오가 전역 ON이어도 커서 밑 면이 不可(plvrYn==='N')면 그 면에선 千鳥 스냅을 끄고 정렬로 배치한다. + const isChidoriAllowedForFace = (face) => { + const roofIndex = face?.roofMaterial?.index + if (roofIndex == null) return true + const plvrYn = moduleSelectionData?.roofConstructions?.find((c) => Number(c.roofIndex) === Number(roofIndex))?.construction?.plvrYn + return plvrYn !== 'N' + } + /** * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인 * 확인 후 셀을 이동시킴 @@ -679,6 +688,14 @@ export function useModuleBasicSetting(tabNum) { return } + // [MANUAL-SURFACE-GATE 2026-07-08] 자동배치와 동일하게 선택된 설치면이 없으면 배치 불가 (선택 없이는 千鳥 판정 대상도 없음) + if (moduleSetupSurface.length === 0) { + swalFire({ text: getMessage('module.place.no.surface'), icon: 'warning' }) + setIsManualModuleSetup(false) + setManualSetupMode(`manualSetup_false`) + return + } + const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체 //수동모드 모듈 설치면 선택 잠금 moduleSetupSurfaces.forEach((obj) => { @@ -843,7 +860,7 @@ export function useModuleBasicSetting(tabNum) { tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2) } - if (isChidori) { + if (isChidori && isChidoriAllowedForFace(moduleSetupSurfaces[i])) { //움직이는 모듈왼쪽 -> 가운데 if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) { tempModule.left = holdCellCenterX + intvHor / 2 @@ -886,7 +903,7 @@ export function useModuleBasicSetting(tabNum) { tempModule.top = holdCellCenterY - toFixedWithoutRounding(height / 2, 2) } - if (isChidori) { + if (isChidori && isChidoriAllowedForFace(moduleSetupSurfaces[i])) { //위쪽 -> 가운데 if (Math.abs(smallTop - holdCellCenterY) < snapDistance) { tempModule.top = holdCellCenterY + intvHor / 2 @@ -986,6 +1003,19 @@ export function useModuleBasicSetting(tabNum) { // } } + // [MANUAL-CHIDORI-BY-FACE 2026-07-08] 千鳥는 「가능면 + 한다」일 때만 허용. 그 외(불가면 전부 / 가능면이어도 「하지 않는다」)는 手動 드래그도 정렬 강제. + // 사용자가 반칸 어긋난 위치에 놓아도 기존 모듈 열/행 격자(pitch)에 흡착시켜 치도리를 제거한다. + if (!(isChidori && isChidoriAllowedForFace(moduleSetupSurfaces[i])) && manualDrawModules && manualDrawModules.length > 0) { + const gridRef = manualDrawModules[0] + if (flowDirection === 'south' || flowDirection === 'north') { + const pitchX = width + intvHor + if (pitchX > 0) tempModule.left = gridRef.left + Math.round((tempModule.left - gridRef.left) / pitchX) * pitchX + } else { + const pitchY = height + intvVer + if (pitchY > 0) tempModule.top = gridRef.top + Math.round((tempModule.top - gridRef.top) / pitchY) * pitchY + } + } + tempModule.setCoords() canvas?.renderAll() inside = true @@ -1194,6 +1224,14 @@ export function useModuleBasicSetting(tabNum) { return } + // [MANUAL-SURFACE-GATE 2026-07-08] 자동배치와 동일하게 선택된 설치면이 없으면 배치 불가 + if (moduleSetupSurface.length === 0) { + swalFire({ text: getMessage('module.place.no.surface'), icon: 'warning' }) + setIsManualModuleLayoutSetup(false) + setManualSetupMode(`manualLayoutSetup_false`) + return + } + const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체 //수동모드 모듈 설치면 선택 잠금 moduleSetupSurfaces.forEach((obj) => { @@ -1396,7 +1434,7 @@ export function useModuleBasicSetting(tabNum) { tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2) } - if (isChidori) { + if (isChidori && isChidoriAllowedForFace(moduleSetupSurfaces[i])) { //움직이는 모듈왼쪽 -> 가운데 if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) { tempModule.left = holdCellCenterX + intvHor / 2 @@ -1439,7 +1477,7 @@ export function useModuleBasicSetting(tabNum) { tempModule.top = holdCellCenterY - toFixedWithoutRounding(width / 2, 2) } - if (isChidori) { + if (isChidori && isChidoriAllowedForFace(moduleSetupSurfaces[i])) { //위쪽 -> 가운데 if (Math.abs(smallTop - holdCellCenterY) < snapDistance) { tempModule.top = holdCellCenterY + intvHor / 2 @@ -1587,6 +1625,9 @@ export function useModuleBasicSetting(tabNum) { if (tempModule) { saveSnapshot() + // [MANUAL-CHIDORI-BY-FACE 2026-07-08] 지정배치 그리드 대상 면(trestlePolygon)이 千鳥 不可(plvrYn==='N')면 千鳥 오프셋 없이 정렬 배치. + // 아래 그리드 생성부의 isChidori 는 이 면 기준 값을 따른다(전역 라디오가 ON이어도 不可면 정렬). + const isChidori = moduleSetupOption.isChidori && isChidoriAllowedForFace(trestlePolygon) let startX, startY let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표 let installedHeightModuleCount = 0