'use client' import { useEffect } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' import { canvasState, globalPitchState } from '@/store/canvasAtom' import { MENU, POLYGON_TYPE } from '@/common/common' import { getIntersectionPoint } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' import { useSwal } from '@/hooks/useSwal' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { usePopup } from '@/hooks/usePopup' import { roofDisplaySelector } from '@/store/settingAtom' import { usePolygon } from '@/hooks/usePolygon' import { fontSelector } from '@/store/fontAtom' import { slopeSelector } from '@/store/commonAtom' import { QLine } from '@/components/fabric/QLine' import { useRoofFn } from '@/hooks/common/useRoofFn' import { outerLinePointsState } from '@/store/outerLineAtom' export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { const { getMessage } = useMessage() const { drawDirectionArrow } = usePolygon() const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const [points, setPoints] = useRecoilState(outerLinePointsState) const canvas = useRecoilValue(canvasState) const globalPitch = useRecoilValue(globalPitchState) const roofDisplay = useRecoilValue(roofDisplaySelector) const slope = useRecoilValue(slopeSelector(globalPitch)) const { swalFire } = useSwal() const { addCanvasMouseEventListener, initEvent } = useEvent() // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext) const { closePopup } = usePopup() const { setSurfaceShapePattern } = useRoofFn() const applySurfaceShape = (surfaceRefs, selectedType, id) => { let length1, length2, length3, length4, length5 const surfaceId = selectedType?.id const azimuth = surfaceRefs.azimuth.current if (surfaceId === 1) { length1 = surfaceRefs.length1.current.value length2 = surfaceRefs.length2.current.value length3 = surfaceRefs.lengthetc.current.value //대각선 } else if ([2, 4].includes(surfaceId)) { length1 = surfaceRefs.length1.current.value length2 = surfaceRefs.length2.current.value } else if ([3, 5, 6, 15, 18].includes(surfaceId)) { length1 = surfaceRefs.length1.current.value length2 = surfaceRefs.length2.current.value length3 = surfaceRefs.length3.current.value } else if ([8, 12, 13, 16, 17].includes(surfaceId)) { length1 = surfaceRefs.length1.current.value length2 = surfaceRefs.length2.current.value length3 = surfaceRefs.length3.current.value length4 = surfaceRefs.length4.current.value } else if ([7, 9, 10, 11, 14].includes(surfaceId)) { length1 = surfaceRefs.length1.current.value length2 = surfaceRefs.length2.current.value length3 = surfaceRefs.length3.current.value length4 = surfaceRefs.length4.current.value length5 = surfaceRefs.length5.current.value } length1 = parseInt(length1 === undefined ? 0 : length1 / 10) length2 = parseInt(length2 === undefined ? 0 : length2 / 10) length3 = parseInt(length3 === undefined ? 0 : length3 / 10) length4 = parseInt(length4 === undefined ? 0 : length4 / 10) length5 = parseInt(length5 === undefined ? 0 : length5 / 10) let isDrawing = true let obj = null let points = [] //일단 팝업을 가린다 if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) { addCanvasMouseEventListener('mouse:move', (e) => { if (!isDrawing) { return } const pointer = canvas?.getPointer(e.e) canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH_TEMP)) points = getSurfaceShape(surfaceId, pointer, { length1, length2, length3, length4, length5 }) console.log('surfaceRefs.xInversion', surfaceRefs.xInversion) console.log('surfaceRefs.yInversion', surfaceRefs.yInversion) console.log('surfaceRefs.rotate', surfaceRefs.rotate) const { xInversion, yInversion, rotate } = surfaceRefs const options = { fill: 'transparent', stroke: 'black', strokeWidth: 1, strokeDasharray: [10, 4], fontSize: 12, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 name: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH_TEMP, flipX: xInversion !== yInversion ? surfaceRefs.yInversion : false, flipY: xInversion !== yInversion ? surfaceRefs.xInversion : false, angle: xInversion && yInversion ? Math.abs((surfaceRefs.rotate + 180) % 360) : Math.abs(surfaceRefs.rotate), originX: 'center', originY: 'center', pitch: globalPitch, } obj = new QPolygon(points, options) obj.setCoords() //좌표 변경 적용 canvas?.add(obj) canvas?.renderAll() }) addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false canvas?.remove(obj) //각도 추가 let originAngle = 0 //기본 남쪽 let direction = 'south' if (azimuth === 'left') { //서 originAngle = 90 direction = 'west' } else if (azimuth === 'right') { //동 originAngle = 270 direction = 'east' } else if (azimuth === 'up') { //북 originAngle = 180 direction = 'north' } //회전, flip등이 먹은 기준으로 새로생성 const batchSurface = new QPolygon(obj.getCurrentPoints(), { fill: 'transparent', stroke: 'red', strokeWidth: 1, strokeDasharray: [10, 4], fontSize: 12, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 name: POLYGON_TYPE.ROOF, originX: 'center', originY: 'center', pitch: globalPitch, surfaceId: surfaceId, direction: direction, }) canvas?.add(batchSurface) setSurfaceShapePattern(batchSurface, roofDisplay.column) drawDirectionArrow(batchSurface) if (setIsHidden) setIsHidden(false) // closePopup(id) initEvent() }) } else { if (setIsHidden) setIsHidden(false) } } //면형상 입력 validate const checkSurfaceShape = (surfaceId, lengths) => { const { length1, length2, length3, length4, length5 } = lengths let check = true if (surfaceId === 1) { if (length1 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (length2 === 0) { if (length3 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } } } else if ([2, 4].includes(surfaceId)) { if (length1 === 0 || length2 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } } else if ([3, 5, 6, 15, 18].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (surfaceId === 3 && length3 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to3'), icon: 'error' }) check = false } if (surfaceId === 5 || surfaceId === 15) { if (length3 >= length2) { swalFire({ text: getMessage('surface.shape.validate.size.2to3'), icon: 'error' }) check = false } } if (surfaceId === 18) { if (length2 >= length3) { swalFire({ text: getMessage('surface.shape.validate.size.3to2'), icon: 'error' }) check = false } } if (surfaceId === 6) { if (length2 >= length3) { swalFire({ text: getMessage('surface.shape.validate.size.3to2'), icon: 'error' }) check = false } } } else if ([8, 12, 13, 16, 17].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (surfaceId === 8) { if (length2 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to2'), icon: 'error' }) check = false } if (length4 >= length3) { swalFire({ text: getMessage('surface.shape.validate.size.3to4'), icon: 'error' }) check = false } } if (surfaceId === 12 || surfaceId === 13 || surfaceId === 16) { if (length2 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to2'), icon: 'error' }) check = false } if (length4 >= length3) { swalFire({ text: getMessage('surface.shape.validate.size.1to2'), icon: 'error' }) check = false } } if (surfaceId === 17) { if (length2 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to2'), icon: 'error' }) check = false } if (length3 >= length4) { swalFire({ text: getMessage('surface.shape.validate.size.4to3'), icon: 'error' }) check = false } } } else if ([7, 9, 10, 11, 14].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0 || length5 === 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (surfaceId === 9 || surfaceId === 10) { if (length2 + length3 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to23'), icon: 'error' }) check = false } if (length5 >= length4) { swalFire({ text: getMessage('surface.shape.validate.size.4to5'), icon: 'error' }) check = false } } if (surfaceId === 11) { if (length1 > length2 + length3) { swalFire({ text: getMessage('surface.shape.validate.size.1to23low'), icon: 'error' }) check = false } if (length5 >= length4) { swalFire({ text: getMessage('surface.shape.validate.size.4to5'), icon: 'error' }) check = false } } if (surfaceId === 14) { if (length2 + length3 >= length1) { swalFire({ text: getMessage('surface.shape.validate.size.1to23'), icon: 'error' }) check = false } if (length5 >= length4) { swalFire({ text: getMessage('surface.shape.validate.size.4to5'), icon: 'error' }) check = false } } } return check } //면형상 가져오기 const getSurfaceShape = (surfaceId, pointer, lengths) => { let points = [] const { length1, length2, length3, length4, length5 } = lengths switch (surfaceId) { case 1: { let newLength2 = length2 if (length3 !== 0) { newLength2 = Math.sqrt(length3 ** 2 - (length1 / 2) ** 2) } points = [ { x: pointer.x, y: pointer.y - parseInt(newLength2) / 2 }, { x: pointer.x - parseInt(length1) / 2, y: pointer.y + parseInt(newLength2) / 2 }, { x: pointer.x + parseInt(length1) / 2, y: pointer.y + parseInt(newLength2) / 2 }, ] break } case 2: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y - length2 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y - length2 / 2 }, ] break } case 3: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length3 / 2, y: pointer.y - length2 / 2 }, { x: pointer.x - length3 / 2, y: pointer.y - length2 / 2 }, ] break } case 4: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y - length2 / 2 }, ] break } case 5: { points = [ { x: pointer.x - length1 / 2, y: pointer.y - length3 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 - length2 }, ] break } case 6: { const angleInRadians = Math.asin(length2 / length3) points = [ { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, { x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians), }, { x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians), }, { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, ] break } case 7: { points = [ { x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y - (length4 + length5) / 2 }, { x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y + (length4 + length5) / 2 }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 - length5, }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5, }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 + length5, }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3, y: pointer.y + (length4 + length5) / 2 - length5 + length5, }, { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3, y: pointer.y + (length4 + length5) / 2 - length5 + length5 - (length4 + length5), }, ] break } case 8: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length3 }, { x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 }, { x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 + (length3 - length4), }, { x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2, y: pointer.y + length4 / 2 - length3 + (length3 - length4), }, ] break } case 9: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + (length4 - length5), }, { x: pointer.x - length1 / 2 + length1 - length3 - length2, y: pointer.y + length4 / 2 - length4 + (length4 - length5), }, ] break } case 10: { points = [ { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 }, { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5), }, { x: pointer.x + length1 / 2 - length1 + length2 + length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5), }, ] break } case 11: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5), }, { x: pointer.x - length1 / 2 + length1 - length2 - length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5), }, ] break } case 12: { const leftHypotenuse = Math.sqrt(((length1 - length2) / 2) ** 2 + length3 ** 2) const rightHypotenuse = (length4 / length3) * leftHypotenuse const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse) points = [ { x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle), }, { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 }, { x: pointer.x + length1 / 2 - rightHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle), }, { x: pointer.x + length1 / 2 - rightHypotenuse * Math.cos(leftAngle) - length2, y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle), }, ] break } case 13: { const pointsArray = [ { x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }, ] const tmpPolygon = new QPolygon( [ { x: 0, y: length3 }, { x: length1 - length2, y: length3 }, { x: (length1 - length2) / 2, y: length3 - length3 }, ], { fill: 'transparent', stroke: 'black', //black strokeWidth: 1, selectable: false, fontSize: 0, }, ) const coord = getIntersectionPoint(tmpPolygon.lines[1].startPoint, tmpPolygon.lines[2].startPoint, length3 - length4) const scale = (length1 - length2) / coord.x tmpPolygon.set({ scaleX: scale }) tmpPolygon.setViewLengthText(false) pointsArray[0].x = 0 pointsArray[0].y = length3 //바닥면부터 시작하게 pointsArray[1].x = pointsArray[0].x + length1 pointsArray[1].y = pointsArray[0].y pointsArray[2].x = pointsArray[1].x pointsArray[2].y = pointsArray[1].y - length4 pointsArray[3].x = pointsArray[2].x - length2 pointsArray[3].y = pointsArray[2].y pointsArray[4].x = tmpPolygon.getCurrentPoints()[2].x pointsArray[4].y = tmpPolygon.getCurrentPoints()[2].y points = [ { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2, }, { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2, }, { x: pointer.x + length1 / 2, y: pointer.y - length4 / 2, }, { x: pointer.x - (length2 - length1 / 2), y: pointer.y - length4 / 2, }, { x: pointer.x - length1 / 2 + pointsArray[4].x, y: pointer.y - length3 + length4 / 2, }, ] break } case 14: { points = [ { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 }, { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 }, { x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, y: pointer.y + length4 / 2 - length4 + length5, }, ] break } case 15: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) }, { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 }, ] break } case 16: { points = [ { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2, }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2, y: pointer.y + length3 / 2 - (length3 - length4), }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2, y: pointer.y + length3 / 2 - (length3 - length4) - length4, }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, y: pointer.y + length3 / 2 - (length3 - length4) - length4, }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4, }, { x: pointer.x - length1 / 2 + length1, y: pointer.y + length3 / 2, }, ] break } case 17: { const angle = (Math.asin(length3 / length4) * 180) / Math.PI // 높이와 빗변으로 먼저 각도구하기 const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 points = [ { x: pointer.x - length1 / 2 + length1, y: pointer.y + length3 / 2, }, { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2, }, { x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)), y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), }, { x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2, y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), }, { x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)), y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), }, ] break } case 18: { const a = Math.sqrt(length3 * length3 - length2 * length2) // 입력된 밑변과 높이 const sinA = a / length3 const angleInRadians = Math.asin(sinA) const angleInDegrees = angleInRadians * (180 / Math.PI) const b = a - length1 / 2 const c = b / Math.tan(angleInRadians) const d = Math.sqrt(b * b + c * c) const newAngleInRadians = (90 - angleInDegrees) * (Math.PI / 180) points = [ { x: pointer.x - (length1 + b) / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2 - b / 2, y: pointer.y + length2 / 2 }, { x: pointer.x + length1 / 2 - b / 2 + d * Math.cos(newAngleInRadians), y: pointer.y + length2 / 2 - d * Math.sin(newAngleInRadians), }, { x: pointer.x - (length1 + b) / 2 + length3 * Math.cos(newAngleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(newAngleInRadians), }, ] break } } return points } const deleteAllSurfacesAndObjects = () => { swalFire({ text: getMessage('batch.canvas.delete.all'), type: 'confirm', confirmFn: () => { canvas.clear() setPoints([]) swalFire({ text: getMessage('plan.message.delete') }) }, // denyFn: () => { // swalFire({ text: '취소되었습니다.', icon: 'error' }) // }, }) } 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 } const moveSurfaceShapeBatch = () => { const roof = canvas.getActiveObject() if (roof) { const childrenObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.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) => { 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') 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) } const resizeSurfaceShapeBatch = (side, target, width, height) => { const objectWidth = target.width const objectHeight = target.height const changeWidth = width / 10 / objectWidth const changeHeight = height / 10 / objectHeight let sideX = 'left' let sideY = 'top' //그룹 중심점 변경 if (side === 2) { sideX = 'right' sideY = 'top' } else if (side === 3) { sideX = 'left' sideY = 'bottom' } else if (side === 4) { sideX = 'right' sideY = 'bottom' } //변경 전 좌표 const newCoords = target.getPointByOrigin(sideX, sideY) target.set({ originX: sideX, originY: sideY, left: newCoords.x, top: newCoords.y, }) target.scaleX = changeWidth target.scaleY = changeHeight const currentPoints = target.getCurrentPoints() target.set({ scaleX: 1, scaleY: 1, width: parseInt((width / 10).toFixed(0)), height: parseInt((height / 10).toFixed(0)), }) //크기 변경후 좌표를 재 적용 const changedCoords = target.getPointByOrigin('center', 'center') target.set({ originX: 'center', originY: 'center', left: changedCoords.x, top: changedCoords.y, }) //면형상 리사이즈시에만 target.fire('polygonMoved') target.points = currentPoints target.fire('modified') setSurfaceShapePattern(target, roofDisplay.column) if (target.direction) { drawDirectionArrow(target) } target.setCoords() canvas.renderAll() } const changeSurfaceLinePropertyEvent = () => { let tmpLines = [] const roof = canvas.getActiveObject() if (roof) { roof.set({ selectable: false, }) roof.lines.forEach((obj, index) => { const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], { ...obj, stroke: 'rgb(3, 255, 0)', strokeWidth: 8, selectable: true, name: 'lineProperty', lineIndex: index, }) tmpLines.push(tmpLine) canvas.add(tmpLine) }) addCanvasMouseEventListener('mouse:down', (e) => { const selectedLine = e.target if (selectedLine && selectedLine.name !== 'roof') { tmpLines.forEach((line) => { line.set({ stroke: 'rgb(3, 255, 0)', name: 'lineProperty', }) }) selectedLine.set({ stroke: 'red', name: 'selectedLineProperty', }) } else { tmpLines.forEach((line) => { line.set({ stroke: 'rgb(3, 255, 0)', name: 'lineProperty', }) }) } }) canvas.renderAll() } canvas.discardActiveObject() } const changeSurfaceLineProperty = (property, roof) => { if (!property) { swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' }) return } const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능 if (selectedLine && selectedLine.name === 'selectedLineProperty') { swalFire({ text: getMessage('modal.line.property.change.confirm'), type: 'confirm', confirmFn: () => { const lineIndex = selectedLine.lineIndex roof.lines[lineIndex].attributes = { ...roof.lines[lineIndex].attributes, type: property.value, } canvas.renderAll() }, }) } else { swalFire({ text: getMessage('modal.line.property.change.unselect'), icon: 'error' }) } } const changeSurfaceLinePropertyReset = (roof) => { const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty') lines.forEach((line) => { canvas.remove(line) }) if (roof) { roof.set({ selectable: true, }) } canvas?.renderAll() } const updateFlippedPoints = (polygon) => { if (!(polygon instanceof fabric.Polygon)) { console.error('The object is not a Polygon.') return } const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon // 현재 points의 사본 가져오기 const newPoints = points.map((point) => { let x = point.x let y = point.y // flipX 적용 if (flipX) { x = width - x } // flipY 적용 if (flipY) { y = height - y } // 스케일 및 전역 좌표 고려 x = (x - width / 2) * scaleX + width / 2 y = (y - height / 2) * scaleY + height / 2 return { x, y } }) // flipX, flipY를 초기화 polygon.flipX = false polygon.flipY = false // points 업데이트 polygon.set({ points: newPoints }) polygon.setCoords() return polygon } return { applySurfaceShape, deleteAllSurfacesAndObjects, moveSurfaceShapeBatch, resizeSurfaceShapeBatch, changeSurfaceLinePropertyEvent, changeSurfaceLineProperty, changeSurfaceLinePropertyReset, } }