모듈 복사, 제거, 추가 후 선택 안되는 현상 수정

This commit is contained in:
hyojun.choi 2025-12-29 14:51:55 +09:00
parent 953ce7535d
commit bbd3d1ec4b
2 changed files with 126 additions and 34 deletions

View File

@ -845,37 +845,33 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = point
// viewport transform만 역변환 (캔버스 줌/팬 보정)
// 결과는 WORLD 좌표 (캔버스 좌표계)
let canvasPoint = point
if (this.canvas) {
const vpt = this.canvas.viewportTransform
if (vpt) {
// viewport transform 역변환
const inverted = fabric.util.invertTransform(vpt)
localPoint = fabric.util.transformPoint(point, inverted)
canvasPoint = fabric.util.transformPoint(point, inverted)
}
}
// 오브젝트의 transform matrix를 고려한 좌표 변환
const matrix = this.calcTransformMatrix()
const invertedMatrix = fabric.util.invertTransform(matrix)
const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix)
// pathOffset을 고려한 최종 좌표 계산
const pathOffset = this.get('pathOffset')
const finalPoint = {
x: Number((transformedPoint.x + pathOffset.x).toFixed(this.toFixed)),
y: Number((transformedPoint.y + pathOffset.y).toFixed(this.toFixed)),
// canvasPoint는 WORLD 좌표
// inPolygonImproved에서 getCurrentPoints()도 WORLD 좌표를 반환
// 따라서 좌표 시스템이 일치함
const checkPoint = {
x: Number(canvasPoint.x.toFixed(this.toFixed)),
y: Number(canvasPoint.y.toFixed(this.toFixed)),
}
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygonImproved(finalPoint)
const isInside = this.inPolygonImproved(checkPoint)
if (!this.selectable) {
this.set('selectable', isInside)
}
return isInside
} else {
return this.inPolygonImproved(finalPoint)
return this.inPolygonImproved(checkPoint)
}
},

View File

@ -84,6 +84,7 @@ export function useModule() {
canvas.renderAll()
})
const surfaceId = selectedModules[0].surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -96,11 +97,15 @@ export function useModule() {
top: module.originCoords.top,
fill: module.originCoords.fill,
})
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
}
}
@ -157,6 +162,7 @@ export function useModule() {
activeModule.set({ strokeWidth: 3 })
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -165,11 +171,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
}
@ -202,6 +212,7 @@ export function useModule() {
})
canvas.renderAll()
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -210,11 +221,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
})
}
@ -269,6 +284,7 @@ export function useModule() {
canvas.renderAll()
})
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -276,11 +292,13 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
surface.set({ modules: [...surface.modules, ...copyModules] })
recalculateAllModulesCoords(surfaceId)
}
})
@ -333,6 +351,7 @@ export function useModule() {
}
})
const surfaceId = modules[0].surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -340,11 +359,13 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.set({ modules: [...moduleSetupSurface.modules, ...copyModules] })
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
@ -426,6 +447,7 @@ export function useModule() {
})
activeModule.set({ strokeWidth: 3 })
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -433,12 +455,14 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.set({ modules: [...moduleSetupSurface.modules, ...copyModules] })
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
}
@ -477,6 +501,7 @@ export function useModule() {
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, leftModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -484,7 +509,7 @@ export function useModule() {
isWarning = true
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = rightModules
} else if (type === MODULE_REMOVE_TYPE.RIGHT) {
leftModules.forEach((module) => {
@ -495,6 +520,7 @@ export function useModule() {
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, rightModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -502,7 +528,7 @@ export function useModule() {
isWarning = true
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = leftModules
} else if (type === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE) {
const sideModules = [...leftModules, ...rightModules]
@ -514,6 +540,7 @@ export function useModule() {
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width / 2 })
module.dirty = true
module.setCoords()
canvas.renderAll()
})
@ -526,6 +553,7 @@ export function useModule() {
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width / 2 })
module.dirty = true
module.setCoords()
canvas.renderAll()
})
@ -547,6 +575,7 @@ export function useModule() {
targetModules = sideModules
}
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.remove.module'),
@ -557,17 +586,24 @@ export function useModule() {
canvas.add(...columnModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
columnModules.forEach((module) => {
module.dirty = true
module.setCoords()
})
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = moduleSetupSurface.modules.filter(
(module) => !columnModules.map((copyModule) => copyModule.id).includes(module.id),
)
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const moduleRowRemove = (type) => {
@ -599,6 +635,7 @@ export function useModule() {
}
if (height === -1) height = module.top - activeModule.top
module.set({ top: module.top - height })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, topModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -606,7 +643,7 @@ export function useModule() {
module.set({ fill: 'red' })
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = bottomModules
} else if (type === MODULE_REMOVE_TYPE.BOTTOM) {
topModules.forEach((module) => {
@ -617,6 +654,7 @@ export function useModule() {
}
if (height === -1) height = activeModule.top - module.top
module.set({ top: module.top + activeModule.height })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, bottomModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -624,6 +662,7 @@ export function useModule() {
module.set({ fill: 'red' })
}
})
canvas.requestRenderAll()
targetModules = topModules
} else if (type === MODULE_REMOVE_TYPE.VERTICAL_SIDE) {
topModules.forEach((module) => {
@ -635,6 +674,7 @@ export function useModule() {
// if (height === -1) height = activeModule.top - module.top
if (height === -1) height = activeModule.height
module.set({ top: module.top + height / 2 })
module.dirty = true
module.setCoords()
})
@ -647,10 +687,11 @@ export function useModule() {
// if (height === -1) height = module.top - activeModule.top
if (height === -1) height = activeModule.height
module.set({ top: module.top - height / 2 })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
const sideModules = [...topModules, ...bottomModules]
sideModules.forEach((module) => {
if (
@ -668,6 +709,7 @@ export function useModule() {
targetModules = sideModules
}
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning && type !== MODULE_REMOVE_TYPE.NONE) {
targetModules.forEach((rect) => rect.set({ fill: 'red' }))
swalFire({
@ -679,13 +721,20 @@ export function useModule() {
canvas.add(...rowModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
rowModules.forEach((module) => {
module.dirty = true
module.setCoords()
})
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
}
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
const moduleColumnInsert = (type) => {
@ -756,6 +805,7 @@ export function useModule() {
module.setCoords()
})
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
@ -764,16 +814,19 @@ export function useModule() {
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = [...moduleSetupSurface.modules, ...copyModules]
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const isFixedModule = () => {
@ -863,6 +916,7 @@ export function useModule() {
})
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
@ -871,16 +925,19 @@ export function useModule() {
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = [...moduleSetupSurface.modules, ...copyModules]
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const alignModule = (type, surfaceArray) => {
@ -924,6 +981,7 @@ export function useModule() {
}
})
canvas.renderAll()
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.align.module'),
@ -932,11 +990,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
})
}
@ -964,6 +1026,7 @@ export function useModule() {
canvas.remove(activeModule)
canvas.renderAll()
setModuleStatisticsData()
recalculateAllModulesCoords(activeModule.surfaceId)
}
const moduleRoofRemove = (surfaceArray) => {
@ -1049,6 +1112,38 @@ export function useModule() {
removeTrestleMaterials()
}
/**
* 모든 모듈의 좌표를 재계산
* / 삭제, 복사, 추가 호출하여 선택 영역(bounding box) 업데이트
* @param {string} surfaceId - 특정 surface의 모듈만 재계산 (선택적)
*/
const recalculateAllModulesCoords = (surfaceId = null) => {
if (!canvas) return
const modules = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.filter((obj) => (surfaceId ? obj.surfaceId === surfaceId : true))
// 모듈의 캐시를 초기화하고 좌표 재계산
modules.forEach((module) => {
// Fabric.js 내부 캐시 초기화
delete module.oCoords
delete module.aCoords
delete module.lineCoords
delete module.__corner
delete module.matrixCache
delete module.ownMatrixCache
// dirty 플래그 설정 및 좌표 재계산
module.dirty = true
module.setCoords()
})
// 렌더링
canvas.renderAll()
}
return {
moduleMove,
moduleMultiMove,
@ -1063,5 +1158,6 @@ export function useModule() {
modulesRemove,
moduleRoofRemove,
alignModule,
recalculateAllModulesCoords,
}
}