import { useState } from 'react' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { canvasSettingState, canvasState, checkedModuleState, currentObjectState, isManualModuleSetupState } from '@/store/canvasAtom' import { rectToPolygon, polygonToTurfPolygon, calculateVisibleModuleHeight, getDegreeByChon } from '@/util/canvas-util' import { addedRoofsState, basicSettingState, roofDisplaySelector } from '@/store/settingAtom' import offsetPolygon, { calculateAngle } from '@/util/qpolygon-utils' import { QPolygon } from '@/components/fabric/QPolygon' import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom' import { useEvent } from '@/hooks/useEvent' import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common' import * as turf from '@turf/turf' import { useSwal } from '@/hooks/useSwal' import { compasDegAtom } from '@/store/orientationAtom' import { QLine } from '@/components/fabric/QLine' import { useRoofFn } from '@/hooks/common/useRoofFn' import { useEffect } from 'react' import { useMessage } from '@/hooks/useMessage' import { moduleStatisticsState } from '@/store/circuitTrestleAtom' import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' import { useMasterController } from '@/hooks/common/useMasterController' import { v4 as uuidv4 } from 'uuid' export function useModuleBasicSetting(tabNum) { const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() const roofDisplay = useRecoilValue(roofDisplaySelector) const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState) const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState) const { addCanvasMouseEventListener, initEvent, removeMouseEvent, addTargetMouseEventListener } = useEvent() const { swalFire } = useSwal() const compasDeg = useRecoilValue(compasDegAtom) const { setSurfaceShapePattern } = useRoofFn() const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState) const checkedModule = useRecoilValue(checkedModuleState) const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState) const setModuleStatistics = useSetRecoilState(moduleStatisticsState) const canvasSetting = useRecoilValue(canvasSettingState) const moduleSelectionData = useRecoilValue(moduleSelectionDataState) const [trestleDetailParams, setTrestleDetailParams] = useState([]) const [trestleDetailList, setTrestleDetailList] = useState([]) const selectedModules = useRecoilValue(selectedModuleState) const { getTrestleDetailList } = useMasterController() const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false) const [currentObject, setCurrentObject] = useRecoilState(currentObjectState) useEffect(() => { // console.log('basicSetting', basicSetting) if (canvas) { //드래그 여부 // canvas.selection = true // canvas.selectionFullyContained = true } }, []) // const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext) //모듈 선택에서 선택된 값들 넘어옴 useEffect(() => { if (moduleSelectionData && tabNum === 3) { const common = moduleSelectionData.common const roofConstructions = moduleSelectionData.roofConstructions const listParams = roofConstructions.map((item) => { return { ...common, moduleTpCd: selectedModules.itemTp, roofMatlCd: item.trestle.roofMatlCd, trestleMkrCd: item.trestle.trestleMkrCd, constMthdCd: item.trestle.constMthdCd, roofBaseCd: item.trestle.roofBaseCd, constTp: item.construction.constTp, mixMatlNo: selectedModules.mixMatlNo, roofPitch: item.addRoof.hajebichi ? item.addRoof.hajebichi : 0, inclCd: String(item.addRoof.pitch), roofIndex: item.addRoof.index, workingWidth: item.addRoof.lenBase, } }) setTrestleDetailParams(listParams) //북면 설치 가능 판매점 if (moduleSelectionData.common.saleStoreNorthFlg === '1') { setSaleStoreNorthFlg(true) } } }, [moduleSelectionData]) //가대 상세 데이터 조회 const getTrestleDetailListData = async () => { const trestleDetailList = await getTrestleDetailList(trestleDetailParams) if (trestleDetailList.length > 0) { setTrestleDetailList(trestleDetailList) } } //가대 상세 데이터 파라메터 담기면 실행 useEffect(() => { if (trestleDetailParams.length > 0) { getTrestleDetailListData(trestleDetailParams) } }, [trestleDetailParams]) //가대 상세 데이터 들어오면 실행 useEffect(() => { if (trestleDetailList.length > 0) { //지붕을 가져옴 canvas .getObjects() .filter((roof) => roof.name === 'roof') .forEach((roof) => { const roofIndex = roof.roofMaterial.index //지붕의 지붕재의 순번 trestleDetailList.forEach((detail) => { if (detail.data !== null) { if (Number(detail.data.roofIndex) === roofIndex) { //roof에 상세 데이터 추가 roof.set({ trestleDetail: detail.data }) //배치면 설치 영역 makeModuleInstArea(roof, detail.data) //surface에 상세 데이터 추가 } else { swalFire({ text: getMessage('module.roof.not.exist'), icon: 'warning' }) } } }) }) } }, [trestleDetailList]) //가대 상세 데이터 기준으로 모듈 설치 배치면 생성 const makeModuleInstArea = (roof, trestleDetail) => { //지붕 객체 반환 if (!roof) { return } //도머등 오브젝트 객체가 있으면 아웃라인 낸다 const batchObjects = canvas ?.getObjects() .filter( (obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW || obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER, ) //도머s 객체 //도머도 외곽을 따야한다 const batchObjectOptions = { stroke: 'red', fill: 'transparent', strokeDashArray: [10, 4], strokeWidth: 1, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, selectable: true, name: POLYGON_TYPE.OBJECT_SURFACE, originX: 'center', originY: 'center', } //도머등 오브젝트 객체가 있으면 아웃라인 낸다 batchObjects.forEach((obj) => { //도머일때 if (obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) { const groupPoints = obj.groupPoints const offsetObjects = offsetPolygon(groupPoints, 10) const dormerOffset = new QPolygon(offsetObjects, batchObjectOptions) dormerOffset.setViewLengthText(false) canvas.add(dormerOffset) //모듈설치면 만들기 } else { //개구, 그림자일때 const points = obj.points const offsetObjects = offsetPolygon(points, 10) const offset = new QPolygon(offsetObjects, batchObjectOptions) offset.setViewLengthText(false) canvas.add(offset) //모듈설치면 만들기 } }) const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id) if (isExistSurface) { return } let offsetLength = canvasSetting.roofSizeSet === '3' ? -90 : (trestleDetail.eaveIntvl / 10) * -1 setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경 const offsetPoints = offsetPolygon(roof.points, offsetLength) //안쪽 offset //모듈설치영역?? 생성 const surfaceId = uuidv4() let isNorth = false //북면이 있지만 if (roof.directionText && roof.directionText.indexOf('北') > -1) { //북쪽일때 해당 서북서, 동북동은 제외한다고 한다 if (!(roof.directionText.indexOf('西北西') > -1 || roof.directionText.indexOf('東北東') > -1)) { isNorth = true } } //모듈설치면 생성 let setupSurface = new QPolygon(offsetPoints, { stroke: 'red', fill: 'rgba(255,255,255,0.1)', strokeDashArray: [10, 4], strokeWidth: 1, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, selectable: true, parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌 name: POLYGON_TYPE.MODULE_SETUP_SURFACE, flowDirection: roof.direction, direction: roof.direction, flipX: roof.flipX, flipY: roof.flipY, surfaceId: surfaceId, originX: 'center', originY: 'center', modules: [], roofMaterial: roof.roofMaterial, trestleDetail: trestleDetail, isNorth: isNorth, perPixelTargetFind: true, // angle: -compasDeg, }) setupSurface.setViewLengthText(false) canvas.add(setupSurface) //모듈설치면 만들기 //지붕면 선택 금지 roof.set({ selectable: false, //선택 금지 evented: false, //클릭 이벤트도 금지 }) canvas.renderAll() //모듈설치면 클릭이벤트 addTargetMouseEventListener('mousedown', setupSurface, function () { toggleSelection(setupSurface) }) } let selectedModuleInstSurfaceArray = [] //설치 범위 지정 클릭 이벤트 const toggleSelection = (setupSurface) => { const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId) //최초 선택일때 if (!isExist) { //설치면이 북면이고 북면설치 허용점이 아니면 if (setupSurface.isNorth && !saleStoreNorthFlg) { swalFire({ text: getMessage('module.not.batch.north'), icon: 'warning' }) return } //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄 setupSurface.set({ ...setupSurface, strokeWidth: 3, strokeDashArray: [0], fill: 'rgba(255,255,255,0.1)', }) canvas.discardActiveObject() // 객체의 활성 상태 해제 //중복으로 들어가는걸 방지하기 위한 코드 canvas?.renderAll() selectedModuleInstSurfaceArray.push(setupSurface) setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] }) } else { //선택후 재선택하면 선택안됨으로 변경 setupSurface.set({ ...setupSurface, fill: 'rgba(255,255,255,0.1)', strokeDashArray: [10, 4], strokeWidth: 1, }) canvas.discardActiveObject() // 객체의 활성 상태 해제 //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함 const removeIndex = setupSurface.parentId const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex) selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1) setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] }) } canvas?.renderAll() setModuleSetupSurface([...selectedModuleInstSurfaceArray]) } //모듈,회로에서 다른메뉴 -> 배치면으로 갈 경수 초기화 const restoreModuleInstArea = () => { //설치면 삭제 const setupArea = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈 삭제 및 초기화 setupArea.forEach((obj) => { if (obj.modules.length > 0) { obj.modules.forEach((module) => { canvas.remove(module) }) } canvas.remove(obj) obj.modules = [] }) //지붕패턴 변경 const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') roofs.forEach((roof) => { setSurfaceShapePattern(roof, roofDisplay.column, false) //패턴 변경 }) } useEffect(() => { console.log('리코일 바뀌냐??') console.log('isManualModuleSetup', isManualModuleSetup) console.log('saleStoreNorthFlg', saleStoreNorthFlg) if (moduleSelectionData.common.saleStoreNorthFlg === '1') { setSaleStoreNorthFlg(true) } manualModuleSetup() }, [isManualModuleSetup]) /** * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인 * 확인 후 셀을 이동시킴 */ const manualModuleSetup = () => { // console.log('isManualModuleSetup', isManualModuleSetup) const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴 if (isManualModuleSetup) { if (checkedModule.length === 0) { swalFire({ text: getMessage('module.place.select.module') }) setIsManualModuleSetup(!isManualModuleSetup) return } if (checkedModule.length > 1) { swalFire({ text: getMessage('module.place.select.one.module') }) setIsManualModuleSetup(!isManualModuleSetup) return } const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체 //수동모드 모듈 설치면 선택 잠금 moduleSetupSurfaces.forEach((obj) => { obj.set({ selectable: false, evented: false, }) }) const moduleOptions = { fill: checkedModule[0].color, stroke: 'black', strokeWidth: 0.1, selectable: true, // 선택 가능하게 설정 lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 name: POLYGON_TYPE.MODULE, } if (moduleSetupSurfaces.length !== 0) { let tempModule let manualDrawModules = [] let inside = false let turfPolygon let flowDirection let trestlePolygon addCanvasMouseEventListener('mouse:move', (e) => { //마우스 이벤트 삭제 후 재추가 const mousePoint = canvas.getPointer(e.e) for (let i = 0; i < moduleSetupSurfaces.length; i++) { turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i]) trestlePolygon = moduleSetupSurfaces[i] manualDrawModules = moduleSetupSurfaces[i].modules // 앞에서 자동으로 했을때 추가됨 flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향 const moduleWidth = Number(checkedModule[0].longAxis) / 10 const moduleHeight = Number(checkedModule[0].shortAxis) / 10 let tmpWidth = flowDirection === 'south' || flowDirection === 'north' ? moduleWidth : moduleHeight let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth let { width, height } = canvasSetting.roofSizeSet === '1' ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection) : { width: tmpWidth, height: tmpHeight } const points = [ { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, ] const turfPoints = coordToTurfPolygon(points) if (turf.booleanWithin(turfPoints, turfPolygon)) { let isDrawing = false if (isDrawing) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임 tempModule = new fabric.Rect({ fill: 'white', stroke: 'black', strokeWidth: 0.3, width: width, height: height, left: mousePoint.x - width / 2, top: mousePoint.y - height / 2, selectable: false, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: 'tempModule', parentId: moduleSetupSurfaces[i].parentId, }) console.log('trestlePolygon', trestlePolygon) console.log('saleStoreNorthFlg', saleStoreNorthFlg) console.log('trestlePolygon.isNorth', trestlePolygon.isNorth) //북면이고 북면설치상점이 아니면 그냥 return if (trestlePolygon.isNorth && !saleStoreNorthFlg) { return } else { canvas?.add(tempModule) //움직여가면서 추가됨 } /** * 스냅기능 */ let snapDistance = 10 let cellSnapDistance = 20 let intvHor = flowDirection === 'south' || flowDirection === 'north' ? moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10 : moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10 let intvVer = flowDirection === 'south' || flowDirection === 'north' ? moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10 : moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10 const trestleLeft = moduleSetupSurfaces[i].left const trestleTop = moduleSetupSurfaces[i].top const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2 // 작은 폴리곤의 경계 좌표 계산 const smallLeft = tempModule.left const smallTop = tempModule.top const smallRight = smallLeft + tempModule.width * tempModule.scaleX const smallBottom = smallTop + tempModule.height * tempModule.scaleY const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2 const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2 /** * 미리 깔아놓은 셀이 있을때 셀에 흡착됨 */ if (manualDrawModules) { manualDrawModules.forEach((cell) => { const holdCellLeft = cell.left const holdCellTop = cell.top const holdCellRight = holdCellLeft + cell.width * cell.scaleX const holdCellBottom = holdCellTop + cell.height * cell.scaleY const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2 const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2 //설치된 셀에 좌측에 스냅 if (Math.abs(smallRight - holdCellLeft) < snapDistance) { tempModule.left = holdCellLeft - width - intvHor } //설치된 셀에 우측에 스냅 if (Math.abs(smallLeft - holdCellRight) < snapDistance) { tempModule.left = holdCellRight + intvHor } //설치된 셀에 위쪽에 스냅 if (Math.abs(smallBottom - holdCellTop) < snapDistance) { tempModule.top = holdCellTop - height - intvVer } //설치된 셀에 밑쪽에 스냅 if (Math.abs(smallTop - holdCellBottom) < snapDistance) { tempModule.top = holdCellBottom + intvVer } //가운데 -> 가운데 if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX - width / 2 } //왼쪽 -> 가운데 if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX } // 오른쪽 -> 가운데 if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX - width } //세로 가운데 -> 가운데 if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) { tempModule.top = holdCellCenterY - height / 2 } // //위쪽 -> 가운데 // if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) { // tempModule.top = holdCellCenterY // } // //아랫쪽 -> 가운데 // if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) { // tempModule.top = holdCellCenterY - height // } }) } // 위쪽 변에 스냅 if (Math.abs(smallTop - trestleTop) < snapDistance) { tempModule.top = trestleTop } // 아래쪽 변에 스냅 if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY } // 왼쪽변에 스냅 if (Math.abs(smallLeft - trestleLeft) < snapDistance) { tempModule.left = trestleLeft } //오른쪽 변에 스냅 if (Math.abs(smallRight - trestleRight) < snapDistance) { tempModule.left = trestleRight - tempModule.width * tempModule.scaleX } if (flowDirection === 'south' || flowDirection === 'north') { // 모듈왼쪽이 세로중앙선에 붙게 스냅 if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 } // 모듈이 가운데가 세로중앙선에 붙게 스냅 if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2 } // 모듈오른쪽이 세로중앙선에 붙게 스냅 if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX } } else { // 모듈이 가로중앙선에 스냅 if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) { tempModule.top = bigCenterY - tempModule.height / 2 } if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 } // 모듈 밑면이 가로중앙선에 스냅 if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY } } tempModule.setCoords() canvas?.renderAll() inside = true break } else { inside = false } } if (!inside) { // tempModule.set({ fill: 'red' }) canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) canvas?.renderAll() } }) addCanvasMouseEventListener('mouse:up', (e) => { let isIntersection = true if (!inside) return if (tempModule) { const rectPoints = [ { x: tempModule.left, y: tempModule.top }, { x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top }, { x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top + tempModule.height * tempModule.scaleY, }, { x: tempModule.left, y: tempModule.top + tempModule.height * tempModule.scaleY }, ] tempModule.set({ points: rectPoints }) const tempTurfModule = polygonToTurfPolygon(tempModule) //도머 객체를 가져옴 if (batchObjects) { batchObjects.forEach((object) => { let dormerTurfPolygon = polygonToTurfPolygon(object, true) const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인 //겹치면 안됨 if (intersection) { swalFire({ text: getMessage('module.place.overobject') }) isIntersection = false } }) } if (!isIntersection) return tempModule.setCoords() //좌표 재정렬 if (turf.booleanWithin(tempTurfModule, turfPolygon)) { //마우스 클릭시 set으로 해당 위치에 셀을 넣음 const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인 if (!isOverlap) { canvas?.remove(tempModule) //안겹치면 넣는다 // tempModule.setCoords() moduleOptions.surfaceId = trestlePolygon.id let manualModule = new QPolygon(tempModule.points, { ...moduleOptions, moduleInfo: checkedModule[0] }) canvas?.add(manualModule) manualDrawModules.push(manualModule) getModuleStatistics() } else { swalFire({ text: getMessage('module.place.overlab') }) } } else { swalFire({ text: getMessage('module.place.out') }) } } }) } } else { if (moduleSetupSurfaces) { //수동모드 해제시 모듈 설치면 선택 잠금 moduleSetupSurfaces.forEach((obj) => { obj.set({ selectable: true, evented: true, }) }) } removeMouseEvent('mouse:up') removeMouseEvent('mouse:move') canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임 } } //자동 모듈 설치(그리드 방식) const autoModuleSetup = (placementRef) => { initEvent() //마우스 이벤트 초기화 if (checkedModule.length === 0) { swalFire({ text: getMessage('module.place.select.module') }) return } const isChidori = placementRef.isChidori.current === 'true' ? true : false const setupLocation = placementRef.setupLocation.current const isMaxSetup = placementRef.isMaxSetup.current === 'true' ? true : false const moduleSetupSurfaces = moduleSetupSurface //선택 설치면 const notSelectedTrestlePolygons = canvas ?.getObjects() .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것 const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체 if (moduleSetupSurfaces.length === 0) { swalFire({ text: getMessage('module.place.no.surface') }) return } //어짜피 자동으로 누르면 선택안된데도 다 날아간다 canvas.getObjects().forEach((obj) => { if (obj.name === 'module') { canvas.remove(obj) } }) // if (moduleIsSetup.length > 0) { // swalFire({ text: 'alert 아이콘 테스트입니다.', icon: 'error' }) // } // moduleSetupSurfaces.forEach((obj) => { // if (obj.modules) { // obj.modules.forEach((module) => { // canvas?.remove(module) // }) // obj.modules = [] // } // }) notSelectedTrestlePolygons.forEach((obj) => { if (obj.modules) { obj.modules.forEach((module) => { canvas?.remove(module) }) obj.modules = [] } }) let moduleOptions = { stroke: 'black', strokeWidth: 0.3, selectable: true, // 선택 가능하게 설정 lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 name: 'module', } let leftMargin, bottomMargin, square, chidoriLength //선택된 지붕안에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 포함되면 배열 반환 const objectsIncludeSurface = (turfModuleSetupSurface) => { let containsBatchObjects = [] containsBatchObjects = batchObjects.filter((batchObject) => { let convertBatchObject = polygonToTurfPolygon(batchObject) // 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직 return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface) }) return containsBatchObjects } // /** // * 도머나 개구가 모듈에 걸치는지 확인하는 로직 // * @param {*} squarePolygon // * @param {*} containsBatchObjects // * @returns // */ // const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { // let isDisjoint = false // // if (containsBatchObjects.length > 0) { // let convertBatchObject // //도머가 있으면 적용되는 로직 // isDisjoint = containsBatchObjects.every((batchObject) => { // if (batchObject.type === 'group') { // convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) // } else { // convertBatchObject = polygonToTurfPolygon(batchObject) // } // /** // * 도머가 여러개일수있으므로 겹치는게 있다면... // * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 // */ // return turf.booleanDisjoint(squarePolygon, convertBatchObject) // }) // } else { // isDisjoint = true // } // return isDisjoint // } /** * 배치면 안에 있는지 확인 * @param {*} squarePolygon * @param {*} turfModuleSetupSurface * @returns */ const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => { return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) } /** * 모듈의 너비와 높이를 계산하는 함수 * @param {object} maxLengthLine 최대 길이 라인 * @param {object} moduleSetupSurface 모듈 설치면 * @param {object} module 모듈 * @returns {object} 모듈의 너비와 높이 */ const getModuleWidthHeight = (maxLengthLine, moduleSetupSurface, module) => { let tmpWidth = (maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? Number(module.longAxis) : Number(module.shortAxis)) / 10 let tmpHeight = (maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? Number(module.shortAxis) : Number(module.longAxis)) / 10 //배치면때는 방향쪽으로 패널이 넓게 누워져야함 if (moduleSetupSurface.flowDirection !== undefined) { tmpWidth = (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? Number(module.longAxis) : Number(module.shortAxis)) / 10 tmpHeight = (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? Number(module.shortAxis) : Number(module.longAxis)) / 10 } console.log(canvasSetting) return canvasSetting.roofSizeSet === '1' ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurface.roofMaterial.pitch), moduleSetupSurface.flowDirection) : { width: tmpWidth, height: tmpHeight } } const getFlowLines = (moduleSetupSurface, module) => { let flowLines = {} if (canvasSetting.roofSizeSet !== '3') { flowLines = { bottom: bottomTopFlowLine(moduleSetupSurface, module).find((obj) => obj.target === 'bottom'), top: bottomTopFlowLine(moduleSetupSurface, module).find((obj) => obj.target === 'top'), left: leftRightFlowLine(moduleSetupSurface, module).find((obj) => obj.target === 'left'), right: leftRightFlowLine(moduleSetupSurface, module).find((obj) => obj.target === 'right'), } } return flowLines } const downFlowSetupModule = ( surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, isCenter = false, intvHor, intvVer, ) => { let setupModule = [] checkedModule.forEach((module, index) => { const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module) const flowLines = getFlowLines(moduleSetupSurface, module) //육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음 let startPoint = flowLines.bottom const moduleArray = [] if (isCenter) { //중앙배치일 경우에는 계산한다 if (flowLines.bottom.type === 'flat' && flowLines.left.type === 'flat' && flowLines.right.type === 'flat') { //하단 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 const halfWidthLength = Math.abs(startPoint.x1 + startPoint.x2) / 2 //밑에 길이에서 반을 가른다 const halfModuleWidthLength = width / 2 startPoint = { ...startPoint, x1: halfWidthLength - halfModuleWidthLength } if (flowLines.top.type === 'flat') { //상단까지 평면이면 직사각,정사각이라 가정하고 상자의 중심으로 계산 const heightLength = Math.abs(flowLines.left.y1 - flowLines.left.y2) //옆에에 길이에서 반을 가른다 const heightMargin = Math.abs(heightLength - height * Math.floor(heightLength / height)) / 2 startPoint = { ...startPoint, y1: startPoint.y1 - heightMargin } } } } // else { // //중앙배치가 아닐때도 흐름 방향 기준면으로 양면이 직선이면 가운데 배치 // if (flowModuleLine.bottom.type === 'flat' && flowModuleLine.left.type === 'flat' && flowModuleLine.right.type === 'flat') { // //하단 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 // const halfWidthLength = Math.abs(startPoint.x1 + startPoint.x2) / 2 //밑에 길이에서 반을 가른다 // const halfModuleWidthLength = width / 2 // startPoint = { ...startPoint, x1: halfWidthLength - halfModuleWidthLength } // } // } const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 let totalLeftEndPoint = maxLeftEndPoint - startPoint.x1 let totalTopEndPoint = maxTopEndPoint - startPoint.y1 let totalWidth = Math.ceil(Math.abs(maxRightEndPoint - maxLeftEndPoint) / width) let diffLeftEndPoint = Math.abs(totalLeftEndPoint / width) let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let startColPoint = Math.abs(width * Math.ceil(diffLeftEndPoint) - startPoint.x1) let tempMaxWidth = isMaxSetup ? width / 2 : width //최대배치인지 확인하려고 넣음 if (isMaxSetup) totalWidth = totalWidth * 2 //최대배치시 2배로 늘려서 반씩 검사하기위함 for (let j = 0; j < diffTopEndPoint; j++) { bottomMargin = j === 0 ? 0 : intvVer * j for (let i = 0; i <= totalWidth; i++) { leftMargin = i === 0 ? 0 : intvHor * i chidoriLength = 0 if (isChidori && !isMaxSetup) { chidoriLength = j % 2 === 0 ? 0 : width / 2 - intvHor } square = [ [startColPoint + tempMaxWidth * i - chidoriLength + leftMargin, startPoint.y1 - height * j - bottomMargin], [startColPoint + tempMaxWidth * i + width - chidoriLength + leftMargin, startPoint.y1 - height * j - bottomMargin], [startColPoint + tempMaxWidth * i + width - chidoriLength + leftMargin, startPoint.y1 - height * j - height - bottomMargin], [startColPoint + tempMaxWidth * i - chidoriLength + leftMargin, startPoint.y1 - height * j - height - bottomMargin], [startColPoint + tempMaxWidth * i - chidoriLength + leftMargin, startPoint.y1 - height * j - bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module } let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects) if (disjointFromTrestle && isDisjoint) { if (index > 0) { setupModule.forEach((item) => { const isOverlap = item.some((item2) => turf.booleanOverlap(squarePolygon, polygonToTurfPolygon(item2, true))) if (!isOverlap) { canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } }) } else { //최초 한번은 그냥 그린다 //겹치는지 확인해서 포함된 모듈만 그린다 canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } } else { tempModule.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 }) } } } setupModule.push(moduleArray) }) } const leftFlowSetupModule = ( surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, isCenter = false, intvHor, intvVer, ) => { let setupModule = [] checkedModule.forEach((module, index) => { const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module) const flowLines = getFlowLines(moduleSetupSurface, module) //육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음 let startPoint = flowLines.left const moduleArray = [] //중앙배치일 경우에는 계산한다 if (isCenter) { if (flowLines.left.type === 'flat' && flowLines.bottom.type === 'flat' && flowLines.top.type === 'flat') { //좌측 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 const halfWidthLength = Math.abs(startPoint.y1 + startPoint.y2) / 2 //밑에 길이에서 반을 가른다 const halfModuleWidthLength = height / 2 startPoint = { ...startPoint, y1: halfWidthLength - halfModuleWidthLength } if (flowLines.right.type === 'flat') { //우측까지 평면이면 직사각,정사각이라 가정하고 상자의 중심으로 계산 const widthLength = Math.abs(flowLines.top.x1 - flowLines.top.x2) //옆에에 길이에서 반을 가른다 const widthMargin = Math.abs(widthLength - width * Math.floor(widthLength / width)) / 2 startPoint = { ...startPoint, x1: startPoint.x1 + widthMargin } } } } const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalTopEndPoint = Math.abs(maxTopEndPoint - startPoint.y1) //전체 높이에서 현재 높이를 뺌 let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let totalHeight = Math.ceil(Math.abs(maxBottomEndPoint - maxTopEndPoint) / height) let totalWidth = Math.abs(startPoint.x1 - maxRightEndPoint) / width let startRowPoint = startPoint.y1 - height * Math.ceil(diffTopEndPoint) let tempMaxHeight = isMaxSetup ? height / 2 : height //최대배치인지 확인하려고 넣음 if (isMaxSetup) totalHeight = totalHeight * 2 //최대배치시 2배로 늘려 서 반씩 검사 for (let i = 0; i <= totalWidth; i++) { bottomMargin = i === 0 ? 0 : intvHor * i for (let j = 0; j < totalHeight; j++) { leftMargin = j === 0 ? 0 : intvVer * j chidoriLength = 0 if (isChidori && !isMaxSetup) { chidoriLength = i % 2 === 0 ? 0 : height / 2 } square = [ [startPoint.x1 + width * i + bottomMargin, startRowPoint + tempMaxHeight * j + leftMargin - chidoriLength], [startPoint.x1 + width * i + width + bottomMargin, startRowPoint + tempMaxHeight * j + leftMargin - chidoriLength], [startPoint.x1 + width * i + width + bottomMargin, startRowPoint + tempMaxHeight * j + height + leftMargin - chidoriLength], [startPoint.x1 + width * i + bottomMargin, startRowPoint + tempMaxHeight * j + height + leftMargin - chidoriLength], [startPoint.x1 + width * i + bottomMargin, startRowPoint + tempMaxHeight * j + leftMargin - chidoriLength], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module } let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects) if (disjointFromTrestle && isDisjoint) { if (index > 0) { setupModule.forEach((item) => { const isOverlap = item.some((item2) => turf.booleanOverlap(squarePolygon, polygonToTurfPolygon(item2, true))) if (!isOverlap) { canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } }) } else { //최초 한번은 그냥 그린다 //겹치는지 확인해서 포함된 모듈만 그린다 canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } } } } setupModule.push(moduleArray) }) } const topFlowSetupModule = ( surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, isCenter = false, intvHor, intvVer, ) => { let setupModule = [] checkedModule.forEach((module, index) => { const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module) const flowLines = getFlowLines(moduleSetupSurface, module) let startPoint = flowLines.top const moduleArray = [] if (isCenter) { //중앙배치일 경우에는 계산한다 if (flowLines.top.type === 'flat' && flowLines.left.type === 'flat' && flowLines.right.type === 'flat') { //하단 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 const halfWidthLength = Math.abs(startPoint.x1 + startPoint.x2) / 2 //밑에 길이에서 반을 가른다 const halfModuleWidthLength = width / 2 startPoint = { ...startPoint, x1: halfWidthLength - halfModuleWidthLength } if (flowLines.bottom.type === 'flat') { //상단까지 평면이면 직사각,정사각이라 가정하고 상자의 중심으로 계산 const heightLength = Math.abs(flowLines.left.y1 - flowLines.left.y2) //옆에에 길이에서 반을 가른다 const heightMargin = Math.abs(heightLength - height * Math.floor(heightLength / height)) / 2 startPoint = { ...startPoint, x1: halfWidthLength - halfModuleWidthLength, y1: startPoint.y1 - heightMargin } } } } // else { // //중앙배치가 아닐때도 흐름 방향 기준면으로 양면이 직선이면 가운데 배치 // if (flowModuleLine.bottom.type === 'flat' && flowModuleLine.left.type === 'flat' && flowModuleLine.right.type === 'flat') { // //하단 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 // const halfWidthLength = Math.abs(startPoint.x1 + startPoint.x2) / 2 //밑에 길이에서 반을 가른다 // const halfModuleWidthLength = width / 2 // startPoint = { ...startPoint, x1: halfWidthLength - halfModuleWidthLength } // } // } const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalLeftEndPoint = maxLeftEndPoint - startPoint.x1 let totalRightEndPoint = maxLeftEndPoint - maxRightEndPoint let totalBottomEndPoint = maxBottomEndPoint - startPoint.y1 let diffLeftEndPoint = Math.abs(totalLeftEndPoint / width) let diffRightEndPoint = Math.ceil(Math.abs(totalRightEndPoint / width)) let diffBottomEndPoint = Math.ceil(Math.abs(totalBottomEndPoint / height)) let startColPoint = Math.abs(width * Math.ceil(diffLeftEndPoint) - startPoint.x1) let tempMaxWidth = isMaxSetup ? width / 2 : width //최대배치인지 확인하려고 넣음 if (isMaxSetup) diffRightEndPoint = diffRightEndPoint * 2 //최대배치시 2배로 늘려서 반씩 검사하기위함 startColPoint = Math.round(startColPoint - intvHor * diffRightEndPoint) for (let j = 0; j < diffBottomEndPoint; j++) { bottomMargin = j === 0 ? 0 : intvVer * j for (let i = 0; i < diffRightEndPoint; i++) { leftMargin = i === 0 ? 0 : intvHor * i chidoriLength = 0 if (isChidori && !isMaxSetup) { chidoriLength = j % 2 === 0 ? 0 : width / 2 - intvHor } square = [ [startColPoint + tempMaxWidth * i + chidoriLength + leftMargin, startPoint.y1 + height * j + bottomMargin], [startColPoint + tempMaxWidth * i + chidoriLength + leftMargin, startPoint.y1 + height * j + height + bottomMargin], [startColPoint + tempMaxWidth * i + width + chidoriLength + leftMargin, startPoint.y1 + height * j + height + bottomMargin], [startColPoint + tempMaxWidth * i + width + chidoriLength + leftMargin, startPoint.y1 + height * j + bottomMargin], [startColPoint + tempMaxWidth * i + chidoriLength + leftMargin, startPoint.y1 + height * j + bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module } let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects) if (disjointFromTrestle && isDisjoint) { if (index > 0) { setupModule.forEach((item) => { const isOverlap = item.some((item2) => turf.booleanOverlap(squarePolygon, polygonToTurfPolygon(item2, true))) if (!isOverlap) { canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } }) } else { //최초 한번은 그냥 그린다 //겹치는지 확인해서 포함된 모듈만 그린다 canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } } } } setupModule.push(moduleArray) }) } const rightFlowSetupModule = ( surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, isCenter = false, intvHor, intvVer, ) => { let setupModule = [] checkedModule.forEach((module, index) => { const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module) const flowLines = getFlowLines(moduleSetupSurface, module) let startPoint = flowLines.right const moduleArray = [] if (isCenter) { if (flowLines.left.type === 'flat' && flowLines.bottom.type === 'flat' && flowLines.top.type === 'flat') { //좌측 기준으로 양면이 직선이면 하단 방면으로 가운데로 배치 const halfWidthLength = Math.abs(startPoint.y1 + startPoint.y2) / 2 //밑에 길이에서 반을 가른다 const halfModuleWidthLength = height / 2 startPoint = { ...startPoint, y1: halfWidthLength + halfModuleWidthLength } if (flowLines.right.type === 'flat') { //우측까지 평면이면 직사각,정사각이라 가정하고 상자의 중심으로 계산 const widthLength = Math.abs(flowLines.top.x1 - flowLines.top.x2) //옆에에 길이에서 반을 가른다 const widthMargin = Math.abs(widthLength - width * Math.floor(widthLength / width)) / 2 startPoint = { ...startPoint, x1: startPoint.x1 - widthMargin } } } } const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalTopEndPoint = Math.abs(maxTopEndPoint - startPoint.y1) //전체 높이에서 현재 높이를 뺌 let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let totalHeight = Math.ceil(Math.abs(maxBottomEndPoint - maxTopEndPoint) / height) let totalWidth = Math.abs(startPoint.x1 - maxLeftEndPoint) / width let startRowPoint = startPoint.y1 - height * Math.ceil(diffTopEndPoint) - 3 // -3으로 위치살짝 보정 let tempMaxHeight = isMaxSetup ? height / 2 : height //최대배치인지 확인하려고 넣음 if (isMaxSetup) totalHeight = totalHeight * 2 //최대배치시 2배로 늘려서 반씩 검사 for (let i = 0; i <= totalWidth; i++) { bottomMargin = i === 0 ? 0 : -(intvHor * i) for (let j = 0; j < totalHeight; j++) { leftMargin = j === 0 ? 0 : intvVer * j chidoriLength = 0 if (isChidori && !isMaxSetup) { chidoriLength = i % 2 === 0 ? 0 : height / 2 - intvHor } square = [ [startPoint.x1 - width * i + bottomMargin, startRowPoint - intvHor + tempMaxHeight * j + leftMargin + chidoriLength], [startPoint.x1 - width * i - width + bottomMargin, startRowPoint - intvHor + tempMaxHeight * j + leftMargin + chidoriLength], [startPoint.x1 - width * i - width + bottomMargin, startRowPoint - intvHor + tempMaxHeight * j + height + leftMargin + chidoriLength], [startPoint.x1 - width * i + bottomMargin, startRowPoint - intvHor + tempMaxHeight * j + height + leftMargin + chidoriLength], [startPoint.x1 - width * i + bottomMargin, startRowPoint - intvHor + tempMaxHeight * j + leftMargin + chidoriLength], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module } let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects) if (disjointFromTrestle && isDisjoint) { if (index > 0) { setupModule.forEach((item) => { const isOverlap = item.some((item2) => turf.booleanOverlap(squarePolygon, polygonToTurfPolygon(item2, true))) if (!isOverlap) { canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } }) } else { //최초 한번은 그냥 그린다 //겹치는지 확인해서 포함된 모듈만 그린다 canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleArray.push(tempModule) } } } } setupModule.push(moduleArray) }) } moduleSetupSurfaces.forEach((moduleSetupSurface, index) => { moduleSetupSurface.fire('mousedown') const moduleSetupArray = [] const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환 const containsBatchObjects = objectsIncludeSurface(turfModuleSetupSurface) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface) let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => { return acc.length > cur.length ? acc : cur }) const flowDirection = moduleSetupSurface.flowDirection let intvHor = flowDirection === 'south' || flowDirection === 'north' ? moduleSetupSurface.trestleDetail.moduleIntvlHor / 10 : moduleSetupSurface.trestleDetail.moduleIntvlVer / 10 let intvVer = flowDirection === 'south' || flowDirection === 'north' ? moduleSetupSurface.trestleDetail.moduleIntvlVer / 10 : moduleSetupSurface.trestleDetail.moduleIntvlHor / 10 //처마면 배치 if (setupLocation === 'eaves') { // 흐름방향이 남쪽일때 if (moduleSetupSurface.flowDirection === 'south') { downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'west') { leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'east') { rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'north') { topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } } else if (setupLocation === 'ridge') { //용마루 if (moduleSetupSurface.flowDirection === 'south') { topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'west') { rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'east') { leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'north') { downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer) } } else if (setupLocation === 'center') { //중가면 if (moduleSetupSurface.flowDirection === 'south') { downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'west') { leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'east') { rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer) } if (moduleSetupSurface.flowDirection === 'north') { topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer) } } // const setupedModules = moduleSetupArray.filter((module, index) => { // let disjointFromTrestle = checkModuleDisjointSurface(module.turfPoints, turfModuleSetupSurface) // let isDisjoint = checkModuleDisjointObjects(module.turfPoints, containsBatchObjects) // if (!(disjointFromTrestle && isDisjoint)) { // canvas?.remove(module) // // module.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 }) // return false // } else { // return module // } // }) // canvas?.renderAll() //나간애들 제외하고 설치된 애들로 겹친애들 삭제 하기 moduleSetupArray.forEach((module, index) => { if (isMaxSetup && index > 0) { const isOverlap = turf.booleanOverlap(polygonToTurfPolygon(moduleSetupArray[index - 1]), polygonToTurfPolygon(module)) //겹치는지 확인 if (isOverlap) { //겹쳐있으면 삭제 canvas?.remove(module) // module.set({ fill: 'rgba(72, 161, 250, 0.4)', stroke: 'black', strokeWidth: 0.1 }) canvas.renderAll() moduleSetupArray.splice(index, 1) return false } } }) moduleSetupSurface.set({ modules: moduleSetupArray }) getModuleStatistics() // const moduleArray = [...moduleIsSetup] // moduleArray.push({ // surfaceId: moduleSetupSurface.surfaceId, // moduleSetupArray: setupedModules, // }) // setModuleIsSetup(moduleArray) }) // calculateForApi() } const coordToTurfPolygon = (points) => { const coordinates = points.map((point) => [point.x, point.y]) coordinates.push(coordinates[0]) return turf.polygon([coordinates]) } const batchObjectGroupToTurfPolygon = (group) => { const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon') let allPoints = [] polygons.forEach((obj) => allPoints.push(...obj.get('points'))) const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y]))) const hull = turf.concave(points, { tolerance: 0.1 }) return hull } const bottomTopFlowLine = (surface, module) => { const flowArray = [] const bottomFlow = surface.lines.reduce( (acc, line, index) => { if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) { return { x1: line.x1, y1: line.y1, index: index } } return acc }, { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) flowArray.push(bottomFlow) const topFlow = surface.lines.reduce( (acc, line, index) => { if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) { return { x1: line.x1, y1: line.y1, index: index } } return acc }, { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) flowArray.push(topFlow) let rtnObjArray = [] flowArray.forEach((center, index) => { const linesArray = new Array() surface.lines.filter((line) => { if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) { linesArray.push(line) } }) let coords = [] if (center.index === 0) { coords = [ { x: linesArray[0].x2, y: linesArray[0].y2 }, { x: center.x1, y: center.y1 }, { x: linesArray[1].x1, y: linesArray[1].y1 }, ] } else { coords = [ { x: linesArray[0].x1, y: linesArray[0].y1 }, { x: center.x1, y: center.y1 }, { x: linesArray[1].x2, y: linesArray[1].y2 }, ] } const adjust1 = coords[0].x - coords[1].x const height1 = coords[1].y - coords[0].y const angle1 = Math.abs(Math.round(Math.atan(height1 / adjust1) * (180 / Math.PI) * 1000) / 1000) const adjust2 = coords[2].x - coords[1].x const height2 = coords[2].y - coords[1].y const angle2 = Math.abs(Math.round(Math.atan(height2 / adjust2) * (180 / Math.PI) * 1000) / 1000) const angle3 = 180 - (angle1 + angle2) const charlie = Number(module.longAxis) / 10 + 3 // 평행선길이 약간 여유를 줌 const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180) const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180)) const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이 const sign = Math.sign(coords[0].y - coords[1].y) // 진행방향 const top = coords[1].y + sign * h // 변경되는 높이 좌표 값 const pointX1 = coords[0].x + ((coords[0].y - top) / (coords[0].y - coords[1].y)) * (coords[1].x - coords[0].x) const pointY1 = top const pointX2 = coords[2].x + ((coords[2].y - top) / (coords[2].y - coords[1].y)) * (coords[1].x - coords[2].x) const pointY2 = top // const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { // stroke: 'red', // strokeWidth: 1, // selectable: true, // }) // canvas?.add(finalLine) // canvas?.renderAll() let rtnObj //평평하면 if (alpha === 0 || beta === 0 || h === 0 || sign === 0) { //꼭지점이 없고 평평할때 ex) 네모 let standardLine if (index === 0) { //bottom standardLine = surface.lines.reduce((acc, line, index) => { if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.y2 > acc.y2)) { return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index } } return acc }) } else { standardLine = surface.lines.reduce((acc, line, index) => { if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.y2 < acc.y2)) { return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index } } return acc }) } rtnObj = { target: index === 0 ? 'bottom' : 'top', x1: standardLine.x1, y1: standardLine.y1, x2: standardLine.x2, y2: standardLine.y2, type: 'flat', } } else { rtnObj = { target: index === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' } } rtnObjArray.push(rtnObj) }) return rtnObjArray } const leftRightFlowLine = (surface, module) => { const flowArray = [] const leftFlow = surface.lines.reduce( (acc, line, index) => { if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) { return { x1: line.x1, y1: line.y1, index: index } } return acc }, { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) flowArray.push(leftFlow) const rightFlow = surface.lines.reduce( (acc, line, index) => { if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) { return { x1: line.x1, y1: line.y1, index: index } } return acc }, { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) flowArray.push(rightFlow) let rtnObjArray = [] flowArray.forEach((center, index) => { const linesArray = surface.lines.filter((line) => { if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) { return line } }) let coords = [] if (center.index === 0) { coords = [ { x: linesArray[1].x1, y: linesArray[1].y1 }, { x: center.x1, y: center.y1 }, { x: linesArray[0].x2, y: linesArray[0].y2 }, ] } else { coords = [ { x: linesArray[0].x1, y: linesArray[0].y1 }, { x: center.x1, y: center.y1 }, { x: linesArray[1].x2, y: linesArray[1].y2 }, ] } const adjust1 = coords[0].x - coords[1].x const height1 = coords[1].y - coords[0].y const angle1 = Math.abs(Math.round(Math.atan(adjust1 / height1) * (180 / Math.PI) * 1000) / 1000) const adjust2 = coords[2].x - coords[1].x const height2 = coords[2].y - coords[1].y const angle2 = Math.abs(Math.round(Math.atan(adjust2 / height2) * (180 / Math.PI) * 1000) / 1000) const angle3 = 180 - (angle1 + angle2) const charlie = Number(module.shortAxis) / 10 + 3 // 평행선길이 약간 여유를 줌 const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180) const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180)) const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이 const sign = Math.sign(coords[0].x - coords[1].x) // 진행방향 const top = coords[1].x + sign * h // 변경되는 높이 좌표 값 // const line3 = new QLine([coords[1].x, coords[1].y, top, coords[1].y], { // stroke: 'blue', // strokeWidth: 1, // selectable: true, // }) // canvas?.add(line3) const pointX1 = top const pointY1 = coords[0].y + ((coords[0].x - top) / (coords[0].x - coords[1].x)) * (coords[1].y - coords[0].y) const pointX2 = top const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y) // const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { // stroke: 'red', // strokeWidth: 1, // selectable: true, // }) // canvas?.add(finalLine) // canvas?.renderAll() let rtnObj //평평하면 if (alpha === 0 || beta === 0 || h === 0 || sign === 0) { //꼭지점이 없고 평평할때 ex) 네모 let standardLine if (index === 0) { //bottom standardLine = surface.lines.reduce((acc, line, index) => { if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) { return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index } } return acc }) } else { standardLine = surface.lines.reduce((acc, line, index) => { if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) { return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index } } return acc }) } rtnObj = { target: index === 0 ? 'left' : 'right', x1: standardLine.x1, y1: standardLine.y1, x2: standardLine.x2, y2: standardLine.y2, type: 'flat', } } else { rtnObj = { target: index === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' } } rtnObjArray.push(rtnObj) }) return rtnObjArray } const findSetupSurfaceMaxLines = (surface) => { const leftFlow = surface.lines.reduce( (acc, line, index) => { if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) { return { x1: line.x1, y1: line.y1 } } return acc }, { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) const rightFlow = surface.lines.reduce( (acc, line, index) => { if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) { return { x1: line.x1, y1: line.y1 } } return acc }, { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) const topFlow = surface.lines.reduce( (acc, line, index) => { if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) { return { x1: line.x1, y1: line.y1 } } return acc }, { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) const bottomFlow = surface.lines.reduce( (acc, line, index) => { if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) { return { x1: line.x1, y1: line.y1 } } return acc }, { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스 ) const obj = { left: leftFlow, right: rightFlow, top: topFlow, bottom: bottomFlow, } return obj } const manualFlatroofModuleSetup = (placementFlatRef) => { let moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴 let flatBatchType = placementFlatRef.setupLocation.current.value const batchObjects = canvas ?.getObjects() .filter( (obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER || obj.name === BATCH_TYPE.SHADOW, ) //도머s 객체 const moduleOptions = { fill: '#BFFD9F', stroke: 'black', strokeWidth: 0.1, selectable: true, // 선택 가능하게 설정 lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 parentId: moduleSetupSurface.parentId, surfaceId: moduleSetupSurface.id, name: POLYGON_TYPE.MODULE, } if (moduleSetupSurfaces.length !== 0) { let tempModule let manualDrawModules = [] let inside = false let turfPolygon let flowDirection let trestlePolygon //남쪽 선택 if (flatBatchType === 'excreta') { //변별로 선택 const excretaLines = canvas.getObjects().filter((obj) => obj.name === 'flatExcretaLine') excretaLines.forEach((obj) => { if (obj.isSelected === true) { const points1 = { x: obj.x1, y: obj.y1 } const points2 = { x: obj.x2, y: obj.y2 } const angle = calculateAngle(points1, points2) //변별로 선택으로 되어있을때 모듈면을 회전시키기 const targetdSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0] targetdSurface.angle = -angle //변별로 선택되어있는 지붕도 회전시키기 const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetdSurface.parentId)[0] targetRoof.angle = -angle targetRoof.fire('modified') targetdSurface.fire('modified') } canvas.remove(obj) }) } else { moduleSetupSurfaces.forEach((surface) => { const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0] if (targetRoof) targetRoof.angle = -compasDeg surface.angle = -compasDeg }) } canvas.renderAll() addCanvasMouseEventListener('mouse:move', (e) => { //마우스 이벤트 삭제 후 재추가 const mousePoint = canvas.getPointer(e.e) for (let i = 0; i < moduleSetupSurfaces.length; i++) { turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i], true) trestlePolygon = moduleSetupSurfaces[i] manualDrawModules = moduleSetupSurfaces[i].modules // 앞에서 자동으로 했을때 추가됨 flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향 let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113 let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172 const points = [ { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, ] const turfPoints = coordToTurfPolygon(points) if (turf.booleanWithin(turfPoints, turfPolygon)) { let isDrawing = false if (isDrawing) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임 tempModule = new fabric.Rect({ fill: 'white', stroke: 'black', strokeWidth: 0.3, width: width, height: height, left: mousePoint.x - width / 2, top: mousePoint.y - height / 2, selectable: false, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: 'tempModule', parentId: moduleSetupSurfaces[i].parentId, }) canvas?.add(tempModule) //움직여가면서 추가됨 /** * 스냅기능 */ let snapDistance = 10 let cellSnapDistance = 20 const trestleLeft = moduleSetupSurfaces[i].left const trestleTop = moduleSetupSurfaces[i].top const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2 // 작은 폴리곤의 경계 좌표 계산 const smallLeft = tempModule.left const smallTop = tempModule.top const smallRight = smallLeft + tempModule.width * tempModule.scaleX const smallBottom = smallTop + tempModule.height * tempModule.scaleY const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2 const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2 /** * 미리 깔아놓은 셀이 있을때 셀에 흡착됨 */ if (manualDrawModules) { manualDrawModules.forEach((cell) => { const holdCellLeft = cell.left const holdCellTop = cell.top const holdCellRight = holdCellLeft + cell.width * cell.scaleX const holdCellBottom = holdCellTop + cell.height * cell.scaleY const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2 const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2 //설치된 셀에 좌측에 스냅 if (Math.abs(smallRight - holdCellLeft) < snapDistance) { tempModule.left = holdCellLeft - width - 1 } //설치된 셀에 우측에 스냅 if (Math.abs(smallLeft - holdCellRight) < snapDistance) { tempModule.left = holdCellRight + 1 } //설치된 셀에 위쪽에 스냅 if (Math.abs(smallBottom - holdCellTop) < snapDistance) { tempModule.top = holdCellTop - height - 1 } //설치된 셀에 밑쪽에 스냅 if (Math.abs(smallTop - holdCellBottom) < snapDistance) { tempModule.top = holdCellBottom + 1 } //가운데 -> 가운데 if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX - width / 2 } //왼쪽 -> 가운데 if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX } // 오른쪽 -> 가운데 if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) { tempModule.left = holdCellCenterX - width } //세로 가운데 -> 가운데 if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) { tempModule.top = holdCellCenterY - height / 2 } // //위쪽 -> 가운데 // if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) { // tempModule.top = holdCellCenterY // } // //아랫쪽 -> 가운데 // if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) { // tempModule.top = holdCellCenterY - height // } }) } // 위쪽 변에 스냅 if (Math.abs(smallTop - trestleTop) < snapDistance) { tempModule.top = trestleTop } // 아래쪽 변에 스냅 if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY } // 왼쪽변에 스냅 if (Math.abs(smallLeft - trestleLeft) < snapDistance) { tempModule.left = trestleLeft } //오른쪽 변에 스냅 if (Math.abs(smallRight - trestleRight) < snapDistance) { tempModule.left = trestleRight - tempModule.width * tempModule.scaleX } if (flowDirection === 'south' || flowDirection === 'north') { // 모듈왼쪽이 세로중앙선에 붙게 스냅 if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 } // 모듈이 가운데가 세로중앙선에 붙게 스냅 if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2 } // 모듈오른쪽이 세로중앙선에 붙게 스냅 if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX } } else { // 모듈이 가로중앙선에 스냅 if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) { tempModule.top = bigCenterY - tempModule.height / 2 } if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 } // 모듈 밑면이 가로중앙선에 스냅 if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY } } tempModule.setCoords() canvas?.renderAll() inside = true break } else { inside = false } } if (!inside) { // tempModule.set({ fill: 'red' }) canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) canvas?.renderAll() } }) addCanvasMouseEventListener('mouse:up', (e) => { let isIntersection = true if (!inside) return if (tempModule) { const rectPoints = [ { x: tempModule.left, y: tempModule.top }, { x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top }, { x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top + tempModule.height * tempModule.scaleY, }, { x: tempModule.left, y: tempModule.top + tempModule.height * tempModule.scaleY }, ] tempModule.set({ points: rectPoints }) const tempTurfModule = polygonToTurfPolygon(tempModule) //도머 객체를 가져옴 if (batchObjects) { batchObjects.forEach((object) => { let dormerTurfPolygon if (object.type === 'group') { //도머는 그룹형태임 dormerTurfPolygon = batchObjectGroupToTurfPolygon(object) } else { //개구, 그림자 dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object)) } const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인 //겹치면 안됨 if (intersection) { swalFire({ text: getMessage('module.place.overobject') }) isIntersection = false } }) } if (!isIntersection) return if (turf.booleanWithin(tempTurfModule, turfPolygon)) { //마우스 클릭시 set으로 해당 위치에 셀을 넣음 const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인 if (!isOverlap) { moduleOptions.surfaceId = trestlePolygon.id let manualModule = new QPolygon(tempModule.points, { ...moduleOptions }) canvas?.add(manualModule) manualDrawModules.push(tempModule) } else { swalFire({ text: getMessage('module.place.overlab') }) } } else { swalFire({ text: getMessage('module.place.out') }) } } }) } getModuleStatistics() } const autoFlatroofModuleSetup = (placementFlatRef) => { initEvent() //마우스 이벤트 초기화 const moduleSetupSurfaces = moduleSetupSurface //선택 설치면 const notSelectedTrestlePolygons = canvas ?.getObjects() .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것 const batchObjects = canvas ?.getObjects() .filter( (obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER || obj.name === BATCH_TYPE.SHADOW, ) //도머s 객체 // if (moduleSetupSurfaces.length === 0) { // alert('선택된 모듈 설치면이 없습니다.') // return // } //어짜피 자동으로 누르면 선택안된데도 다 날아간다 canvas.getObjects().forEach((obj) => { if (obj.name === 'module') { canvas.remove(obj) } }) notSelectedTrestlePolygons.forEach((obj) => { if (obj.modules) { obj.modules.forEach((module) => { canvas?.remove(module) }) obj.modules = [] } }) const flatBatchType = placementFlatRef.setupLocation.current.value //남쪽 선택 if (flatBatchType === 'excreta') { //변별로 선택 const excretaLines = canvas.getObjects().filter((obj) => obj.name === 'flatExcretaLine') excretaLines.forEach((obj) => { if (obj.isSelected === true) { const points1 = { x: obj.x1, y: obj.y1 } const points2 = { x: obj.x2, y: obj.y2 } const angle = calculateAngle(points1, points2) // const targetdSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0] const targetSurface = canvas .getObjects() .filter((surface) => surface.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && surface.surfaceId === obj.surfaceId)[0] const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetSurface.parentId)[0] targetRoof.angle = -angle targetSurface.angle = -angle targetRoof.fire('modified') targetSurface.fire('modified') moduleSetupSurfaces.push(targetSurface) } canvas.remove(obj) }) } else { moduleSetupSurfaces.forEach((surface) => { const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0] if (targetRoof) targetRoof.angle = -compasDeg surface.angle = -compasDeg }) } canvas.renderAll() moduleSetupSurfaces.forEach((surface) => { let currentPoints = surface.getCurrentPoints() let lines = [] for (let i = 0; i < currentPoints.length; i++) { const start = currentPoints[i] const end = currentPoints[(i + 1) % currentPoints.length] const line = new QLine([start.x, start.y, end.x, end.y], {}) lines.push(line) } surface.lines.forEach((targetLine, index) => { targetLine.x1 = lines[index].x1 targetLine.y1 = lines[index].y1 targetLine.x2 = lines[index].x2 targetLine.y2 = lines[index].y2 }) const flowLines = { bottom: bottomTopFlowLine(surface).find((obj) => obj.target === 'bottom'), top: bottomTopFlowLine(surface).find((obj) => obj.target === 'top'), left: leftRightFlowLine(surface).find((obj) => obj.target === 'left'), right: leftRightFlowLine(surface).find((obj) => obj.target === 'right'), } surface.set({ flowLines: flowLines }) }) const moduleOptions = { fill: '#BFFD9F', stroke: 'black', strokeWidth: 0.1, selectable: true, // 선택 가능하게 설정 lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 parentId: moduleSetupSurface.parentId, surfaceId: moduleSetupSurface.id, name: POLYGON_TYPE.MODULE, } let leftMargin, bottomMargin, square //선택된 지붕안에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 포함되면 배열 반환 const objectsIncludeSurface = (turfModuleSetupSurface) => { let containsBatchObjects = [] containsBatchObjects = batchObjects.filter((batchObject) => { let convertBatchObject if (batchObject.type === 'group') { //도머는 그룹형태임 convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) } else { //개구, 그림자 batchObject.set({ points: rectToPolygon(batchObject) }) canvas?.renderAll() // set된걸 바로 적용하기 위해 convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환 } // 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직 return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface) }) return containsBatchObjects } /** * 배치면 안에 있는지 확인 * @param {*} squarePolygon * @param {*} turfModuleSetupSurface * @returns */ const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => { return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) } let moduleGroup = [] const flatRoofDownFlowSetupModule = (surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) => { let startPoint = moduleSetupSurface.flowLines.bottom const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 let totalLeftEndPoint = maxLeftEndPoint - startPoint.x1 let totalTopEndPoint = maxTopEndPoint - startPoint.y1 let totalWidth = Math.ceil(Math.abs(maxRightEndPoint - maxLeftEndPoint) / width) let diffLeftEndPoint = Math.abs(totalLeftEndPoint / width) let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let startColPoint = Math.abs(width * Math.ceil(diffLeftEndPoint) - startPoint.x1) let tempMaxWidth = width //최대배치인지 확인하려고 넣음 for (let j = 0; j < diffTopEndPoint; j++) { bottomMargin = marginHeight * j for (let i = 0; i <= totalWidth; i++) { leftMargin = marginWidth * i square = [ [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 - height * j - bottomMargin], [startColPoint + tempMaxWidth * i + width + leftMargin, startPoint.y1 - height * j - bottomMargin], [startColPoint + tempMaxWidth * i + width + leftMargin, startPoint.y1 - height * j - height - bottomMargin], [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 - height * j - height - bottomMargin], [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 - height * j - bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) moduleOptions.surfaceId = moduleSetupSurface.id let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleGroup.push(tempModule) } } } const flatRoofLeftFlowSetupModule = (surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) => { let startPoint = moduleSetupSurface.flowLines.left const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalTopEndPoint = Math.abs(maxTopEndPoint - startPoint.y1) //전체 높이에서 현재 높이를 뺌 let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let totalHeight = Math.ceil(Math.abs(maxBottomEndPoint - maxTopEndPoint) / height) let totalWidth = Math.abs(startPoint.x1 - maxRightEndPoint) / width let startRowPoint = startPoint.y1 - height * Math.ceil(diffTopEndPoint) let tempMaxHeight = height //최대배치인지 확인하려고 넣음 for (let i = 0; i <= totalWidth; i++) { bottomMargin = marginHeight * i for (let j = 0; j < totalHeight; j++) { leftMargin = marginWidth * j square = [ [startPoint.x1 + width * i + leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], [startPoint.x1 + width * i + width + leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], [startPoint.x1 + width * i + width + leftMargin, startRowPoint + tempMaxHeight * j + height + bottomMargin], [startPoint.x1 + width * i + leftMargin, startRowPoint + tempMaxHeight * j + height + bottomMargin], [startPoint.x1 + width * i + leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions.surfaceId = moduleSetupSurface.id let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleGroup.push(tempModule) } } } const flatRoofTopFlowSetupModule = (surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) => { let startPoint = moduleSetupSurface.flowLines.top const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxRightEndPoint = surfaceMaxLines.right.x1 //최 우측 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalLeftEndPoint = maxLeftEndPoint - startPoint.x1 let totalRightEndPoint = maxLeftEndPoint - maxRightEndPoint let totalBottomEndPoint = maxBottomEndPoint - startPoint.y1 let diffLeftEndPoint = Math.abs(totalLeftEndPoint / width) let diffRightEndPoint = Math.ceil(Math.abs(totalRightEndPoint / width)) let diffBottomEndPoint = Math.ceil(Math.abs(totalBottomEndPoint / height)) let startColPoint = Math.abs(width * Math.ceil(diffLeftEndPoint) - startPoint.x1) let tempMaxWidth = width //최대배치인지 확인하려고 넣음 for (let j = 0; j < diffBottomEndPoint; j++) { bottomMargin = marginHeight * j for (let i = 0; i < diffRightEndPoint; i++) { leftMargin = marginWidth * i square = [ [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 + height * j + bottomMargin], [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 + height * j + height + bottomMargin], [startColPoint + tempMaxWidth * i + width + leftMargin, startPoint.y1 + height * j + height + bottomMargin], [startColPoint + tempMaxWidth * i + width + leftMargin, startPoint.y1 + height * j + bottomMargin], [startColPoint + tempMaxWidth * i + leftMargin, startPoint.y1 + height * j + bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions.surfaceId = moduleSetupSurface.id let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleGroup.push(tempModule) } } } const flatRoofRightFlowSetupModule = (surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) => { let startPoint = moduleSetupSurface.flowLines.right const maxLeftEndPoint = surfaceMaxLines.left.x1 //최 좌측 const maxTopEndPoint = surfaceMaxLines.top.y1 //최 상단 const maxBottomEndPoint = surfaceMaxLines.bottom.y1 //최하단 let totalTopEndPoint = Math.abs(maxTopEndPoint - startPoint.y1) //전체 높이에서 현재 높이를 뺌 let diffTopEndPoint = Math.abs(totalTopEndPoint / height) let totalHeight = Math.ceil(Math.abs(maxBottomEndPoint - maxTopEndPoint) / height) let totalWidth = Math.abs(startPoint.x1 - maxLeftEndPoint) / width let startRowPoint = startPoint.y1 - height * Math.ceil(diffTopEndPoint) - 3 // -3으로 위치살짝 보정 let tempMaxHeight = height //최대배치인지 확인하려고 넣음 for (let i = 0; i <= totalWidth; i++) { bottomMargin = marginHeight * i for (let j = 0; j < totalHeight; j++) { leftMargin = marginWidth * j square = [ [startPoint.x1 - width * i - leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], [startPoint.x1 - width * i - width - leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], [startPoint.x1 - width * i - width - leftMargin, startRowPoint + tempMaxHeight * j + height + bottomMargin], [startPoint.x1 - width * i - leftMargin, startRowPoint + tempMaxHeight * j + height + bottomMargin], [startPoint.x1 - width * i - leftMargin, startRowPoint + tempMaxHeight * j + bottomMargin], ] let squarePolygon = turf.polygon([square]) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) // if (disjointFromTrestle && isDisjoint) { moduleOptions.surfaceId = moduleSetupSurface.id let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon }) canvas?.add(tempModule) moduleSetupArray.push(tempModule) moduleGroup.push(tempModule) } } } moduleSetupSurfaces.forEach((moduleSetupSurface, index) => { moduleSetupSurface.fire('mousedown') const moduleSetupArray = [] let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => { return acc.length > cur.length ? acc : cur }) const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface, true) //폴리곤을 turf 객체로 변환 const containsBatchObjects = objectsIncludeSurface(turfModuleSetupSurface) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 let width = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 172.2 : 113.4 let height = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 113.4 : 172.2 //배치면때는 방향쪽으로 패널이 넓게 누워져야함 if (moduleSetupSurface.flowDirection !== undefined) { width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4 height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2 } const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface) const marginWidth = 1 const marginHeight = 1 canvas.renderAll() if (compasDeg >= 0 && compasDeg < 90) { flatRoofDownFlowSetupModule(surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) } else if (compasDeg >= 90 && compasDeg < 180) { flatRoofLeftFlowSetupModule(surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) } else if (compasDeg >= 180 && compasDeg < 270) { flatRoofRightFlowSetupModule(surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) } else { flatRoofTopFlowSetupModule(surfaceMaxLines, width, height, moduleSetupArray, moduleSetupSurface, marginWidth, marginHeight) } const setupedModules = moduleSetupArray.filter((module, index) => { let disjointFromTrestle = checkModuleDisjointSurface(module.turfPoints, turfModuleSetupSurface) let isDisjoint = checkModuleDisjointObjects(module.turfPoints, containsBatchObjects) if (!(disjointFromTrestle && isDisjoint)) { canvas?.remove(module) // module.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 }) return false } else { return module } }) canvas?.renderAll() // 나간애들 제외하고 설치된 애들로 겹친애들 삭제 하기 setupedModules.forEach((module, index) => { if (index > 0) { const isOverlap = turf.booleanOverlap(polygonToTurfPolygon(setupedModules[index - 1]), polygonToTurfPolygon(module)) //겹치는지 확인 if (isOverlap) { //겹쳐있으면 삭제 canvas?.remove(module) // module.set({ fill: 'rgba(72, 161, 250, 0.4)', stroke: 'black', strokeWidth: 0.1 }) canvas.renderAll() setupedModules.splice(index, 1) return false } } }) moduleSetupSurface.set({ modules: setupedModules }) getModuleStatistics() // console.log('moduleSetupSurface', moduleSetupSurface) // console.log('setupedModules', setupedModules) // const groupTest = new fabric.Group([moduleSetupSurface, ...setupedModules], { // angle: compasDeg, // }) // canvas.add(groupTest) }) // console.log(calculateForApi()) //드래그 하기위해 기능 활성화 } /** * 도머나 개구가 모듈에 걸치는지 확인하는 로직 * @param {*} squarePolygon * @param {*} containsBatchObjects * @returns */ const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { let isDisjoint = false if (containsBatchObjects.length > 0) { let convertBatchObject //도머가 있으면 적용되는 로직 isDisjoint = containsBatchObjects.every((batchObject) => { if (batchObject.type === 'group') { convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) } else { if (!batchObject.points) { batchObject.set({ points: rectToPolygon(batchObject) }) } convertBatchObject = polygonToTurfPolygon(batchObject) } /** * 도머가 여러개일수있으므로 겹치는게 있다면... * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 */ return turf.booleanDisjoint(squarePolygon, convertBatchObject) }) } else { isDisjoint = true } return isDisjoint } const getModuleStatistics = () => { const surfaces = canvas.getObjects().filter((obj) => POLYGON_TYPE.MODULE_SETUP_SURFACE === obj.name) console.log('🚀 ~ getModuleStatistics ~ surfaces:', surfaces) let totalWpout = 0 let moduleInfo = {} const rows = surfaces.map((surface) => { let wpOut = 0 moduleInfo = {} surface.modules.forEach((module) => { if (!moduleInfo[module.moduleInfo.itemId]) { moduleInfo[module.moduleInfo.itemId] = { name: module.moduleInfo.itemNm, amount: 0, id: module.moduleInfo.itemId } } wpOut += +module.moduleInfo.wpOut moduleInfo[module.moduleInfo.itemId].amount++ }) totalWpout += wpOut console.log('🚀 ~ moduleData.rows=surfaces.map ~ module:', module) const rowObject = {} Object.keys(moduleInfo).forEach((key) => { rowObject[key] = moduleInfo[key].amount }) return { ...rowObject, // 총 발전량 = 발전량 * 모듈 개수 ...surface, name: canvas.getObjects().filter((obj) => obj.id === surface.parentId)[0].directionText, // 지붕면 // powerGeneration: wpOut.toLocaleString('ko-KR', { maximumFractionDigits: 4 }), wpOut: (wpOut / 1000).toFixed(3), } }) console.log('🚀 ~ getModuleStatistics ~ rows:', rows) console.log('🚀 ~ getModuleStatistics ~ moduleInfo:', moduleInfo) const header = [ { name: getMessage('modal.panel.batch.statistic.roof.shape'), prop: 'name' }, ...Object.keys(moduleInfo).map((key) => { return { name: moduleInfo[key].name, prop: key } }), { name: `${getMessage('modal.panel.batch.statistic.power.generation.amount')}(kW)`, prop: 'wpOut' }, ] let footer = [getMessage('modal.panel.batch.statistic.total')] let footerData = {} rows.forEach((row) => { Object.keys(moduleInfo).map((key) => { if (!footerData[key]) footerData[key] = 0 footerData[key] += row[key] }) }) Object.keys(footerData).forEach((key) => { footer.push(footerData[key]) }) footer.push((totalWpout / 1000).toFixed(3)) console.log({ header: header, rows, footer: footer }) setModuleStatistics({ header: header, rows, footer: footer }) } return { selectedModules, manualModuleSetup, autoModuleSetup, restoreModuleInstArea, manualFlatroofModuleSetup, autoFlatroofModuleSetup, checkModuleDisjointObjects, } }