[2296-2] 모듈 이동 시 モジュール間離隔(간격) 미검증 수정 + 입력창 Del키 막힘(preventDefault) 수정

This commit is contained in:
ysCha 2026-07-01 09:34:17 +09:00
parent d562d02413
commit dfa90812f4
2 changed files with 60 additions and 2 deletions

View File

@ -113,6 +113,9 @@ export function useModule() {
const moduleMove = (length, direction) => { const moduleMove = (length, direction) => {
const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴 const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴
// [MOVE-CLEARANCE 2026-07-01] 경고 후 선택 해제 상태에서 재시도 시 selectedObj[0] undefined 크래시 방지
// (형제 함수 moduleMultiMove 와 동일한 선택 없음 가드)
if (selectedObj.length === 0) return
const selectedIds = selectedObj.map((obj) => obj.id) // selectedObj의 ID 추출 const selectedIds = selectedObj.map((obj) => obj.id) // selectedObj의 ID 추출
if (selectedObj[0].circuit) { if (selectedObj[0].circuit) {
swalFire({ swalFire({
@ -144,7 +147,9 @@ export function useModule() {
canvas.renderAll() canvas.renderAll()
if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface)) { // [MOVE-CLEARANCE 2026-07-01] 이동 시 モジュール間離隔 검증 추가 (isTooCloseToOtherModules)
const _tooClose = isTooCloseToOtherModules(module, isSetupModules, setupSurface)
if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface) || _tooClose) {
isWarning = true isWarning = true
module.set({ fill: 'red' }) module.set({ fill: 'red' })
} }
@ -222,7 +227,13 @@ export function useModule() {
module.setCoords() module.setCoords()
canvas.renderAll() canvas.renderAll()
if (isOverlapOtherModules(module, otherModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) { // [MOVE-CLEARANCE 2026-07-01] 행/열 이동 시 モジュール間離隔 검증 추가
if (
isOverlapOtherModules(module, otherModules) ||
isOverlapObjects(module, objects) ||
isOutsideSurface(module, moduleSetupSurface) ||
isTooCloseToOtherModules(module, otherModules, moduleSetupSurface)
) {
isWarning = true isWarning = true
module.set({ fill: 'red' }) module.set({ fill: 'red' })
} }
@ -1052,6 +1063,47 @@ export function useModule() {
) )
} }
// [MOVE-CLEARANCE 2026-07-01] 이동 시 モジュール間離隔(縦 moduleIntvlVer / 横 moduleIntvlHor) 미검증 보완.
// isOverlapOtherModules 는 실제 겹침만 보므로, 이동으로 모듈을 인접 모듈에 30/3mm 미만으로 붙여도 통과한다.
// 같은 열(X-range 겹침)→세로 간격>=Ver, 같은 행(Y-range 겹침)→가로 간격>=Hor 요구.
//
// ⚠ 간격은 반드시 stroke 를 뺀 실제 폴리곤 정점(getCurrentPoints)으로 잰다.
// getBoundingRect 는 strokeWidth 를 포함해 모듈이 실제보다 커 보이고(1606→1609mm),
// 정상 격자(정확히 30/3mm)도 측정 간격이 축소돼 위반 오판 → 모든 방향 이동 차단(회귀) 발생.
const isTooCloseToOtherModules = (module, otherModules, surface) => {
if (!otherModules || otherModules.length === 0) return false
const gapVer = Number(surface?.trestleDetail?.moduleIntvlVer ?? 0) / 10 // mm → canvas unit
const gapHor = Number(surface?.trestleDetail?.moduleIntvlHor ?? 0) / 10
if (gapVer <= 0 && gapHor <= 0) return false
const EPS = 0.05 // 0.5mm — 좌표 반올림 잔차(±0.02) 흡수, 의미있는 위반은 통과 못함
const bboxOf = (o) => {
const pts = o.getCurrentPoints ? o.getCurrentPoints() : null
if (pts && pts.length) {
const xs = pts.map((p) => p.x)
const ys = pts.map((p) => p.y)
return { l: Math.min(...xs), r: Math.max(...xs), t: Math.min(...ys), b: Math.max(...ys) }
}
const r = o.getBoundingRect(true, true)
return { l: r.left, r: r.left + r.width, t: r.top, b: r.top + r.height }
}
const mb = bboxOf(module)
return otherModules.some((o) => {
if (o.id === module.id) return false
const nb = bboxOf(o)
const overlapX = mb.l < nb.r - EPS && nb.l < mb.r - EPS
const overlapY = mb.t < nb.b - EPS && nb.t < mb.b - EPS
if (overlapX) {
const vGap = mb.t >= nb.b ? mb.t - nb.b : nb.t >= mb.b ? nb.t - mb.b : 0
if (vGap < gapVer - EPS) return true
}
if (overlapY) {
const hGap = mb.l >= nb.r ? mb.l - nb.r : nb.l >= mb.r ? nb.l - mb.r : 0
if (hGap < gapHor - EPS) return true
}
return false
})
}
const isOverlapObjects = (module, objects) => { const isOverlapObjects = (module, objects) => {
return !checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects) return !checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)
} }

View File

@ -372,6 +372,12 @@ export function useCanvasEvent() {
* https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values * https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
*/ */
const handleKeyDown = (e) => { const handleKeyDown = (e) => {
// [INPUT-GUARD 2026-07-01] 입력창/텍스트에어리어 포커스 중에는 캔버스 단축키 처리를 건너뛴다.
// Delete/Backspace 케이스가 switch 에 매칭돼 아래 preventDefault 까지 도달, 필드 내 문자삭제를 막던 버그.
// (QContextMenu wrappedKeyup 과 동일한 편집요소 가드)
const el = document.activeElement
if (el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable)) return
const key = e.key const key = e.key
switch (key) { switch (key) {