열.단 이동/복사 기능 추가

This commit is contained in:
김민식 2024-12-26 13:23:57 +09:00
parent 49b49ef076
commit 363214654d
2 changed files with 347 additions and 1 deletions

View File

@ -9,6 +9,8 @@ import { deepCopyArray } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { useModal } from '@nextui-org/react'
import { useModule } from '@/hooks/module/useModule'
export default function PanelEdit(props) { export default function PanelEdit(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -18,6 +20,7 @@ export default function PanelEdit(props) {
const [direction, setDirection] = useState('up') const [direction, setDirection] = useState('up')
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy } = useModule()
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas) {
@ -28,7 +31,20 @@ export default function PanelEdit(props) {
// //
const handleApply = () => { const handleApply = () => {
contextModuleMove(length, direction) // const activeModuleIds = canvas.getActiveObjects().map((obj) => obj.id)
if (type === 'move') {
moduleMove(length, direction)
} else if (type === 'copy') {
moduleCopy(length, direction)
} else if (type === 'columnMove') {
moduleMultiMove('column', length, direction)
} else if (type === 'columnCopy') {
moduleMultiCopy('column', length, direction)
} else if (type === 'rowMove') {
moduleMultiMove('row', length, direction)
} else if (type === 'rowCopy') {
moduleMultiCopy('row', length, direction)
}
closePopup(id) closePopup(id)
} }

View File

@ -0,0 +1,330 @@
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { canvasState } from '@/store/canvasAtom'
import { isOverlap, polygonToTurfPolygon } from '@/util/canvas-util'
import { useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import * as turf from '@turf/turf'
import { useSwal } from '../useSwal'
import { QPolygon } from '@/components/fabric/QPolygon'
export function useModule() {
const canvas = useRecoilValue(canvasState)
const { swalFire } = useSwal()
const moduleMove = (length, direction) => {
const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => {
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
}
const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴
const selectedIds = selectedObj.map((obj) => obj.id) // selectedObj의 ID 추출
canvas.discardActiveObject() //선택해제
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module' && !selectedIds.includes(obj.id)) // selectedObj에 없는 객체만 필터링
const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴
const setupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === selectedModules[0].surfaceId)[0]
const isOverlapArray = []
const isInSurfaceArray = []
if (selectedModules) {
canvas.remove(...selectedModules)
selectedModules.forEach((module) => {
module.set({
originCoords: {
left: module.left,
top: module.top,
},
})
if (direction === 'up') {
module.set({ ...module, top: module.top - Number(length) })
} else if (direction === 'down') {
module.set({ ...module, top: module.top + Number(length) })
} else if (direction === 'left') {
module.set({ ...module, left: module.left - Number(length) })
} else if (direction === 'right') {
module.set({ ...module, left: module.left + Number(length) })
}
module.setCoords()
canvas.renderAll()
//다른 모듈과 겹치는지 확인하는 로직
const isOverlap = isSetupModules.some((isSetupModule) =>
turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(isSetupModule, true)),
)
isOverlapArray.push(isOverlap)
const turfModuleSetupSurface = polygonToTurfPolygon(setupSurface, true)
const turfModule = polygonToTurfPolygon(module, true)
//나갔는지 확인하는 로직
const isInSurface = turf.booleanContains(turfModuleSetupSurface, turfModule) || turf.booleanWithin(turfModule, turfModuleSetupSurface)
isInSurfaceArray.push(isInSurface)
})
const isNotOverlap = isOverlapArray.some((isOverlap) => isOverlap) // true면 겹침
const isNotOutSurface = isInSurfaceArray.every((isOutSurface) => isOutSurface) //false면 밖으로 나감
//안겹치고 안나갔으면 이동시킨다 아니면 원래 위치로 돌려놓는다
if (isNotOverlap || !isNotOutSurface) {
selectedModules.forEach((module) => {
module.set({ ...module, left: module.originCoords.left, top: module.originCoords.top })
module.setCoords()
})
}
canvas.add(...selectedModules)
canvas.renderAll()
}
}
const moduleCopy = (length, direction) => {
if (canvas.getActiveObjects().length === 0) return
const activeModuleIds = canvas.getActiveObjects().map((obj) => obj.id)
const modules = canvas.getObjects().filter((obj) => activeModuleIds.includes(obj.id))
const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE)
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0]
canvas.discardActiveObject() //선택해제
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length)
const moduleOptions = { ...module, left, top, id: uuidv4() }
const rect = new QPolygon(module.points, moduleOptions)
canvas.add(rect)
rect.setCoords()
canvas.renderAll()
// const isOverlapOtherModules = otherModules.some((otherModule) => rect.intersectsWithObject(otherModule))
const isOverlapOtherModules = otherModules.some(
(otherModule) =>
turf.booleanOverlap(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)) ||
turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)),
)
const isOutsideSurface =
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(rect, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(moduleSetupSurface, true))
if (isOverlapOtherModules || isOutsideSurface) {
swalFire({
title: isOverlapOtherModules ? '겹치는 모듈이 있습니다.' : '영역 밖',
icon: 'error',
type: 'confirm',
confirmFn: () => {
canvas.remove(rect)
canvas.renderAll()
},
})
}
})
}
const moduleMultiMove = (type, length, direction) => {
if (canvas.getActiveObjects().length === 0) return
if (canvas.getActiveObjects().length > 1) {
swalFire({
title: '여러 개의 모듈을 선택할 수 없습니다.',
icon: 'error',
type: 'alert',
})
canvas.discardActiveObject()
return
}
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule)
const otherModules = getOtherModules(modules)
const objects = getObjects()
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []]
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false)
module.originPos = {
top: module.top,
left: module.left,
}
module.set({ top, left })
module.setCoords()
canvas.renderAll()
if (otherModules.length > 0) {
isOverlapOtherModules.push(
otherModules.some(
(otherModule) =>
turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)) ||
turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)),
),
)
}
if (objects.length > 0) {
isOverlapObjects.push(
objects.some(
(object) =>
turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)) ||
turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)),
),
)
}
isOutsideSurface.push(
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)),
)
})
if (
isOverlapOtherModules.some((isOverlap) => isOverlap) ||
isOverlapObjects.some((isOverlap) => isOverlap) ||
isOutsideSurface.some((isOutside) => isOutside)
) {
swalFire({
title: isOverlapOtherModules.some((isOverlap) => isOverlap) ? '겹치는 모듈이 있습니다.' : '영역 밖',
icon: 'error',
type: 'confirm',
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const moduleMultiCopy = (type, length, direction) => {
if (canvas.getActiveObjects().length === 0) return
if (canvas.getActiveObjects().length > 1) {
swalFire({
title: '여러 개의 모듈을 선택할 수 없습니다.',
icon: 'error',
type: 'alert',
})
canvas.discardActiveObject()
return
}
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule)
const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE)
const objects = getObjects()
const copyRects = []
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0]
let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []]
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, true)
const moduleOptions = { ...module, left, top, id: uuidv4() }
const rect = new QPolygon(module.points, moduleOptions)
canvas.add(rect)
copyRects.push(rect)
module.setCoords()
if (otherModules.length > 0) {
isOverlapOtherModules.push(
otherModules.some(
(otherModule) =>
turf.booleanOverlap(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)) ||
turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)),
),
)
}
if (objects.length > 0) {
isOverlapObjects.push(
objects.some(
(object) =>
turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)) ||
turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)),
),
)
}
isOutsideSurface.push(
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(rect, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(moduleSetupSurface, true)),
)
})
console.log(
'🚀 ~ moduleMultiCopy ~ sOverlapOtherModules, isOverlapObjects, isOutsideSurface:',
isOverlapOtherModules,
isOverlapObjects,
isOutsideSurface,
)
if (
isOverlapOtherModules.some((isOverlap) => isOverlap) ||
isOverlapObjects.some((isOverlap) => isOverlap) ||
isOutsideSurface.some((isOutside) => isOutside)
) {
swalFire({
title: isOverlapOtherModules.some((isOverlap) => isOverlap) ? '겹치는 모듈이 있습니다.' : '영역 밖',
icon: 'error',
type: 'confirm',
confirmFn: () => {
canvas.remove(...copyRects)
canvas.renderAll()
},
})
} else {
canvas.renderAll()
}
}
const getRowModules = (target) => {
return canvas.getObjects().filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top)
}
const getColumnModules = (target) => {
return canvas.getObjects().filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left)
}
const getPosotion = (target, direction, length, hasMargin = false) => {
let top = target.top
let left = target.left
if (direction === 'up') {
top = Number(target.top) - Number(length)
top = hasMargin ? top - Number(target.height) : top
} else if (direction === 'down') {
top = Number(target.top) + Number(target.height) + Number(length)
top = hasMargin ? top + Number(target.height) : top
} else if (direction === 'left') {
left = Number(target.left) - Number(length)
left = hasMargin ? left - Number(target.width) : left
} else if (direction === 'right') {
left = Number(target.left) + Number(length)
left = hasMargin ? left + Number(target.width) : left
}
return { top, left }
}
const getOtherModules = (modules) => {
const moduleIds = modules.map((module) => module.id)
return canvas
.getObjects()
.filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE && !moduleIds.includes(obj.id))
}
const getObjects = () => {
return canvas
?.getObjects()
.filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER, BATCH_TYPE.SHADOW].includes(obj.name))
}
return {
moduleMove,
moduleMultiMove,
moduleCopy,
moduleMultiCopy,
}
}