qcast-front/src/hooks/module/useModuleBasicSetting.js
2025-02-05 16:57:45 +09:00

2508 lines
107 KiB
JavaScript

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,
}
}