'use client' import { useMessage } from '@/hooks/useMessage' import { useRecoilState, useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { polygonToTurfPolygon, rectToPolygon, triangleToPolygon, pointsToTurfPolygon, setSurfaceShapePattern } from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' import { drawDirectionArrow } from '@/util/qpolygon-utils' export function useObjectBatch() { const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const { addCanvasMouseEventListener, initEvent } = useEvent() const { swalFire } = useSwal() const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => { 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 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 = canvas.getPointer(e.e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) if (!selectedSurface) { swalFire({ text: '지붕안에 그려야해요', icon: 'error' }) initEvent() //이벤트 초기화 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 = canvas.getPointer(e.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 (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: '개구를 배치할 수 없습니다.', 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: '겹치기 불가요...', icon: 'error' }) deleteTempObjects() return } } isDown = false rect.set({ name: objName }) rect.setCoords() initEvent() } }) } 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 = canvas.getPointer(e.e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ 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 - width / 2, top: pointer.y - height / 2, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, }) //개구냐 그림자냐에 따라 변경 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 (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: '개구를 배치할 수 없습니다.', 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: '겹치기 불가요...', icon: 'error' }) deleteTempObjects() return } } isDown = false rect.set({ name: objName }) rect.setCoords() initEvent() } }) } } const applyDormers = (dormerPlacement, buttonAct, surfaceShapePolygons) => { 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 height = dormerPlacement.heightRef.current.value / 10 const width = dormerPlacement.widthRef.current.value / 10 const pitch = dormerPlacement.pitchRef.current.value const offsetRef = dormerPlacement.offsetRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetRef.current.value) / 10 const offsetWidthRef = dormerPlacement.offsetWidthRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetWidthRef.current.value) / 10 const directionRef = dormerPlacement.directionRef.current let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints console.log('dormerPlacement', dormerPlacement) if (height === '' || pitch === '' || height <= 0 || pitch <= 0) { swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) return } //삼각형 도머 if (buttonAct === 3) { const bottomLength = height / (pitch * 0.25) const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25) addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 const pointer = canvas.getPointer(e.e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) 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, }) canvas?.add(dormerOffset) } }) addCanvasMouseEventListener('mouse:up', (e) => { if (dormer) { const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) //지붕 밖으로 그렸을때 if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { swalFire({ text: '개구를 배치할 수 없습니다.', 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 splitedTriangle = offsetRef > 0 ? splitDormerTriangle(dormerOffset, directionRef) : splitDormerTriangle(dormer, directionRef) canvas?.remove(offsetRef > 0 ? dormerOffset : dormer) if (offsetRef > 0) dormer.set({ name: dormerName, stroke: 'black', strokeWidth: 1, strokeDashArray: [0], }) //오프셋이 있을땐 같이 도머로 만든다 const leftTriangle = new QPolygon(splitedTriangle[0], { fill: 'transparent', stroke: 'black', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, fontSize: 14, direction: direction, originX: 'center', originY: 'center', name: dormerName, }) const rightTriangle = new QPolygon(splitedTriangle[1], { fill: 'transparent', stroke: 'black', strokeWidth: 1, selectable: true, lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 viewLengthText: true, fontSize: 14, direction: direction, originX: 'center', originY: 'center', name: dormerName, }) canvas?.add(leftTriangle) canvas?.add(rightTriangle) //패턴 setSurfaceShapePattern(leftTriangle) setSurfaceShapePattern(rightTriangle) //방향 drawDirectionArrow(leftTriangle) drawDirectionArrow(rightTriangle) isDown = false initEvent() } }) } else if (buttonAct === 4) { const heightLength = height - (width / 2) * (pitch * 0.25) //(동의길이 깊이)+출폭(깊이)-[(입력한 폭값)/2+출폭(폭)]*(0.25*입력한 寸) const heightOffsetLength = height + offsetRef - (width / 2 + offsetWidthRef) * (pitch * 0.25) addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 const pointer = canvas.getPointer(e.e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { selectedSurface = surface } }) 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 - heightLength) }, { 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 - heightLength) }, ] pentagonOffsetPoints = [ { x: pointer.x, y: pointer.y }, { x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef - heightOffsetLength }, { 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 - heightOffsetLength }, ] 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 trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) // const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) // //지붕 밖으로 그렸을때 // if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { // swalFire({ text: '개구를 배치할 수 없습니다.', 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' } const offsetMode = offsetRef > 0 || offsetWidthRef > 0 ? 'offset' : 'normal' let splitedPentagon = offsetRef > 0 || offsetWidthRef > 0 ? splitDormerPentagon(dormerOffset, directionRef, offsetMode) : splitDormerPentagon(dormer, directionRef, offsetMode) canvas?.remove(offsetRef > 0 || offsetWidthRef > 0 ? dormerOffset : dormer) if (offsetRef > 0) dormer.set({ name: dormerName, stroke: 'black', strokeWidth: 1, strokeDashArray: [0], }) //오프셋이 있을땐 같이 도머로 만든다 const leftPentagon = new QPolygon(splitedPentagon[0], { 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, }) const rightPentagon = new QPolygon(splitedPentagon[1], { 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, }) canvas?.add(leftPentagon) canvas?.add(rightPentagon) //패턴 setSurfaceShapePattern(leftPentagon) setSurfaceShapePattern(rightPentagon) //방향 drawDirectionArrow(leftPentagon) drawDirectionArrow(rightPentagon) isDown = false initEvent() } }) } } 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() //이벤트 초기화 } const splitDormerTriangle = (triangle, direction) => { const halfWidth = triangle.width / 2 let leftPoints = [] let rightPoints = [] 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 }, ] } 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 }, ] } 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 }, ] } else if (direction === 'right') { leftPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top + triangle.height }, ] rightPoints = [ { x: triangle.left, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top }, { x: triangle.left + triangle.height, y: triangle.top - triangle.height }, ] } return [leftPoints, rightPoints] } const splitDormerPentagon = (pentagon, direction, offsetMode) => { const points = pentagon.points console.log(pentagon.points) let leftPoints = [] let rightPoints = [] 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 }, ] } 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[3].x, y: points[0].y - (points[1].y - points[0].y) }, { x: points[3].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) }, ] } 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), 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 }, ] } 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), 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 }, ] } console.log(leftPoints, rightPoints) return [leftPoints, rightPoints] } return { applyOpeningAndShadow, applyDormers, splitDormerTriangle, splitDormerPentagon, } }