'use client' import { useEffect, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useRecoilValue } from 'recoil' import { canvasState, currentObjectState } from '@/store/canvasAtom' import { BATCH_TYPE, INPUT_TYPE, POLYGON_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { getDegreeByChon, getTrianglePoints, pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding, } from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' import { usePolygon } from '@/hooks/usePolygon' import { QPolygon } from '@/components/fabric/QPolygon' import { v4 as uuidv4 } from 'uuid' import { fontSelector } from '@/store/fontAtom' import { useRoofFn } from '@/hooks/common/useRoofFn' import { roofDisplaySelector } from '@/store/settingAtom' import { usePopup } from '@/hooks/usePopup' import { useMouse } from '@/hooks/useMouse' import { useTurf } from '@/hooks/common/useTurf' import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting' export function useObjectBatch({ isHidden, setIsHidden }) { const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const [popupId, setPopupId] = useState(uuidv4()) const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent() // const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext) const { swalFire } = useSwal() const { drawDirectionArrow, addPolygon, addLengthText } = usePolygon() const { setSurfaceShapePattern } = useRoofFn() const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const roofDisplay = useRecoilValue(roofDisplaySelector) const { closePopup, closeAll, addPopup } = usePopup() const { getIntersectMousePoint } = useMouse() const { checkModuleDisjointSurface } = useTurf() const currentObject = useRecoilValue(currentObjectState) useEffect(() => { if (canvas) { // dbClickEvent() } return () => { initEvent() if (canvas) canvas.off('mouse:dblclick') } }, []) const dbClickEvent = () => { // console.log('dbClickEvent 실행') const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) // console.log('dormerObject', dormerObject) if (dormerObject) { canvas.off('mouse:dblclick') canvas.on('mouse:dblclick', (e) => { console.log('event', e) if (e.target && e.target instanceof fabric.Group) { const pointer = getIntersectMousePoint(e) const objects = e.target._objects // 클릭한 위치에 있는 객체 찾기 const clickedObject = objects.find((obj) => { if (obj.type === 'QPolygon') { const polygon = pointsToTurfPolygon(obj.getCurrentPoints()) const turfPointer = turf.point([pointer.x, pointer.y]) return turf.booleanPointInPolygon(turfPointer, polygon) } else { return obj.containsPoint(pointer) } }) if (clickedObject) { // 클릭된 객체 선택 canvas.setActiveObject(clickedObject) canvas.renderAll() } } }) } } const applyOpeningAndShadow = (objectPlacement, buttonAct) => { const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value const isCrossChecked = buttonAct === 1 ? objectPlacement.isCrossRef.current.checked : false const objName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP let rect, isDown, origX, origY let selectedSurface //프리입력 if (selectedType === INPUT_TYPE.FREE) { addCanvasMouseEventListener('mouse:down', (e) => { isDown = true const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygonImproved({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) if (!selectedSurface) { swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) initEvent() //이벤트 초기화 if (setIsHidden) setIsHidden(false) return } origX = pointer.x origY = pointer.y rect = new fabric.Rect({ left: origX, top: origY, originX: 'left', originY: 'top', width: 0, height: 0, angle: 0, stroke: 'black', }) //개구냐 그림자냐에 따라 변경 rect.set({ fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)', name: objTempName, }) canvas?.add(rect) }) addCanvasMouseEventListener('mouse:move', (e) => { if (!isDown) return if (selectedSurface) { const pointer = getIntersectMousePoint(e) const width = pointer.x - origX const height = pointer.y - origY rect.set({ width: Math.abs(width), height: Math.abs(height) }) if (width < 0) { rect.set({ left: Math.abs(pointer.x) }) } if (height < 0) { rect.set({ top: Math.abs(pointer.y) }) } canvas?.renderAll() } }) addCanvasMouseEventListener('mouse:up', (e) => { if (rect) { const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) //그림자는 배치면이 겹친부분에도 그려져야 한다 if (rect.name === BATCH_TYPE.OPENING_TEMP) { //지붕 밖으로 그렸을때 if (!checkModuleDisjointSurface(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) //일단 지워 deleteTempObjects() return } } if (!isCrossChecked) { const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj)) const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon)) if (isCross) { swalFire({ text: getMessage('batch.object.notinstall.cross'), icon: 'error' }) deleteTempObjects() return } } isDown = false rect.set({ name: objName, parentId: selectedSurface.id, points: rectToPolygon(rect) }) rect.setCoords() canvas.setActiveObject(rect) //선택된 상태로 initEvent() closeAll() addPopup(popupId, 1, ) } }) } else if (selectedType === INPUT_TYPE.DIMENSION) { const width = objectPlacement.widthRef.current.value / 10 const height = objectPlacement.heightRef.current.value / 10 if (width === '' || height === '' || width <= 0 || height <= 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) return } // setShowObjectSettingModal(false) //메뉴보이고 addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === objTempName)) //움직일때 일단 지워가면서 움직임 const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygonImproved({ x: pointer.x, y: pointer.y })) { selectedSurface = surface rect = new fabric.Rect({ fill: 'white', stroke: 'black', strokeWidth: 1, width: width, height: height, left: pointer.x, top: pointer.y, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, parentId: selectedSurface.id, }) //개구냐 그림자냐에 따라 변경 rect.set({ fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)', name: objTempName, }) canvas?.add(rect) } }) }) addCanvasMouseEventListener('mouse:up', (e) => { if (rect) { const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) //그림자는 배치면이 겹친부분에도 그려져야 한다 if (rect.name === BATCH_TYPE.OPENING_TEMP) { //지붕 밖으로 그렸을때 if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) //일단 지워 deleteTempObjects() return } } if (!isCrossChecked) { const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj)) const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon)) if (isCross) { swalFire({ text: getMessage('batch.object.notinstall.cross'), icon: 'error' }) deleteTempObjects() return } } isDown = false rect.set({ name: objName, parentId: selectedSurface.id, points: rectToPolygon(rect) }) rect.setCoords() initEvent() if (setIsHidden) setIsHidden(false) } }) } } /** * 오브젝트 도머 배치 * @param {} dormerPlacement * @param {*} buttonAct * @param {*} surfaceShapePolygons * @returns */ const applyDormers = ({ height, width, pitch, offsetRef, offsetWidthRef, directionRef }, buttonAct) => { const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP //도머의 각도 const dormerAngle = getDegreeByChon(pitch) let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const id = uuidv4() if (height === '' || pitch === '' || height <= 0 || pitch <= 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) return } let theta = 0 let bottomLength = 0, bottomOffsetLength = 0, planeHypotenuse = 0, planeOffsetHypotenuse = 0, actualBottomLength = 0, actualBottomOffsetLength = 0, actualHypotenuse = 0, actualOffsetHypotenuse = 0, shortHeight = 0, shortOffsetHeight = 0, extraHeight = 0, extraOffsetHeight = 0 //삼각형 도머 if (buttonAct === 3) { let groupDormerPoints = [] //나중에 offset을 위한 포인트 저장용 addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) if (selectedSurface) { const roofAngle = selectedSurface.roofMaterial.angle theta = Math.atan(Math.tan((roofAngle * Math.PI) / 180) / Math.tan((dormerAngle * Math.PI) / 180)) //센터 삼각형용 bottomLength = Number((Math.tan(theta) * height).toFixed()) //좌우 삼각형용 bottomOffsetLength = Number((Math.tan(theta) * (height + offsetRef)).toFixed()) planeHypotenuse = Math.sqrt(Math.pow(height, 2) + Math.pow(bottomLength, 2)) actualBottomLength = Math.sqrt( Math.pow(Math.tan(theta) * height, 2) + Math.pow(Math.tan(theta) * height * Math.tan((dormerAngle * Math.PI) / 180), 2), ) actualHypotenuse = Math.sqrt(Math.pow(height, 2) + Math.pow(actualBottomLength, 2)) actualBottomOffsetLength = Math.sqrt( Math.pow(Math.tan(theta) * (height + offsetRef), 2) + Math.pow(Math.tan(theta) * (height + offsetRef) * Math.tan((dormerAngle * Math.PI) / 180), 2), ) planeOffsetHypotenuse = Math.sqrt(Math.pow(height + offsetRef, 2) + Math.pow(bottomOffsetLength, 2)) actualOffsetHypotenuse = Math.sqrt(Math.pow(height + offsetRef, 2) + Math.pow(actualBottomOffsetLength, 2)) let angle = 0 if (directionRef === 'left') { //서 angle = 90 } else if (directionRef === 'right') { //동 angle = 270 } else if (directionRef === 'up') { //북 angle = 180 } dormer = new fabric.Triangle({ fill: 'white', stroke: 'red', strokeDashArray: [5, 5], strokeWidth: 1, width: bottomLength * 2, height: height, left: pointer.x, top: pointer.y, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: dormerTempName, originX: 'center', originY: 'top', angle: angle, }) canvas?.add(dormer) if (offsetRef > 0) { dormerOffset = new fabric.Triangle({ fill: 'gray', stroke: 'red', strokeDashArray: [5, 5], strokeWidth: 1, width: bottomOffsetLength * 2, height: height + offsetRef, left: pointer.x, top: pointer.y, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: dormerTempName, originX: 'center', originY: 'top', angle: angle, objectId: id, }) canvas?.add(dormerOffset) } } }) addCanvasMouseEventListener('mouse:up', (e) => { if (dormer) { const trianglePolygon = pointsToTurfPolygon(offsetRef > 0 ? getTrianglePoints(dormerOffset) : getTrianglePoints(dormer)) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) //지붕 밖으로 그렸을때 if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) //일단 지워 deleteTempObjects() return } //각도 추가 let originAngle = 0 //기본 남쪽 let direction = 'south' if (directionRef === 'left') { //서 originAngle = 90 direction = 'west' } else if (directionRef === 'right') { //동 originAngle = 270 direction = 'east' } else if (directionRef === 'up') { //북 originAngle = 180 direction = 'north' } let { leftPoints, rightPoints, groupPoints, dormerPoints } = offsetRef > 0 ? splitDormerTriangle(dormerOffset, directionRef, dormer) : splitDormerTriangle(dormer, directionRef) canvas?.remove(offsetRef > 0 ? dormerOffset : dormer) if (offsetRef > 0) dormer.set({ name: dormerName, stroke: 'black', strokeWidth: 1, strokeDashArray: [0], }) //오프셋이 있을땐 같이 도머로 만든다 const leftTriangle = addPolygon( leftPoints, { fill: 'white', stroke: 'black', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, // direction: direction, originX: 'center', originY: 'center', name: dormerName, pitch: pitch, fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, groupPoints: groupPoints, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(planeOffsetHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualOffsetHypotenuse, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(bottomOffsetLength, 1) * 10, actualSize: toFixedWithoutRounding(actualBottomOffsetLength, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, actualSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, }, }, ], }, false, ) const rightTriangle = addPolygon( rightPoints, { fill: 'white', stroke: 'black', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, // direction: direction, originX: 'center', originY: 'center', name: dormerName, pitch: pitch, fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, groupPoints: groupPoints, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, actualSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(bottomOffsetLength, 1) * 10, actualSize: toFixedWithoutRounding(actualBottomOffsetLength, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(planeOffsetHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualOffsetHypotenuse, 1) * 10, }, }, ], }, false, ) // canvas?.add(leftTriangle) // canvas?.add(rightTriangle) //패턴 setSurfaceShapePattern(leftTriangle, roofDisplay.column, false, selectedSurface.roofMaterial, true) setSurfaceShapePattern(rightTriangle, roofDisplay.column, false, selectedSurface.roofMaterial, true) //방향 // drawDirectionArrow(leftTriangle) // drawDirectionArrow(rightTriangle) let offsetPolygon groupDormerPoints = groupPoints if (offsetRef > 0) { canvas?.remove(dormer) offsetPolygon = addPolygon( dormerPoints, { selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, name: 'triangleDormerOffset', id: id, fill: 'rgba(255, 255, 255, 0.6)', stroke: 'black', strokeWidth: 1, originX: 'center', originY: 'top', fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, // angle: originAngle, pitch: pitch, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(planeHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualHypotenuse, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(bottomLength * 2, 1) * 10, actualSize: toFixedWithoutRounding(bottomLength * 2, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(planeHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualHypotenuse, 1) * 10, }, }, ], }, false, ) } // addLengthText(leftTriangle) // addLengthText(rightTriangle) // if (offsetRef > 0) addLengthText(offsetPolygon) const groupPolygon = offsetPolygon ? [leftTriangle, rightTriangle, offsetPolygon] : [leftTriangle, rightTriangle] const objectGroup = new fabric.Group(groupPolygon, { subTargetCheck: true, name: dormerName, id: id, parentId: selectedSurface.id, originX: 'center', originY: 'center', groupPoints: groupDormerPoints, }) canvas?.add(objectGroup) objectGroup._objects.forEach((obj) => { if (obj.hasOwnProperty('texts')) { obj.texts.forEach((text) => { text.bringToFront() }) } }) isDown = false initEvent() // dbClickEvent() if (setIsHidden) setIsHidden(false) } }) } else if (buttonAct === 4) { addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) if (selectedSurface) { const roofAngle = selectedSurface.roofMaterial.angle theta = Math.atan(Math.tan((roofAngle * Math.PI) / 180) / Math.tan((dormerAngle * Math.PI) / 180)) //5각형중에 3각형의 높이 작은영역 shortHeight = width / 2 / Math.tan(theta) extraHeight = height - shortHeight planeHypotenuse = Math.sqrt(Math.pow(shortHeight, 2) + Math.pow(width / 2, 2)) actualBottomLength = Math.sqrt(Math.pow((width / 2) * Math.tan((dormerAngle * Math.PI) / 180), 2) + Math.pow(width / 2, 2)) actualHypotenuse = Math.sqrt(Math.pow(actualBottomLength, 2) + Math.pow(shortHeight, 2)) //큰영역 shortOffsetHeight = (width / 2 + offsetWidthRef) / Math.tan(theta) extraOffsetHeight = height + offsetRef - shortOffsetHeight planeOffsetHypotenuse = Math.sqrt(Math.pow(shortOffsetHeight, 2) + Math.pow(width / 2 + offsetWidthRef, 2)) actualBottomOffsetLength = Math.sqrt( Math.pow((width / 2 + offsetWidthRef) * Math.tan((dormerAngle * Math.PI) / 180), 2) + Math.pow(width / 2 + +offsetWidthRef, 2), ) actualOffsetHypotenuse = Math.sqrt(Math.pow(actualBottomOffsetLength, 2) + Math.pow(shortOffsetHeight, 2)) let angle = 0 if (directionRef === 'left') { //서 angle = 90 } else if (directionRef === 'right') { //동 angle = 270 } else if (directionRef === 'up') { //북 angle = 180 } pentagonPoints = [ { x: pointer.x, y: pointer.y }, { x: pointer.x - width / 2, y: pointer.y + (height - extraHeight) }, { x: pointer.x - width / 2, y: pointer.y + height }, { x: pointer.x + width / 2, y: pointer.y + height }, { x: pointer.x + width / 2, y: pointer.y + (height - extraHeight) }, ] pentagonOffsetPoints = [ { x: pointer.x, y: pointer.y }, { x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef - extraOffsetHeight }, { x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef }, { x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef }, { x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef - extraOffsetHeight }, ] dormer = new QPolygon(pentagonPoints, { fill: 'white', stroke: 'red', strokeDashArray: [5, 5], strokeWidth: 1, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: dormerTempName, originX: 'center', originY: 'top', angle: angle, }) canvas?.add(dormer) if (offsetRef > 0 || offsetWidthRef > 0) { dormerOffset = new QPolygon(pentagonOffsetPoints, { fill: 'gray', stroke: 'red', strokeDashArray: [5, 5], strokeWidth: 1, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: dormerTempName, originX: 'center', originY: 'top', angle: angle, }) canvas?.add(dormerOffset) } } }) addCanvasMouseEventListener('mouse:up', (e) => { if (dormer) { const pentagonPolygon = pointsToTurfPolygon(offsetRef > 0 ? dormerOffset.getCurrentPoints() : dormer.getCurrentPoints()) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) //지붕 밖으로 그렸을때 if (!turf.booleanWithin(pentagonPolygon, selectedSurfacePolygon)) { swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) //일단 지워 deleteTempObjects() return } //각도 추가 let originAngle = 0 //기본 남쪽 let direction = 'south' if (directionRef === 'left') { //서 originAngle = 90 direction = 'west' } else if (directionRef === 'right') { //동 originAngle = 270 direction = 'east' } else if (directionRef === 'up') { //북 originAngle = 180 direction = 'north' } let { leftPoints, rightPoints, groupPoints } = offsetRef > 0 || offsetWidthRef > 0 ? splitDormerPentagon(dormerOffset, directionRef) : splitDormerPentagon(dormer, directionRef) canvas?.remove(offsetRef > 0 || offsetWidthRef > 0 ? dormerOffset : dormer) if (offsetRef > 0) dormer.set({ name: dormerName, stroke: 'black', strokeWidth: 1, strokeDashArray: [0], }) //오프셋이 있을땐 같이 도머로 만든다 const leftPentagon = addPolygon( leftPoints, { fill: 'transparent', stroke: 'red', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, fontSize: 14, // direction: direction, originX: 'center', originY: 'center', name: dormerName, pitch: pitch, groupPoints: groupPoints, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(planeOffsetHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualOffsetHypotenuse, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(extraOffsetHeight, 1) * 10, actualSize: toFixedWithoutRounding(extraOffsetHeight, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(width / 2 + offsetWidthRef, 1) * 10, actualSize: toFixedWithoutRounding(actualBottomOffsetLength, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, actualSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, }, }, ], }, false, ) const rightPentagon = addPolygon( rightPoints, { fill: 'transparent', stroke: 'red', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, fontSize: 14, // direction: direction, originX: 'center', originY: 'center', name: dormerName, pitch: pitch, groupPoints: groupPoints, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, actualSize: toFixedWithoutRounding(height + offsetRef, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(width / 2 + offsetWidthRef, 1) * 10, actualSize: toFixedWithoutRounding(actualBottomOffsetLength, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(extraOffsetHeight, 1) * 10, actualSize: toFixedWithoutRounding(extraOffsetHeight, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(planeOffsetHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualOffsetHypotenuse, 1) * 10, }, }, ], }, false, ) // canvas?.add(leftPentagon) // canvas?.add(rightPentagon) //패턴 setSurfaceShapePattern(leftPentagon, roofDisplay.column, false, selectedSurface.roofMaterial, true) setSurfaceShapePattern(rightPentagon, roofDisplay.column, false, selectedSurface.roofMaterial, true) //방향 // drawDirectionArrow(leftPentagon) // drawDirectionArrow(rightPentagon) let offsetPolygon if (offsetRef > 0) { canvas?.remove(dormer) offsetPolygon = addPolygon( dormer.getCurrentPoints(), { selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, name: 'pentagonDormerOffset', id: id, fill: 'rgba(255, 255, 255, 0.6)', stroke: 'black', strokeWidth: 1, originX: 'center', originY: 'top', fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, // angle: originAngle, groupPoints: groupPoints, dormerAttributes: { height: height, width: width, pitch: pitch, offsetRef: offsetRef, offsetWidthRef: offsetWidthRef, directionRef: directionRef, }, lines: [ { attributes: { planeSize: toFixedWithoutRounding(planeHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualHypotenuse, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(extraHeight, 1) * 10, actualSize: toFixedWithoutRounding(extraHeight, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(width, 1) * 10, actualSize: toFixedWithoutRounding(actualBottomLength * 2, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(extraHeight, 1) * 10, actualSize: toFixedWithoutRounding(extraHeight, 1) * 10, }, }, { attributes: { planeSize: toFixedWithoutRounding(planeHypotenuse, 1) * 10, actualSize: toFixedWithoutRounding(actualHypotenuse, 1) * 10, }, }, ], }, false, ) } // addLengthText(leftPentagon) // addLengthText(rightPentagon) // addLengthText(offsetPolygon) const groupPolygon = offsetPolygon ? [leftPentagon, rightPentagon, offsetPolygon] : [leftPentagon, rightPentagon] const objectGroup = new fabric.Group(groupPolygon, { subTargetCheck: true, name: dormerName, id: id, parentId: selectedSurface.id, groupYn: true, originX: 'center', originY: 'center', groupPoints: groupPoints, }) canvas?.add(objectGroup) objectGroup._objects.forEach((obj) => { if (obj.hasOwnProperty('texts')) { obj.texts.forEach((text) => { text.bringToFront() }) } }) isDown = false initEvent() // dbClickEvent() if (setIsHidden) setIsHidden(false) } }) } } const deleteTempObjects = () => { const deleteTarget = canvas ?.getObjects() .filter( (obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP || obj.name === BATCH_TYPE.TRIANGLE_DORMER_TEMP || obj.name === BATCH_TYPE.PENTAGON_DORMER_TEMP, ) canvas?.remove(...deleteTarget) initEvent() //이벤트 초기화 if (setIsHidden) setIsHidden(false) } const splitDormerTriangle = (triangle, direction, dormer = null) => { const halfWidth = triangle.width / 2 let dormerPoints = [] let leftPoints = [] let rightPoints = [] let groupPoints = [] if (dormer) { dormerPoints = getTrianglePoints(dormer) } if (direction === 'down') { leftPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - halfWidth, y: triangle.top + triangle.height }, { x: triangle.left, y: triangle.top + triangle.height }, ] rightPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left, y: triangle.top + triangle.height }, { x: triangle.left + halfWidth, y: triangle.top + triangle.height }, ] groupPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - halfWidth, y: triangle.top + triangle.height }, { x: triangle.left + halfWidth, y: triangle.top + triangle.height }, ] } else if (direction === 'up') { leftPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - halfWidth, y: triangle.top - triangle.height }, { x: triangle.left, y: triangle.top - triangle.height }, ] rightPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left, y: triangle.top - triangle.height }, { x: triangle.left + halfWidth, y: triangle.top - triangle.height }, ] groupPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - halfWidth, y: triangle.top - triangle.height }, { x: triangle.left + halfWidth, y: triangle.top - triangle.height }, ] } else if (direction === 'left') { leftPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - triangle.height, y: triangle.top - halfWidth }, { x: triangle.left - triangle.height, y: triangle.top }, ] rightPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - triangle.height, y: triangle.top }, { x: triangle.left - triangle.height, y: triangle.top + halfWidth }, ] groupPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left - triangle.height, y: triangle.top - halfWidth }, { x: triangle.left - triangle.height, y: triangle.top + halfWidth }, ] } else if (direction === 'right') { leftPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top + halfWidth }, { x: triangle.left + triangle.height, y: triangle.top }, ] rightPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top - halfWidth }, ] groupPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top + halfWidth }, { x: triangle.left + triangle.height, y: triangle.top - halfWidth }, ] } return { leftPoints, rightPoints, groupPoints, dormerPoints } } const splitDormerPentagon = (pentagon, direction, offsetMode) => { const points = pentagon.points let leftPoints = [] let rightPoints = [] let groupPoints = [] if (direction === 'down') { leftPoints = [ { x: points[0].x, y: points[0].y }, { x: points[1].x, y: points[1].y }, { x: points[2].x, y: points[2].y }, { x: points[0].x, y: points[3].y }, ] rightPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x, y: points[2].y }, { x: points[3].x, y: points[3].y }, { x: points[4].x, y: points[4].y }, ] groupPoints = [ { x: points[0].x, y: points[0].y }, { x: points[1].x, y: points[1].y }, { x: points[2].x, y: points[3].y }, { x: points[3].x, y: points[3].y }, { x: points[4].x, y: points[4].y }, ] } else if (direction === 'up') { leftPoints = [ { x: points[0].x, y: points[0].y }, { x: points[1].x, y: points[0].y - (points[1].y - points[0].y) }, { x: points[2].x, y: points[0].y - (points[2].y - points[0].y) }, { x: points[0].x, y: points[0].y - (points[2].y - points[0].y) }, ] rightPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x, y: points[0].y - (points[2].y - points[0].y) }, { x: points[3].x, y: points[0].y - (points[2].y - points[0].y) }, { x: points[3].x, y: points[0].y - (points[1].y - points[0].y) }, ] groupPoints = [ { x: points[0].x, y: points[0].y }, // 기준점 { x: points[1].x, y: points[0].y - (points[1].y - points[0].y) }, { x: points[2].x, y: points[0].y - (points[2].y - points[0].y) }, { x: points[3].x, y: points[0].y - (points[2].y - points[0].y) }, { x: points[3].x, y: points[0].y - (points[1].y - points[0].y) }, ] } else if (direction === 'left') { leftPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x - (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x), }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y }, ] rightPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x), }, { x: points[0].x - (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, ] groupPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x - (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x), }, { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x), }, { x: points[0].x - (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, ] } else if (direction === 'right') { leftPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x + (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x), }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y }, ] rightPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x), }, { x: points[0].x + (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, ] groupPoints = [ { x: points[0].x, y: points[0].y }, { x: points[0].x + (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x), }, { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x), }, { x: points[0].x + (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, ] } return { leftPoints, rightPoints, groupPoints } } const resizeObjectBatch = (side, target, width, height, popupId) => { const objectWidth = target.width const objectHeight = target.height const changeWidth = toFixedWithoutRounding(width / 10 / objectWidth, 1) const changeHeight = toFixedWithoutRounding(height / 10 / objectHeight, 1) 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.setCoords() canvas?.renderAll() //변경 좌표를 한번 적용 target.scaleX = changeWidth || 1 target.scaleY = changeHeight || 1 canvas?.renderAll() //변경 좌표를 한번 적용 //크기 변경후 좌표를 재 적용 const changedCoords = target.getPointByOrigin(target.originX, target.originY) target.set({ ...target, originX: target.originX, originY: target.originY, left: changedCoords.x, top: changedCoords.y, height: toFixedWithoutRounding(height / 10, 1), width: toFixedWithoutRounding(width / 10, 1), scaleX: 1, scaleY: 1, }) // target.setCoords() canvas.renderAll() const currentPoints = rectToPolygon(target) target.points = currentPoints if (target.type === 'group') reGroupObject(target) closePopup(popupId) } const reGroupObject = (groupObj) => { groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨 const reGroupObjects = [] groupObj._objects.forEach((obj) => { const newObj = addPolygon( obj.getCurrentPoints(), { ...obj, points: obj.getCurrentPoints(), scaleX: 1, scaleY: 1, lines: obj.lines ?? [], groupPoints: groupObj.groupPoints ?? [], groupId: groupObj.groupId ?? '', }, false, ) reGroupObjects.push(newObj) canvas.remove(obj) if (newObj.direction) { drawDirectionArrow(newObj) } newObj.fire('modified') }) const reGroup = new fabric.Group(reGroupObjects, { subTargetCheck: true, name: groupObj.name, id: groupObj.id, groupYn: true, parentId: groupObj.parentId, originX: 'center', originY: 'center', groupPoints: groupObj.groupPoints ?? [], groupId: groupObj.groupId ?? '', }) canvas?.add(reGroup) canvas?.remove(groupObj) reGroup._objects.forEach((obj) => { if (obj.hasOwnProperty('texts')) { obj.texts.forEach((text) => { text.bringToFront() }) } }) return reGroup } const moveObjectBatch = () => { const obj = canvas.getActiveObject() const parentSurface = canvas?.getObjects().filter((item) => item.name === POLYGON_TYPE.ROOF && item.id === obj.parentId)[0] if (obj) { obj.set({ lockMovementX: false, lockMovementY: false, }) const originObj = { ...obj } const turfSurface = pointsToTurfPolygon(parentSurface.getCurrentPoints()) const originLeft = obj.left const originTop = obj.top addCanvasMouseEventListener('mouse:up', (e) => { //개구, 그림자 타입일 경우 폴리곤 타입 변경 if (BATCH_TYPE.OPENING === obj.name || BATCH_TYPE.SHADOW === obj.name) { obj.set({ points: rectToPolygon(obj), }) //그림자는 아무데나 설치 할 수 있게 해달라고 함 if (obj.name === BATCH_TYPE.OPENING) { const turfObject = pointsToTurfPolygon(obj.points) if (turf.booleanWithin(turfObject, turfSurface)) { obj.set({ lockMovementX: true, lockMovementY: true, }) obj.setCoords() } else { swalFire({ title: getMessage('batch.object.outside.roof'), icon: 'warning', }) obj.set({ ...originObj, lockMovementX: true, lockMovementY: true }) } } else { obj.set({ lockMovementX: true, lockMovementY: true, }) obj.bringToFront() } } else { obj.set({ lockMovementX: true, lockMovementY: true, }) if (obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) { const calcLeft = obj.left - originLeft const calcTop = obj.top - originTop const currentDormerPoints = obj.groupPoints.map((item) => { return { x: item.x + calcLeft, y: item.y + calcTop, } }) const turfObject = pointsToTurfPolygon(currentDormerPoints) if (turf.booleanWithin(turfObject, turfSurface)) { obj.set({ lockMovementX: true, lockMovementY: true, groupPoints: currentDormerPoints, }) if (obj.type === 'group') reGroupObject(obj) obj.setCoords() } else { swalFire({ title: getMessage('batch.object.outside.roof'), icon: 'warning', }) obj.set({ ...originObj, lockMovementX: true, lockMovementY: true }) } } } canvas.discardActiveObject() initEvent() }) } } const copyObjectBatch = () => { const obj = canvas.getActiveObject() if (obj) { let clonedObj = null const parentSurface = canvas?.getObjects().filter((item) => item.name === POLYGON_TYPE.ROOF && item.id === obj.parentId)[0] obj.clone((cloned) => { clonedObj = cloned }) addCanvasMouseEventListener('mouse:move', (e) => { const pointer = canvas?.getPointer(e.e) if (!clonedObj) return canvas .getObjects() .filter((clonedObj) => clonedObj.name === 'clonedObj') .forEach((clonedObj) => canvas?.remove(clonedObj)) clonedObj.set({ left: pointer.x, top: pointer.y, name: 'clonedObj', }) canvas.add(clonedObj) }) addCanvasMouseEventListener('mouse:up', (e) => { //개구, 그림자 타입일 경우 폴리곤 타입 변경 if (BATCH_TYPE.OPENING == obj.name || BATCH_TYPE.SHADOW == obj.name) { clonedObj.set({ points: rectToPolygon(clonedObj), }) const turfSurface = pointsToTurfPolygon(parentSurface.points) const turfObject = pointsToTurfPolygon(clonedObj.points) if (turf.booleanWithin(turfObject, turfSurface)) { clonedObj.set({ lockMovementX: true, lockMovementY: true, name: BATCH_TYPE.OPENING, parentId: parentSurface.id, }) clonedObj.setCoords() } else { swalFire({ title: getMessage('batch.object.outside.roof'), icon: 'warning', }) canvas.remove(clonedObj) } } else { clonedObj.set({ lockMovementX: true, lockMovementY: true, name: obj.name, }) if (clonedObj.type === 'group') reGroupObject(clonedObj) clonedObj.setCoords() } canvas.discardActiveObject() initEvent() }) } } const dormerOffsetKeyEvent = (setArrow1, setArrow2) => { addDocumentEventListener('keydown', document, (e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { const keyEvent = e.key === 'ArrowDown' ? 'down' : 'up' setArrow1(keyEvent) } else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { const keyEvent = e.key === 'ArrowLeft' ? 'left' : 'right' setArrow2(keyEvent) } }) } const dormerOffset = (arrow1, arrow2, length1, length2) => { length1 = parseInt(length1) / 10 length2 = parseInt(length2) / 10 const dormer = canvas.getActiveObject() const { left, top } = dormer if (length1) { if (!arrow1) { swalFire({ title: getMessage('length.direction.is.required'), icon: 'warning', }) } else { dormer.top = arrow1 === 'down' ? dormer.top + length1 : dormer.top - length1 } } if (length2) { if (!arrow2) { swalFire({ title: getMessage('length.direction.is.required'), icon: 'warning', }) } else { dormer.left = arrow2 === 'left' ? dormer.left - length2 : dormer.left + length2 } } const parentSurface = canvas?.getObjects().filter((item) => item.name === POLYGON_TYPE.ROOF && item.id === dormer.parentId)[0] const turfSurface = pointsToTurfPolygon(parentSurface.getCurrentPoints()) let turfDormer = pointsToTurfPolygon(rectToPolygon(dormer)) let currentPoints = [] if (dormer.name === BATCH_TYPE.TRIANGLE_DORMER || dormer.name === BATCH_TYPE.PENTAGON_DORMER) { const calcLeft = dormer.left - left const calcTop = dormer.top - top currentPoints = dormer.groupPoints.map((item) => { return { x: item.x + calcLeft, y: item.y + calcTop, } }) turfDormer = pointsToTurfPolygon(currentPoints) } if (!turf.booleanWithin(turfDormer, turfSurface)) { swalFire({ title: getMessage('batch.object.outside.roof'), icon: 'warning', }) //위치 원복` dormer.left = left dormer.top = top return } if (dormer.type === 'group') { dormer.set({ groupPoints: currentPoints }) const newDormer = reGroupObject(dormer) canvas?.setActiveObject(newDormer) } else { const currentPoints = rectToPolygon(dormer) dormer.points = currentPoints } canvas.renderAll() } return { applyOpeningAndShadow, applyDormers, splitDormerTriangle, splitDormerPentagon, resizeObjectBatch, moveObjectBatch, dormerOffsetKeyEvent, dormerOffset, copyObjectBatch, } }