배치면 오브젝트 이동 기능 추가

This commit is contained in:
yjnoh 2024-11-06 12:44:35 +09:00
parent 524eab2130
commit daf6bd4e00
7 changed files with 288 additions and 29 deletions

View File

@ -137,6 +137,22 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}) })
}) })
this.on('polygonMoved', () => {
//폴리곤일때만 사용
let matrix = this.calcTransformMatrix()
let transformedPoints = this.get('points')
.map((p) => {
return new fabric.Point(p.x - this.pathOffset.x, p.y - this.pathOffset.y)
})
.map((p) => {
return fabric.util.transformPoint(p, matrix)
})
this.set('points', transformedPoints)
this.set('pathOffset', { x: this.left, y: this.top })
this.setCoords()
})
// polygon.fillCell({ width: 50, height: 30, padding: 10 }) // polygon.fillCell({ width: 50, height: 30, padding: 10 })
}, },
@ -211,6 +227,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
let points = this.getCurrentPoints() let points = this.getCurrentPoints()
this.texts = []
points.forEach((start, i) => { points.forEach((start, i) => {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
const dx = end.x - start.x const dx = end.x - start.x

View File

@ -8,6 +8,8 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { useRef, useState, useEffect } from 'react' import { useRef, useState, useEffect } from 'react'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -15,8 +17,8 @@ export default function SizeSetting(props) {
const { id, pos = contextPopupPosition, target } = props const { id, pos = contextPopupPosition, target } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { reSizeObjectBatch } = useObjectBatch({}) const { resizeObjectBatch } = useObjectBatch({})
const { reSizePolygon } = useSurfaceShapeBatch()
const widthRef = useRef(null) const widthRef = useRef(null)
const heightRef = useRef(null) const heightRef = useRef(null)
@ -30,7 +32,15 @@ export default function SizeSetting(props) {
const width = widthRef.current.value const width = widthRef.current.value
const height = heightRef.current.value const height = heightRef.current.value
reSizeObjectBatch(settingTarget, target, width, height) if (
target.name === BATCH_TYPE.OPENING ||
target.name === BATCH_TYPE.SHADOW ||
target.name === BATCH_TYPE.TRIANGLE_DORMER ||
target.name === BATCH_TYPE.PENTAGON_DORMER ||
target.name === POLYGON_TYPE.ROOF
) {
resizeObjectBatch(settingTarget, target, width, height)
}
} }
return ( return (

View File

@ -100,8 +100,6 @@ export function useCanvasConfigInitialize() {
const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup') const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup')
const groupIds = [] const groupIds = []
console.log('groupDimensionInit', groups)
groups.forEach((group) => { groups.forEach((group) => {
if (!groupIds.includes(group.id)) { if (!groupIds.includes(group.id)) {
groupIds.push(group.id) groupIds.push(group.id)
@ -157,6 +155,7 @@ export function useCanvasConfigInitialize() {
//그룹아이디로 캔버스의 객체를 조회함 //그룹아이디로 캔버스의 객체를 조회함
const groupObjects = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id) const groupObjects = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)
const objectsName = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].groupName const objectsName = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].groupName
const objectsParentId = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].parentId
let objectArray = [] let objectArray = []
@ -181,8 +180,14 @@ export function useCanvasConfigInitialize() {
lockMovementY: true, lockMovementY: true,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
parentId: objectsParentId,
}) })
canvas.add(group) canvas.add(group)
//그룹 객체 재그룹 완료
group.getObjects().forEach((obj) => {
obj.fire('modified')
})
}) })
} }

View File

@ -33,10 +33,16 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}, []) }, [])
const dbClickEvent = () => { const dbClickEvent = () => {
console.log('dbClickEvent 실행')
const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER)
console.log('dormerObject', dormerObject)
if (dormerObject) { if (dormerObject) {
canvas.off('mouse:dblclick')
canvas.on('mouse:dblclick', (e) => { canvas.on('mouse:dblclick', (e) => {
console.log('event', e)
if (e.target && e.target instanceof fabric.Group) { if (e.target && e.target instanceof fabric.Group) {
const pointer = canvas.getPointer(e.e) const pointer = canvas.getPointer(e.e)
const objects = e.target._objects const objects = e.target._objects
@ -71,7 +77,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
let rect, isDown, origX, origY let rect, isDown, origX, origY
let selectedSurface let selectedSurface
//프리입력 //프리입력
console.log('useObjectBatch', isHidden)
if (selectedType === INPUT_TYPE.FREE) { if (selectedType === INPUT_TYPE.FREE) {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDown = true isDown = true
@ -160,7 +166,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
@ -204,6 +210,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
lockRotation: true, lockRotation: true,
lockScalingX: true, lockScalingX: true,
lockScalingY: true, lockScalingY: true,
parentId: selectedSurface.id,
}) })
//개구냐 그림자냐에 따라 변경 //개구냐 그림자냐에 따라 변경
@ -241,7 +248,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
@ -483,6 +490,9 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
subTargetCheck: true, subTargetCheck: true,
name: dormerName, name: dormerName,
id: id, id: id,
parentId: selectedSurface.id,
originX: 'center',
originY: 'center',
}) })
canvas?.add(objectGroup) canvas?.add(objectGroup)
@ -693,6 +703,10 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
subTargetCheck: true, subTargetCheck: true,
name: dormerName, name: dormerName,
id: id, id: id,
parentId: selectedSurface.id,
groupYn: true,
originX: 'center',
originY: 'center',
}) })
canvas?.add(objectGroup) canvas?.add(objectGroup)
@ -858,9 +872,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
return [leftPoints, rightPoints] return [leftPoints, rightPoints]
} }
const reSizeObjectBatch = (side, target, width, height) => { const resizeObjectBatch = (side, target, width, height) => {
const targetObj = canvas.getActiveObject()
const objectWidth = target.width const objectWidth = target.width
const objectHeight = target.height const objectHeight = target.height
const changeWidth = (width / 10 / objectWidth).toFixed(2) const changeWidth = (width / 10 / objectWidth).toFixed(2)
@ -884,7 +896,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const newCoords = target.getPointByOrigin(sideX, sideY) const newCoords = target.getPointByOrigin(sideX, sideY)
target.set({ target.set({
...target,
originX: sideX, originX: sideX,
originY: sideY, originY: sideY,
left: newCoords.x, left: newCoords.x,
@ -894,8 +905,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
target.setCoords() target.setCoords()
canvas?.renderAll() //변경 좌표를 한번 적용 canvas?.renderAll() //변경 좌표를 한번 적용
target.scaleX = changeWidth === 0 ? 1 : changeWidth target.scaleX = changeWidth || 1
target.scaleY = changeHeight === 0 ? 1 : changeHeight target.scaleY = changeHeight || 1
//크기 변경후 좌표를 재 적용 //크기 변경후 좌표를 재 적용
const changedCoords = target.getPointByOrigin('center', 'center') const changedCoords = target.getPointByOrigin('center', 'center')
@ -910,9 +921,12 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
if (target.name === 'roof') { if (target.name === 'roof') {
//얘는 일단 도머에 적용함 //얘는 일단 도머에 적용함
target._objects.forEach((obj) => { if (target.type === 'group') {
setSurfaceShapePattern(obj) target._objects.forEach((obj) => setSurfaceShapePattern(obj))
}) } else {
setSurfaceShapePattern(target)
target.fire('modified')
}
} }
// target.setCoords() // target.setCoords()
canvas.renderAll() canvas.renderAll()
@ -923,27 +937,29 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const reGroupObject = (groupObj) => { const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨 groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = [] const reGroupObjects = []
groupObj._objects.forEach((obj) => { groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), { const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj, ...obj,
points: obj.getCurrentPoints(), points: obj.getCurrentPoints(),
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
texts: [],
}) })
reGroupObjects.push(newObj) reGroupObjects.push(newObj)
canvas.remove(obj) canvas.remove(obj)
if (obj.direction) { if (obj.direction) {
drawDirectionArrow(obj) drawDirectionArrow(obj)
} }
newObj.fire('modified')
}) })
const reGroup = new fabric.Group(reGroupObjects, { const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true, subTargetCheck: true,
name: groupObj.name, name: groupObj.name,
id: groupObj.id, id: groupObj.id,
groupYn: true, groupYn: true,
parentId: groupObj.parentId,
originX: 'center',
originY: 'center',
}) })
canvas?.add(reGroup) canvas?.add(reGroup)
canvas?.remove(groupObj) canvas?.remove(groupObj)
@ -965,7 +981,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}) })
initEvent() initEvent()
obj.setCoords() obj.setCoords()
reGroupObject(obj) if (obj.type === 'group') reGroupObject(obj)
}) })
} }
} }
@ -975,7 +991,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
applyDormers, applyDormers,
splitDormerTriangle, splitDormerTriangle,
splitDormerPentagon, splitDormerPentagon,
reSizeObjectBatch, resizeObjectBatch,
moveObjectBatch, moveObjectBatch,
} }
} }

View File

@ -12,10 +12,12 @@ import { useEvent } from '@/hooks/useEvent'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { fontSelector } from '@/store/fontAtom'
export function useSurfaceShapeBatch() { export function useSurfaceShapeBatch() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const globalPitch = useRecoilValue(globalPitchState) const globalPitch = useRecoilValue(globalPitchState)
@ -598,8 +600,214 @@ export function useSurfaceShapeBatch() {
}) })
} }
const findAllChildren = (parentId) => {
let allChildren = []
// 직계 자식 객체들 찾기
const directChildren = canvas.getObjects().filter((obj) => obj.parentId === parentId)
directChildren.forEach((child) => {
allChildren.push(child) // 현재 자식 추가
// 자식이 그룹인 경우
if (child.type === 'group') {
// 그룹 내부의 객체들 추가
child.getObjects().forEach((groupItem) => {
allChildren.push(groupItem)
// 그룹 내부 객체의 자식들도 찾기
const nestedChildren = findAllChildren(groupItem.id)
allChildren.push(...nestedChildren)
})
}
// 현재 자식의 하위 자식들 찾기
const childrenOfChild = findAllChildren(child.id)
allChildren.push(...childrenOfChild)
})
// 중복 제거하여 반환
return [...new Set(allChildren)]
}
const findGroupObjects = (parentId) => {
let groupObjectsArray = []
// 직계 자식 객체들 찾기
const directChildren = canvas.getObjects().filter((obj) => obj.parentId === parentId)
// 각 자식 객체에 대해 처리
directChildren.forEach((child) => {
groupObjectsArray.push(child) // 현재 자식 추가
// 자식이 그룹인 경우 그룹 내부 객체들도 처리
if (child.type === 'group') {
child.getObjects().forEach((groupItem) => {
// 그룹 내부 각 아이템의 하위 객체들 찾기
const nestedObjects = findGroupObjects(groupItem.id)
groupObjectsArray.push(...nestedObjects)
})
}
// 일반 자식의 하위 객체들 찾기
const childObjects = findGroupObjects(child.id)
groupObjectsArray.push(...childObjects)
})
return groupObjectsArray
}
function getAllRelatedObjects(id) {
const result = []
const map = new Map()
// Create a map of objects by their id
canvas.getObjects().forEach((obj) => {
map.set(obj.id, obj)
})
// Helper function to recursively find all related objects
function findRelatedObjects(id) {
const obj = map.get(id)
if (obj) {
result.push(obj)
canvas.getObjects().forEach((o) => {
if (o.parentId === id) {
findRelatedObjects(o.id)
}
})
}
}
// Start the search with the given parentId
findRelatedObjects(id)
return result
}
const moveSurfaceShapeBatch = () => {
const roof = canvas.getActiveObject()
if (roof) {
let isDragging = false
const childrenObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
console.log('childrenObjects', childrenObjects)
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id && obj.type === 'group')
// const ungroupObjects = [] // 그룹 해제된 객체들
// const groupChildObjects = []
// groupObjects.forEach((obj) => {
// obj._restoreObjectsState()
// obj.getObjects().forEach((o) => {
// o.set({
// ungroupYn: true,
// })
// canvas.add(o)
// ungroupObjects.push(o)
// })
// canvas.remove(obj)
// })
// const childObjects = findAllChildren(roof.id)
// groupObjects.forEach((obj) => {
// groupChildObjects.push(...obj.getObjects())
// })
// console.log('ungroupObjects', ungroupObjects)
// console.log('childObjects', childObjects)
// console.log('groupChildObjects', groupChildObjects)
// const children = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
// let grandChildren = []
// children.forEach((child) => {
// if (child.type === 'group') {
// child.getObjects().forEach((grandChild) => {
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === grandChild.id)
// grandChildren.push(...groupObjects)
// })
// } else {
// grandChildren.push(...canvas.getObjects().filter((obj) => obj.parentId === child.id))
// }
// })
const selectionArray = [roof, ...childrenObjects]
const selection = new fabric.ActiveSelection(selectionArray, {
canvas: canvas,
draggable: true,
lockMovementX: false, // X축 이동 허용
lockMovementY: false, // Y축 이동 허용
originX: 'center',
originY: 'center',
})
canvas.setActiveObject(selection)
addCanvasMouseEventListener('mouse:up', (e) => {
isDragging = false
canvas.selection = true
canvas.discardActiveObject() // 모든 선택 해제
canvas.requestRenderAll() // 화면 업데이트
selection.getObjects().forEach((obj) => {
obj.set({
lockMovementX: true,
lockMovementY: true,
})
obj.setCoords()
if (obj.type === 'group') {
reGroupObject(obj)
}
})
canvas.renderAll()
roof.fire('polygonMoved')
if (roof.type === 'group') reGroupObject(obj)
drawDirectionArrow(roof)
initEvent()
})
}
}
const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = []
groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj,
points: obj.getCurrentPoints(),
scaleX: 1,
scaleY: 1,
})
reGroupObjects.push(newObj)
canvas.remove(obj)
if (obj.direction) {
drawDirectionArrow(obj)
}
})
const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true,
name: groupObj.name,
id: groupObj.id,
groupYn: true,
parentId: groupObj.parentId,
})
canvas?.add(reGroup)
canvas?.remove(groupObj)
}
return { return {
applySurfaceShape, applySurfaceShape,
deleteAllSurfacesAndObjects, deleteAllSurfacesAndObjects,
moveSurfaceShapeBatch,
} }
} }

View File

@ -33,6 +33,7 @@ import RowRemove from '@/components/floor-plan/modal/module/row/RowRemove'
import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert' import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export function useContextMenu() { export function useContextMenu() {
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
@ -50,7 +51,7 @@ export function useContextMenu() {
const [column, setColumn] = useState(null) const [column, setColumn] = useState(null)
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({}) const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
case MENU.PLAN_DRAWING: case MENU.PLAN_DRAWING:
@ -256,6 +257,7 @@ export function useContextMenu() {
}, [currentContextMenu]) }, [currentContextMenu])
useEffect(() => { useEffect(() => {
console.log('currentObject', currentObject)
if (currentObject?.name) { if (currentObject?.name) {
console.log(currentObject?.name) console.log(currentObject?.name)
switch (currentObject.name) { switch (currentObject.name) {
@ -305,22 +307,25 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: '사이즈 변경', name: '사이즈 변경',
component: <SizeSetting id={popupId} />, component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'roofMaterialRemove', id: 'roofMaterialRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'roofMaterialMove', id: 'roofMaterialMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveSurfaceShapeBatch(),
}, },
{ {
id: 'roofMaterialCopy', id: 'roofMaterialCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
], ],
[ [

View File

@ -77,11 +77,9 @@ export function usePlan() {
}) })
//디렉션이 있는 경우에만 //디렉션이 있는 경우에만
if (group.lineDirection) { if (group.lineDirection) obj.set({ lineDirection: group.lineDirection })
obj.set({ //부모객체가 있으면 (면형상 위에 도머등..)
lineDirection: group.lineDirection, if (group.parentId) obj.set({ parentId: group.parentId })
})
}
canvas?.add(obj) canvas?.add(obj)
obj.setCoords() obj.setCoords()