import { useRecoilValue } from 'recoil' import { canvasState, currentAngleTypeSelector } from '@/store/canvasAtom' import { POLYGON_TYPE, TRESTLE_MATERIAL } from '@/common/common' import { moduleSelectionDataState } from '@/store/selectedModuleOptions' import { getDegreeByChon } from '@/util/canvas-util' import { v4 as uuidv4 } from 'uuid' import { useMasterController } from '@/hooks/common/useMasterController' import { basicSettingState, trestleDisplaySelector } from '@/store/settingAtom' import { useSwal } from '@/hooks/useSwal' import { useContext } from 'react' import { QcastContext } from '@/app/QcastProvider' import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle' // 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주 const MODULE_MARGIN = 10 // 회로 및 가대설정 export const useTrestle = () => { const canvas = useRecoilValue(canvasState) const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터 const { getQuotationItem } = useMasterController() const currentAngleType = useRecoilValue(currentAngleTypeSelector) const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet const isTrestleDisplay = useRecoilValue(trestleDisplaySelector) const { swalFire } = useSwal() const { setIsGlobalLoading } = useContext(QcastContext) const { getSelectedPcsItemList } = useCircuitTrestle() const { resetCircuits } = useCircuitTrestle() const apply = () => { const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit) if (notAllocationModules.length > 0) { swalFire({ text: '回路番号が設定されていないモジュールがあります。 番号を設定しなおすか、 パネルを削除してください。', icon: 'error' }) setIsGlobalLoading(false) return } try { //처마력바가 체크되어 있는 경우 exposedBottomPoints를 이용해 처마력바 그려줘야함. // exposedBottomPoints는 노출 최하면 들의 centerPoint 배열. const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) // 기존 eaveBar를 삭제 canvas.getObjects().forEach((obj) => { if (obj.name === 'eaveBar' || obj.name === 'rack' || obj.name === 'halfEaveBar' || obj.name === 'smartRack') { canvas.remove(obj) } }) canvas.getObjects().forEach((obj) => { if (obj.name === 'bracket') { canvas.remove(obj) } }) surfaces.forEach((surface) => { const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId) const roofMaterialIndex = parent.roofMaterial.index if (+roofSizeSet === 3) { return } const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction if (!construction) { swalFire({ text: 'construction 존재안함', icon: 'error' }) return } const plvrYn = construction.plvrYn let moduleRowsTotCnt = 0 let isEaveBar = construction.setupCover let isSnowGuard = construction.setupSnowCover const direction = parent.direction const rack = surface.trestleDetail.rack let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn, lessSupFitIntvlPct, lessSupFitQty } = surface.trestleDetail if (!rack && lessSupFitIntvlPct === 0 && lessSupFitQty === 0) { //25/02/06 가대없음의 경우 랙정보가 없음 return } let rackInfos = [] if (rack) { rackInfos = Object.keys(rack).map((key) => { return { key, value: rack[key] } }) } // 모듈들의 centerPoint들을 이용해 각 모듈의 정보(가장 아랫라인 모듈, 가장 윗라인 모듈, 접면, 반접면 등 계산) const result = calculateForApi(surface) if (!result) { return } const centerPoints = result.centerPoints const exposedBottomModules = [] // 아래 두면이 모두 노출 되어있는 경우 const leftExposedHalfBottomModules = [] // 왼쪽 면만 노출되어있는 경우 const rightExposedHalfBottomPoints = [] // 오른쪽 면만 노출되어 있는 경우 const leftExposedHalfTopModules = [] // 왼쪽 면만 노출되어 있는 경우 const rightExposedHalfTopPoints = [] // 오른쪽 면만 노출되어 있는 경우 const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE) modules.forEach((module) => { const { x, y } = module.getCenterPoint() const isExposedBottom = result.exposedBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2) const isLeftExposedHalfBottom = result.leftExposedHalfBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2) const isRightExposedHalfBottom = result.rightExposedHalfBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2) const isRightExposedHalfTop = result.rightExposedHalfTopPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2) const isLeftExposedHalfTop = result.leftExposedHalfTopPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2) if (isExposedBottom) { exposedBottomModules.push(module) } if (isLeftExposedHalfBottom) { leftExposedHalfBottomModules.push(module) } if (isRightExposedHalfBottom) { rightExposedHalfBottomPoints.push(module) } if (isRightExposedHalfTop) { leftExposedHalfTopModules.push(module) } if (isLeftExposedHalfTop) { rightExposedHalfTopPoints.push(module) } }) // 4개중 한개라도 있는 경우 치조배치로 간주한다. const isChidory = leftExposedHalfBottomModules.length > 0 || rightExposedHalfBottomPoints.length > 0 || leftExposedHalfTopModules.length > 0 || rightExposedHalfTopPoints.length > 0 surface.isChidory = isChidory if (plvrYn === 'N' && isChidory) { swalFire({ text: '치조불가공법입니다.', icon: 'error' }) clear() throw new Error('치조불가공법입니다.') } surface.set({ isChidory: isChidory }) canvas .getObjects() .filter((obj) => ['eaveBar', 'halfEaveBar'].includes(obj.name) && obj.parentId === surface.id) .forEach((obj) => { canvas.remove(obj) }) if (isEaveBar) { // 처마력바설치 true인 경우 설치 exposedBottomModules.forEach((module) => { const bottomPoints = findTopTwoPoints([...module.getCurrentPoints()], direction) if (!bottomPoints) return const eaveBar = new fabric.Line([bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y], { parent: surface, name: TRESTLE_MATERIAL.EAVE_BAR, stroke: 'blue', strokeWidth: 4, selectable: false, surfaceId: surface.id, parentId: module.id, visible: isTrestleDisplay, }) canvas.add(eaveBar) canvas.renderAll() }) if (isChidory && cvrPlvrYn === 'Y') { leftExposedHalfBottomModules.forEach((module) => { const bottomPoints = findTopTwoPoints([...module.getCurrentPoints()], direction) let barPoints = [] //설치해야할 반처마커버 포인트를 방향에 따라 설정 if (direction === 'south') { barPoints = [bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x - module.width / 2, bottomPoints[1].y] } else if (direction === 'north') { barPoints = [bottomPoints[0].x + module.width / 2, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y] } else if (direction === 'east') { barPoints = [bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[0].y - module.height / 2] } else if (direction === 'west') { barPoints = [bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y - module.height / 2] } if (!bottomPoints) return const halfEaveBar = new fabric.Line(barPoints, { parent: surface, name: TRESTLE_MATERIAL.HALF_EAVE_BAR, stroke: 'blue', strokeWidth: 4, selectable: false, surfaceId: surface.id, parentId: module.id, visible: isTrestleDisplay, }) canvas.add(halfEaveBar) canvas.renderAll() }) rightExposedHalfBottomPoints.forEach((module) => { const bottomPoints = findTopTwoPoints([...module.points], direction) let barPoints = [] //설치해야할 반처마커버 포인트를 방향에 따라 설정 if (direction === 'south') { barPoints = [bottomPoints[0].x + module.width / 2, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y] } else if (direction === 'north') { barPoints = [bottomPoints[0].x, bottomPoints[0].y, bottomPoints[0].x + module.width / 2, bottomPoints[1].y] } else if (direction === 'east') { barPoints = [bottomPoints[0].x, bottomPoints[1].y + module.height / 2, bottomPoints[1].x, bottomPoints[1].y] } else if (direction === 'west') { barPoints = [bottomPoints[0].x, bottomPoints[1].y - module.height / 2, bottomPoints[1].x, bottomPoints[1].y] } if (!bottomPoints) return const halfEaveBar = new fabric.Line(barPoints, { parent: surface, name: TRESTLE_MATERIAL.HALF_EAVE_BAR, stroke: 'blue', strokeWidth: 4, selectable: false, parentId: module.id, visible: isTrestleDisplay, }) canvas.add(halfEaveBar) canvas.renderAll() }) } } const horizontal = ['south', 'north'].includes(direction) ? surface.trestleDetail.moduleIntvlHor : surface.trestleDetail.moduleIntvlVer const vertical = ['south', 'north'].includes(direction) ? surface.trestleDetail.moduleIntvlVer : surface.trestleDetail.moduleIntvlHor let mostRowsModule = 0 // 모듈 최대 단 수 // 가대 설치를 위한 가장 아래 모듈로부터 위로 몇단인지 계산 // 오른쪽,왼쪽 둘 다 아래에 아무것도 없는, 처마 커버를 필요로 하는 모듈 exposedBottomModules.forEach((module) => { let { width, height } = { ...module } width = Math.floor(width) height = Math.floor(height) let { x: startX, y: startY } = { ...module.getCenterPoint() } let { x, y } = { ...module.getCenterPoint() } let leftRows = 1 let rightRows = 1 let centerRows = 1 let hasNextModule = true let findLeft = true let findRight = true let leftFindModuleList = [module] let rightFindModuleList = [module] let centerFindModuleList = [module] //우선 절반을 나눈 뒤 왼쪽부터 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ leftFindModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. if (findLeft) { nextModule = findNextLeftModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findLeft = false } else { nextModule = findNextRightModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findLeft = true } if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ leftFindModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } hasNextModule = true x = startX y = startY // 오른쪽 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. rightRows++ rightFindModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. if (findRight) { nextModule = findNextRightModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findRight = false } else { nextModule = findNextLeftModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findRight = true } if (nextModule) { // 바로 위 모듈을 찾는다. rightRows++ rightFindModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } hasNextModule = true x = startX y = startY // 센터 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. centerFindModuleList.push(nextModule) centerRows++ x = nextModule.x y = nextModule.y } else { hasNextModule = false } } const leftModuleInfos = leftFindModuleList.map((module) => { return { moduleTpCd: module.moduleInfo.moduleTpCd, } }) const rightModuleInfos = rightFindModuleList.map((module) => { return { moduleTpCd: module.moduleInfo.moduleTpCd, } }) const centerModuleInfos = centerFindModuleList.map((module) => { return { moduleTpCd: module.moduleInfo.moduleTpCd, } }) const leftRowsInfo = moduleTransformData(leftModuleInfos) const rightRowsInfo = moduleTransformData(rightModuleInfos) const centerRowsInfo = moduleTransformData(centerModuleInfos) // 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다. const leftRacks = rackInfos.find((rack) => { if (leftRowsInfo.rowsInfo.length === 1) { return ( rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) ) } else { return ( rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count && Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count ) } })?.value.racks // 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다. const rightRacks = rackInfos.find((rack) => { if (rightRowsInfo.rowsInfo.length === 1) { return ( rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) ) } else { return ( rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count && Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count ) } })?.value.racks // 해당 rack으로 그려준다. const centerRacks = rackInfos.find((rack) => { if (centerRowsInfo.rowsInfo.length === 1) { return ( rack.value.moduleTpCd === centerRowsInfo.moduleTotalTp && rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) ) } else { return ( rack.value.moduleTpCd === centerRowsInfo.moduleTotalTp && rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && Number(rack.value.moduleTpRows1) === centerRowsInfo.rowsInfo[0].count && Number(rack.value.moduleTpRows2) === centerRowsInfo.rowsInfo[1].count ) } })?.value.racks mostRowsModule = Math.max(leftRows, rightRows, centerRows, mostRowsModule) if (rackYn === 'Y') { drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L', rackYn) drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R', rackYn) if (rackQty === 3) { //rack 갯수가 3개인 경우는 중간렉도 추가해줘야함 drawRacks(centerRacks, rackQty, rackIntvlPct, module, direction, 'C', rackYn) } else if (rackQty === 4) { drawRacks(leftRacks, rackQty, rackIntvlPct / 3, module, direction, 'L', rackYn) drawRacks(rightRacks, rackQty, rackIntvlPct / 3, module, direction, 'R', rackYn) } } module.set({ leftRows, rightRows, centerRows }) }) // 왼쪽아래에 모듈이 없는 모듈들 leftExposedHalfBottomModules.forEach((module) => { const { width, height } = module let { x: startX, y: startY } = { ...module.getCenterPoint() } let { x, y } = { ...module.getCenterPoint() } let leftRows = 1 let hasNextModule = true let findLeft = true let findModuleList = [module] //우선 절반을 나눈 뒤 왼쪽부터 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ findModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. if (findLeft) { nextModule = findNextLeftModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findLeft = false } else { nextModule = nextModule = findNextRightModule( { x, y, width, height, horizontal, vertical, }, centerPoints, direction, ) findLeft = true } if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ findModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } const leftModuleInfos = findModuleList.map((module) => { return { moduleTpCd: module.moduleInfo.moduleTpCd, } }) const leftRowsInfo = moduleTransformData(leftModuleInfos) // 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다. const leftRacks = rackInfos.find((rack) => { if (leftRowsInfo.rowsInfo.length === 1) { return ( rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) ) } else { return ( rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count && Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count ) } })?.value.racks mostRowsModule = Math.max(leftRows, mostRowsModule) if (rackYn === 'Y') { drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L', rackYn) } module.set({ leftRows }) }) // 오른쪽 아래에 모듈이 없는 모듈들 rightExposedHalfBottomPoints.forEach((module) => { const { width, height } = module let { x: startX, y: startY } = { ...module.getCenterPoint() } let { x, y } = { ...module.getCenterPoint() } let rightRows = 1 let hasNextModule = true let findRight = true let findModuleList = [module] // 오른쪽 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. rightRows++ findModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. if (findRight) { nextModule = findNextRightModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findRight = false } else { nextModule = findNextLeftModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) findRight = true } if (nextModule) { // 바로 위 모듈을 찾는다. rightRows++ findModuleList.push(nextModule) x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } const rightRowsInfos = findModuleList.map((module) => { return { moduleTpCd: module.moduleInfo.moduleTpCd, } }) const rightRowsInfo = moduleTransformData(rightRowsInfos) // 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다. const rightRacks = rackInfos.find((rack) => { if (rightRowsInfo.rowsInfo.length === 1) { return ( rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) ) } else { return ( rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count && Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count ) } })?.value.racks mostRowsModule = Math.max(rightRows, mostRowsModule) // 해당 rack으로 그려준다. if (rackYn === 'Y') { drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R', rackYn) } module.set({ rightRows }) }) surface.set({ moduleRowsTotCnt: mostRowsModule }) if (rackYn === 'N') { // rack이 없을경우 installBracketWithOutRack(surface, exposedBottomModules, leftExposedHalfBottomModules, rightExposedHalfBottomPoints, isChidory) } else if (rackYn === 'Y') { installBracket(surface) } const quotationParam = getTrestleParams(surface, exposedBottomModules) surface.set({ quotationParam }) }) return true } catch (e) { // 에러 발생시 가대 초기화 console.error(e) clear() setViewCircuitNumberTexts(true) setIsGlobalLoading(false) return false } } //module Rack 정보를 얻기위한 데이터 가공 function moduleTransformData(arr) { let counts = {} arr.forEach((item) => { counts[item.moduleTpCd] = (counts[item.moduleTpCd] || 0) + 1 }) let moduleTotalTp = Object.keys(counts).join('') let rowsInfo = Object.entries(counts).map(([moduleTpCd, count]) => ({ moduleTpCd, count })) return { moduleTotalTp, rowsInfo } } // itemList 조회 후 estimateParam에 저장 const getEstimateData = async () => { const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //surfaces.pcses들을 배열로 묶는다 const pcses = surfaces[0].pcses.filter((pcs) => pcs !== null && pcs !== undefined) surfaces.forEach((surface, index) => { if (index !== 0) { if (surface.pcses) { pcses.concat(surface.pcses) } } }) const allModules = surfaces.map((surface) => surface.modules).flat() // 모듈 파라미터 생성 const modules = getModulesParam(allModules) const trestles = [] //가대 파라미터 생성 surfaces.forEach((surface) => { if (surface.quotationParam) { trestles.push(surface.quotationParam) } }) // trestles 배열에서 null인 경우 제거 const params = { trestles, pcses, modules } //견적서 itemList 조회 const { data, data2, result } = await getQuotationItem(params) if (result.resultCode === 'E') { swalFire({ text: result.resultMsg, icon: 'error' }) return } let itemList if (!data || data.length === 0) { return } itemList = data //northArrangement 북면 설치 여부 const northArrangement = getNorthArrangement() // circuitItemList의 경우는 moduleList에서 circuitId만 groupBy한다. let circuitItemList = [] // roofSurfaceList 생성 const roofSurfaceList = surfaces.map((surface) => { const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId) const { directionText, roofMaterial, moduleCompass, surfaceCompass } = parent const slope = Number(roofMaterial.pitch) const roofMaterialIndex = parent.roofMaterial.index const { nameJp: roofMaterialIdMulti } = roofMaterial const moduleSelection = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex) let construction = moduleSelection?.construction let constructSpecification = +roofSizeSet === 3 ? null : construction.constTp let constructSpecificationMulti = +roofSizeSet === 3 ? null : construction.constTpJp const trestle = moduleSelection?.trestle let trestleMkrCd = +roofSizeSet === 3 ? null : trestle.trestleMkrCd let supportMethodId = +roofSizeSet === 3 ? null : trestle.constMthdCd let roofBaseCd = +roofSizeSet === 3 ? null : trestle.roofBaseCd let supportMeaker = +roofSizeSet === 3 ? null : trestle.trestleMkrCdJp let supportMethodIdMulti = +roofSizeSet === 3 ? null : trestle.constMthdCdJp const modules = surface.modules const moduleList = modules.map((module) => { circuitItemList.push(module.pcsItemId) return { itemId: module.moduleInfo.itemId, circuit: module.circuitNumber, pcItemId: module.pcsItemId, } }) return { roofSurfaceId: surface.id, roofSurface: directionText.replace(/[0-9]/g, ''), roofMaterialId: +roofSizeSet === 3 ? '陸屋根' : roofMaterial.roofMatlCd, // 육지붕의 경우 지붕재 ID 값이 없기 때문에 임의 값 지정 supportMethodId, constructSpecification, constructSpecificationMulti, roofMaterialIdMulti: +roofSizeSet === 3 ? '陸屋根' : roofMaterialIdMulti, supportMethodIdMulti, supportMeaker, slope: +roofSizeSet === 3 ? 0 : slope, classType: currentAngleType === 'slope' ? '0' : '1', angle: +roofSizeSet === 3 ? 0 : getDegreeByChon(slope), azimuth: getAzimuth(parent), moduleList, } }) // circuitItemList 중복제거 circuitItemList = circuitItemList.filter((item, index) => circuitItemList.indexOf(item) === index) circuitItemList = getSelectedPcsItemList() return { itemList, northArrangement, roofSurfaceList, circuitItemList } } // 발전 시뮬레이션 용 각도 재계산 const getAzimuth = (parent) => { const { moduleCompass, surfaceCompass, direction } = parent if (surfaceCompass) { if (surfaceCompass > 180) { return surfaceCompass - 360 } return surfaceCompass } switch (direction) { case 'south': { if (moduleCompass < 0) { return -1 * moduleCompass } else if (moduleCompass === 0) { return 0 } else if (moduleCompass < 180) { return -1 * moduleCompass } else if (moduleCompass === 180) { return 180 } } case 'north': { if (moduleCompass < 0) { return -1 * (180 + moduleCompass) } else if (moduleCompass === 0) { return 180 } else if (moduleCompass < 180) { return 180 - moduleCompass } else if (moduleCompass === 180) { return 0 } } case 'west': { if (moduleCompass > -180 && moduleCompass < -90) { return -180 - (90 + moduleCompass) } else if (moduleCompass < 0) { return 180 - (90 + moduleCompass) } else if (moduleCompass === 0) { return 90 } else if (moduleCompass < 180) { return 90 - moduleCompass } else if (moduleCompass === 180) { return -90 } } case 'east': { if (moduleCompass < 0) { return -(90 + moduleCompass) } else if (moduleCompass === 0) { return -90 } else if (moduleCompass < 90) { return -180 + (90 - moduleCompass) } else if (moduleCompass < 180) { return 180 + (90 - moduleCompass) } else if (moduleCompass === 180) { return 90 } } } return 0 } // 북면설치가 한개라도 되어있는지 확인 const getNorthArrangement = () => { const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) let northArrangement = '0' surfaces.forEach((surface) => { const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId) const directionText = parent.directionText // ['西北西','東北東'] 의 경우를 제외하고는 北이 들어간 경우 전부 북면으로 간주 if (directionText.includes('北') && !directionText.includes('西北西') && !directionText.includes('東北東')) { if (surface.modules.length > 0) { northArrangement = '1' } } }) return northArrangement } // 다음 윗 모듈을 찾는다. const findNextModule = (currentPoint, centerPoints, direction) => { let { x, y, horizontal, vertical } = { ...currentPoint } let { widthArr, heightArr } = centerPoints[0] let width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length + horizontal let height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length + vertical let maxX = MODULE_MARGIN let maxY = MODULE_MARGIN let result switch (direction) { case 'south': { heightArr.forEach((height) => { if (result) return result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) }) break } case 'north': { heightArr.forEach((height) => { if (result) return result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) }) break } case 'east': { widthArr.forEach((width) => { if (result) return result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) }) break } case 'west': { widthArr.forEach((width) => { if (result) return result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + width)) < maxX && Math.abs(centerPoint.y - y) < maxY) }) break } } return result } // 다음 왼쪽 모듈을 찾는다. const findNextLeftModule = (currentPoint, centerPoints, direction) => { let { x, y, horizontal, vertical } = { ...currentPoint } let { widthArr, heightArr } = centerPoints[0] let width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length + horizontal let height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length + vertical let result let topLeftPoint let maxX = MODULE_MARGIN let maxY = MODULE_MARGIN switch (direction) { case 'south': { width = width + horizontal height = height + vertical heightArr.forEach((h) => { topLeftPoint = { x: x - width / 2, y: y - h } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ) }) break } case 'north': { width = width + horizontal height = height + vertical heightArr.forEach((h) => { topLeftPoint = { x: x + width / 2, y: y + h } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ) }) break } case 'east': { widthArr.forEach((w) => { topLeftPoint = { x: x - w, y: y + height / 2 } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ) }) break } case 'west': { widthArr.forEach((w) => { topLeftPoint = { x: x + w, y: y - height / 2 } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ) }) break } } return result } // 다음 오른쪽 모듈을 찾는다. const findNextRightModule = (currentPoint, centerPoints, direction) => { let { x, y, horizontal, vertical } = { ...currentPoint } let { widthArr, heightArr } = centerPoints[0] let width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length + horizontal let height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length + vertical let result let topRightPoint let maxX = MODULE_MARGIN let maxY = MODULE_MARGIN switch (direction) { case 'south': { heightArr.forEach((h) => { topRightPoint = { x: x + width / 2, y: y - h } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ) }) break } case 'north': { heightArr.forEach((h) => { topRightPoint = { x: x - width / 2, y: y + h } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ) }) break } case 'east': { widthArr.forEach((w) => { topRightPoint = { x: x - w, y: y - height / 2 } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ) }) break } case 'west': { widthArr.forEach((w) => { topRightPoint = { x: x + w, y: y + height / 2 } if (result) return result = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ) }) break } } return result } // rack을 그린다. const drawRacks = (rackInfos, rackQty, rackIntvlPct, module, direction, l, rackYn) => { const { width, height, left, top, lastX, lastY, surfaceId } = module const surface = canvas.getObjects().find((obj) => obj.id === surfaceId) if (!rackInfos) { const maxRows = surface.trestleDetail.moduleMaxRows const maxCols = surface.trestleDetail.moduleMaxCols const msg = `選択した家で設置可能 モジュールの最大段数は${maxRows}、最大列数は${maxCols}です。 上限より上部に取り付けたモジュールを削除してください。` swalFire({ title: msg, type: 'alert' }) throw new Error('rackInfos is null') } const roof = canvas.getObjects().find((obj) => obj.id === surface.parentId) const degree = getDegreeByChon(roof.roofMaterial.pitch) const moduleLeft = lastX ?? left const moduleTop = lastY ?? top let startPointX, startPointY switch (l) { case 'L': { // 왼쪽부분 시작 점 if (direction === 'south') { startPointX = module.left + width / rackIntvlPct startPointY = module.top + module.height break } else if (direction === 'east') { startPointX = module.left + width startPointY = module.top + height - height / rackIntvlPct break } else if (direction === 'west') { startPointX = module.left startPointY = module.top + height / rackIntvlPct break } else if (direction === 'north') { startPointX = module.left + width - width / rackIntvlPct startPointY = module.top break } } case 'R': { // 오른쪽부분 시작 점 if (direction === 'south') { startPointX = module.left + module.width - width / rackIntvlPct startPointY = module.top + module.height / 2 + height / 2 break } else if (direction === 'east') { startPointX = module.left + width startPointY = module.top + height / rackIntvlPct break } else if (direction === 'west') { startPointX = module.left startPointY = module.top + height - height / rackIntvlPct break } else if (direction === 'north') { startPointX = module.left + width / rackIntvlPct startPointY = module.top break } } case 'C': { // 중간부분 시작점 if (direction === 'south') { const x = module.left + module.width / 2 const y = module.top + module.height / 2 startPointX = x startPointY = y + height / 2 break } else if (direction === 'east') { const x = module.left + width const y = module.top + module.height / 2 startPointX = x startPointY = y break } else if (direction === 'west') { const x = module.left const y = module.top + module.height / 2 startPointX = x startPointY = y break } else if (direction === 'north') { const x = module.left + module.width / 2 const y = module.top startPointX = x startPointY = y break } } } switch (direction) { case 'south': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct, rackRowsCd, smartRack, smartRackYn } = rackInfo let rackLength = getTrestleLength(rackLen, degree) / 10 if (smartRackYn === 'Y') { let smartRackId = uuidv4() smartRack.forEach(({ seq, setRackTpCd, setRackTpLen, supFitQty }) => { rackLength = getTrestleLength(setRackTpLen, degree) / 10 if (setRackTpCd === 'RACK') { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY - rackLength], { name: TRESTLE_MATERIAL.SMART_RACK, stroke: 'red', strokeWidth: 4, selectable: true, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, visible: isTrestleDisplay, parentId: module.id, surfaceId: surface.id, supFitQty, supFitIntvlPct, rackLen: rackLength, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'top', }) startPointY -= rackLength + 8 canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointY -= rackLength } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY - rackLength], { name: TRESTLE_MATERIAL.RACK, stroke: 'red', strokeWidth: 4, selectable: false, fill: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, rackId: itemId, direction: 'top', }) canvas.add(rack) canvas.renderAll() } startPointY -= rackLength + 8 }) break } case 'east': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct, rackRowsCd, smartRack, smartRackYn } = rackInfo let rackLength = getTrestleLength(rackLen, degree) / 10 if (smartRackYn === 'Y') { let smartRackId = uuidv4() smartRack.forEach(({ seq, setRackTpCd, setRackTpLen, supFitQty }) => { rackLength = getTrestleLength(setRackTpLen, degree) / 10 if (setRackTpCd === 'RACK') { const rack = new fabric.Line([startPointX, startPointY, startPointX - rackLength, startPointY], { name: TRESTLE_MATERIAL.SMART_RACK, stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, supFitQty, supFitIntvlPct, rackLen: rackLength, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'left', }) startPointX -= rackLength + 8 canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointX -= rackLength } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX - rackLength, startPointY], { name: TRESTLE_MATERIAL.RACK, stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, strokeWidth: 4, selectable: false, visible: isTrestleDisplay, supFitQty, supFitIntvlPct, rackLen, rackYn, rackRowsCd, rackId: itemId, direction: 'left', }) canvas.add(rack) canvas.renderAll() } startPointX -= rackLength + 8 }) break } case 'west': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct, rackRowsCd, smartRack, smartRackYn } = rackInfo let rackLength = getTrestleLength(rackLen, degree) / 10 if (smartRackYn === 'Y') { let smartRackId = uuidv4() smartRack.forEach(({ seq, setRackTpCd, setRackTpLen, supFitQty }) => { rackLength = getTrestleLength(setRackTpLen, degree) / 10 if (setRackTpCd === 'RACK') { const rack = new fabric.Line([startPointX, startPointY, startPointX + rackLength, startPointY], { name: TRESTLE_MATERIAL.SMART_RACK, stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, supFitQty, supFitIntvlPct, rackLen: rackLength, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'right', }) startPointX += rackLength + 8 canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointX += rackLength } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX + rackLength, startPointY], { name: TRESTLE_MATERIAL.RACK, stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, rackId: itemId, direction: 'right', }) canvas.add(rack) canvas.renderAll() } startPointX += rackLength + 8 }) break } case 'north': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct, rackRowsCd, smartRack, smartRackYn } = rackInfo let rackLength = getTrestleLength(rackLen, degree) / 10 if (smartRackYn === 'Y') { let smartRackId = uuidv4() smartRack.forEach(({ seq, setRackTpCd, setRackTpLen, supFitQty }) => { rackLength = getTrestleLength(setRackTpLen, degree) / 10 if (setRackTpCd === 'RACK') { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY + rackLength], { name: TRESTLE_MATERIAL.SMART_RACK, stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'bottom', }) canvas.add(rack) canvas.renderAll() startPointY += rackLength + 8 } else if (setRackTpCd === 'INTVL') { startPointY += rackLength } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY + rackLength], { name: TRESTLE_MATERIAL.RACK, stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, visible: isTrestleDisplay, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, rackId: itemId, direction: 'bottom', }) canvas.add(rack) canvas.renderAll() } startPointY += rackLength + 8 }) break } } } //파라미터로 받은 배치면의 rack을 가지고 지지금구를 그린다. const installBracket = (surface) => { const modules = surface.modules const racks = [] modules.forEach((module) => { canvas .getObjects() .filter((obj) => obj.name === 'rack' || obj.name === 'smartRack') .forEach((rack) => { if (rack.parentId === module.id) { canvas.remove(canvas.getObjects().filter((obj) => obj.name === 'bracket' && obj.parentId === rack.id)) racks.push(rack) } }) }) canvas.renderAll() racks.forEach((rack) => { let { x1, y1, x2, y2, direction, supFitQty, supFitIntvlPct, rackLen, name } = rack const bracketLength = 10 if (direction === 'top') { const result = getBracketPoints(supFitQty, supFitIntvlPct) result.forEach((percent) => { const len = name === 'smartRack' ? y2 - (y2 - y1) * percent - bracketLength / 2 : y2 - (y2 - y1) * percent - bracketLength / 2 const bracket = new fabric.Rect({ left: x2 - bracketLength / 3, top: len, fill: 'green', name: TRESTLE_MATERIAL.BRACKET, parentId: rack.parentId, visible: isTrestleDisplay, surfaceId: surface.id, width: bracketLength, height: bracketLength, selectable: false, }) canvas.add(bracket) }) canvas.renderAll() } else if (direction === 'left') { const result = getBracketPoints(supFitQty, supFitIntvlPct) result.forEach((percent) => { const len = name === 'smartRack' ? x1 - Math.abs(x2 - x1) * percent - bracketLength / 2 : x1 - Math.abs(x2 - x1) * percent - bracketLength / 2 const bracket = new fabric.Rect({ left: len, top: y2 - bracketLength / 3, fill: 'green', name: TRESTLE_MATERIAL.BRACKET, parentId: rack.parentId, visible: isTrestleDisplay, surfaceId: surface.id, width: bracketLength, height: bracketLength, selectable: false, }) canvas.add(bracket) }) canvas.renderAll() } else if (direction === 'right') { const result = getBracketPoints(supFitQty, supFitIntvlPct) result.forEach((percent) => { const len = name === 'smartRack' ? x2 - Math.abs(x2 - x1) * percent - bracketLength / 2 : x2 - Math.abs(x2 - x1) * percent - bracketLength / 2 const bracket = new fabric.Rect({ left: len, top: y2 - bracketLength / 3, fill: 'green', parentId: rack.parentId, visible: isTrestleDisplay, surfaceId: surface.id, name: TRESTLE_MATERIAL.BRACKET, width: bracketLength, height: bracketLength, selectable: false, }) canvas.add(bracket) }) canvas.renderAll() } else if (direction === 'bottom') { const result = getBracketPoints(supFitQty, supFitIntvlPct) result.forEach((percent) => { const len = name === 'smartRack' ? y2 - (y2 - y1) * percent - bracketLength / 2 : y2 - (y2 - y1) * percent - bracketLength / 2 const bracket = new fabric.Rect({ left: x2 - bracketLength / 3, top: len, fill: 'green', name: TRESTLE_MATERIAL.BRACKET, parentId: rack.parentId, visible: isTrestleDisplay, surfaceId: surface.id, width: bracketLength, height: bracketLength, selectable: false, }) canvas.add(bracket) }) canvas.renderAll() } }) } //랙 없음 인 경우 지지금구 설치 const installBracketWithOutRack = (surface, exposedBottomModules, leftExposedHalfBottomModules, rightExposedHalfBottomPoints, isChidory) => { let { rackQty, rackIntvlPct, moduleIntvlHor, moduleIntvlVer, lessSupFitQty, lessSupFitIntvlPct } = surface.trestleDetail rackQty = lessSupFitQty rackIntvlPct = lessSupFitIntvlPct const direction = surface.direction canvas.renderAll() exposedBottomModules.forEach((module) => { canvas.renderAll() drawBracketWithOutRack(module, rackIntvlPct, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) drawBracketWithOutRack(module, rackIntvlPct, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) if (!isChidory && rackQty === 3) { // 치도리가 아니면서 갯수가 3개인 경우 센터도 설치 필요함 drawBracketWithOutRack(module, rackIntvlPct, module.centerRows + 1, 'C', surface.direction, moduleIntvlHor, moduleIntvlVer) } if (isChidory && rackQty === 3) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) } if (rackQty === 4) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) } if (!isChidory && rackQty === 5) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'C', surface.direction, moduleIntvlHor, moduleIntvlVer) } }) leftExposedHalfBottomModules.forEach((module) => { drawBracketWithOutRack(module, rackIntvlPct, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) if (rackQty === 3 && !findSamePointInBottom(exposedBottomModules, module)) { // 해당 모듈과 같은 위치 맨 아래에 모듈이 없는 경우 하나 더 설치 필요 drawBracketWithOutRack(module, rackIntvlPct / 3, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) } if (rackQty === 4) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.leftRows + 1, 'L', surface.direction, moduleIntvlHor, moduleIntvlVer) } }) rightExposedHalfBottomPoints.forEach((module) => { drawBracketWithOutRack(module, rackIntvlPct, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) if (rackQty === 3 && !findSamePointInBottom(exposedBottomModules, module, direction)) { // 해당 모듈과 같은 위치 맨 아래에 모듈이 없는 경우 하나 더 설치 필요 drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) } if (rackQty === 4) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) } }) } // 방향에 따라 가장 아래모듈중 같은 좌표가 있는지 확인 const findSamePointInBottom = (exposedBottomModules, module, direction) => { const { x, y } = module.getCenterPoint() switch (direction) { case 'south': { return exposedBottomModules.find((exposedBottomModule) => { const { x: exposedX, y: exposedY } = exposedBottomModule.getCenterPoint() return Math.abs(x - exposedX) < 2 && Math.abs(y - exposedY) > module.height }) } case 'north': { return exposedBottomModules.find((exposedBottomModule) => { const { x: exposedX, y: exposedY } = exposedBottomModule.getCenterPoint() return Math.abs(x - exposedX) < 2 && Math.abs(y - exposedY) > module.height }) } case 'east': { return exposedBottomModules.find((exposedBottomModule) => { const { x: exposedX, y: exposedY } = exposedBottomModule.getCenterPoint() return Math.abs(y - exposedY) < 2 && Math.abs(x - exposedX) > module.width }) } case 'west': { return exposedBottomModules.find((exposedBottomModule) => { const { x: exposedX, y: exposedY } = exposedBottomModule.getCenterPoint() return Math.abs(y - exposedY) < 2 && Math.abs(x - exposedX) > module.width }) } } } // 랙 없음의 지지금구를 그린다. const drawBracketWithOutRack = (module, rackIntvlPct, count, l, direction, moduleIntvlHor, moduleIntvlVer) => { let { width, height, left, top } = module let startPointX let startPointY switch (l) { case 'L': { // 왼쪽부분 시작 점 if (direction === 'south') { startPointX = left + width / rackIntvlPct startPointY = top + height break } else if (direction === 'east') { startPointX = left + width startPointY = top + height - height / rackIntvlPct break } else if (direction === 'west') { startPointX = left startPointY = top + height / rackIntvlPct break } else if (direction === 'north') { startPointX = left + width - width / rackIntvlPct startPointY = top break } } case 'R': { // 오른쪽부분 시작 점 if (direction === 'south') { startPointX = left + width - width / rackIntvlPct startPointY = top + height / 2 + height / 2 break } else if (direction === 'east') { startPointX = left + width startPointY = top + height / rackIntvlPct break } else if (direction === 'west') { startPointX = left startPointY = top + height - height / rackIntvlPct break } else if (direction === 'north') { startPointX = left + width / rackIntvlPct startPointY = top break } } case 'C': { // 중간부분 시작점 if (direction === 'south') { const x = left + width / 2 const y = top + height / 2 startPointX = x startPointY = y + height / 2 break } else if (direction === 'east') { const x = left + width const y = top + height / 2 startPointX = x startPointY = y break } else if (direction === 'west') { const x = left const y = top + height / 2 startPointX = x startPointY = y break } else if (direction === 'north') { const x = left + width / 2 const y = top startPointX = x startPointY = y break } } } for (let i = 0; i < count; i++) { const bracket = new fabric.Rect({ left: startPointX - 5, top: startPointY - 5, fill: 'green', name: TRESTLE_MATERIAL.BRACKET, parentId: module.id, surfaceId: module.surfaceId, width: 10, height: 10, selectable: false, visible: isTrestleDisplay, }) canvas.add(bracket) canvas.renderAll() if (direction === 'south') { startPointY -= height } else if (direction === 'north') { startPointY += height } else if (direction === 'east') { startPointX -= width } else if (direction === 'west') { startPointX += width } } } const getBracketPoints = (n, percent) => { if (n < 2) { return [] } const points = [] const topY = percent / 100 // 맨 위의 점의 y 좌표 (10%) const bottomY = 1 - percent / 100 // 맨 아래의 점의 y 좌표 (90%) // 맨 위의 점 추가 points.push(topY) // 중간 점들 추가 const interval = (bottomY - topY) / (n - 1) for (let i = 1; i < n - 1; i++) { points.push(topY + i * interval) } // 맨 아래의 점 추가 points.push(bottomY) return points } function findTopTwoPoints(points, direction) { switch (direction) { case 'south': points.sort((a, b) => b.y - a.y) break case 'north': points.sort((a, b) => a.y - b.y) break case 'east': points.sort((a, b) => b.x - a.x) break case 'west': points.sort((a, b) => a.x - b.x) break } return points.slice(0, 2) } const getCenterPoints = (moduleSurface) => { const centerPoints = [] let widthArr = [] let heightArr = [] const modules = moduleSurface.modules modules.forEach((module, index) => { module.tempIndex = index const { x, y } = module.getCenterPoint() const { width, height } = { ...module } widthArr.push(width) heightArr.push(height) centerPoints.push({ x, y, width: Math.floor(width), height: Math.floor(height), index, moduleInfo: module.moduleInfo }) }) //widthArr 중복 제거 1이상 차이가 나지 않으면 같은 너비로 간주 widthArr = removeCloseValues(Array.from(new Set(widthArr))) heightArr = removeCloseValues(Array.from(new Set(heightArr))) const widthAvg = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length const heightAvg = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length widthArr.push(widthAvg) heightArr.push(heightAvg) centerPoints.forEach((centerPoint, index) => { // widthArr의 평균값을 추가한다. centerPoint.widthArr = [...widthArr] centerPoint.heightArr = [...heightArr] }) function removeCloseValues(arr, threshold = 1) { arr.sort((a, b) => a - b) // 배열을 정렬 let result = [] for (let i = 0; i < arr.length; i++) { if (i === 0 || Math.abs(arr[i] - arr[i - 1]) >= threshold) { result.push(arr[i]) } } return result } return centerPoints } const calculateForApi = (moduleSurface) => { const centerPoints = getCenterPoints(moduleSurface) const direction = moduleSurface.direction const modules = moduleSurface.modules const horizontal = ['south', 'north'].includes(direction) ? moduleSurface.trestleDetail.moduleIntvlHor : moduleSurface.trestleDetail.moduleIntvlVer const vertical = ['south', 'north'].includes(direction) ? moduleSurface.trestleDetail.moduleIntvlVer : moduleSurface.trestleDetail.moduleIntvlHor const maxX = MODULE_MARGIN const maxY = MODULE_MARGIN if (centerPoints.length === 0) return //완전 노출 하면 let exposedBottom = 0 // 반 노출 하면 let exposedHalfBottom = 0 // 완전 노출 상면 let exposedTop = 0 //반 노출 상면 let exposedHalfTop = 0 // 완전 접면 let touchDimension = 0 //반접면 let halfTouchDimension = 0 // 노출bottom면의 points let exposedBottomPoints = [] // 반노출 bottom면의 points let leftExposedHalfBottomPoints = [] let rightExposedHalfBottomPoints = [] let leftExposedHalfTopPoints = [] let rightExposedHalfTopPoints = [] centerPoints.forEach((centerPoint, index) => { let { x, y, width, height, widthArr, heightArr } = { ...centerPoint } // centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인 let bottomCell let bottomLeftPoint let bottomRightPoint let leftBottomCnt = 0 let rightBottomCnt = 0 switch (direction) { case 'south': //widthArr의 값을 전부 더한 후 widthArr의 길이로 나누어 평균값을 구한다. width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length + horizontal height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length + vertical heightArr.forEach((h) => { if (bottomCell) return bottomCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + h)) < maxY) if (leftBottomCnt === 0) { bottomLeftPoint = { x: x - width / 2, y: y + h } leftBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ).length } if (rightBottomCnt === 0) { bottomRightPoint = { x: x + width / 2, y: y + h } rightBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ).length } }) break case 'north': width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length + horizontal height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length + vertical heightArr.forEach((h) => { if (bottomCell) return bottomCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - h)) < maxY) if (leftBottomCnt === 0) { bottomLeftPoint = { x: x + width / 2, y: y - h } leftBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ).length } if (rightBottomCnt === 0) { bottomRightPoint = { x: x - width / 2, y: y - h } rightBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ).length } }) break case 'east': widthArr.forEach((w) => { if (bottomCell) return bottomCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + w)) < maxX && Math.abs(centerPoint.y - y) < maxY) if (leftBottomCnt === 0) { bottomLeftPoint = { x: x + w, y: y + height / 2 } leftBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ).length } if (rightBottomCnt === 0) { bottomRightPoint = { x: x + w, y: y - height / 2 } rightBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ).length } }) break case 'west': widthArr.forEach((w) => { if (bottomCell) return bottomCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - w)) < maxX && Math.abs(centerPoint.y - y) < maxY) if (leftBottomCnt === 0) { bottomLeftPoint = { x: x - w, y: y - height / 2 } leftBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ).length } if (rightBottomCnt === 0) { bottomRightPoint = { x: x - w, y: y + height / 2 } rightBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ).length } }) break } if (bottomCell) { return } // 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다. if (leftBottomCnt + rightBottomCnt === 1) { exposedHalfBottom++ if (leftBottomCnt === 1) { rightExposedHalfBottomPoints.push(centerPoint) } if (rightBottomCnt === 1) { leftExposedHalfBottomPoints.push(centerPoint) } } else if (leftBottomCnt + rightBottomCnt === 0) { exposedBottomPoints.push(centerPoint) } }) // 노출상면 및 접면 체크 centerPoints.forEach((centerPoint, index) => { let { x, y, width, height, widthArr, heightArr } = { ...centerPoint } let topCell let topLeftPoint let topRightPoint let leftTopCnt = 0 let rightTopCnt = 0 switch (direction) { case 'south': width = width + horizontal height = height + vertical heightArr.forEach((h) => { if (topCell) return topCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + h)) < maxY) if (leftTopCnt === 0) { topLeftPoint = { x: x - width / 2, y: y - h } leftTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ).length } if (rightTopCnt === 0) { topRightPoint = { x: x + width / 2, y: y - h } rightTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ).length } }) break case 'north': height = height + vertical heightArr.forEach((h) => { if (topCell) return topCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - h)) < maxY) if (leftTopCnt === 0) { topLeftPoint = { x: x + width / 2, y: y + h } leftTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ).length } if (rightTopCnt === 0) { topRightPoint = { x: x - width / 2, y: y + h } rightTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ).length } }) break case 'east': width = width + horizontal widthArr.forEach((w) => { if (topCell) return topCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + w)) < maxX && Math.abs(centerPoint.y - y) < maxY) if (leftTopCnt === 0) { topLeftPoint = { x: x - w, y: y + height / 2 } leftTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ).length } if (rightTopCnt === 0) { topRightPoint = { x: x - w, y: y - height / 2 } rightTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ).length } }) break case 'west': width = width + horizontal widthArr.forEach((w) => { if (topCell) return topCell = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - w)) < maxX && Math.abs(centerPoint.y - y) < maxY) if (leftTopCnt === 0) { topLeftPoint = { x: x + w, y: y - height / 2 } leftTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ).length } if (rightTopCnt === 0) { topRightPoint = { x: x + w, y: y + height / 2 } rightTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ).length } }) break } if (topCell) { touchDimension++ return } if (leftTopCnt + rightTopCnt === 2) { touchDimension++ return } if (leftTopCnt + rightTopCnt === 1) { exposedHalfTop++ halfTouchDimension++ if (leftTopCnt === 1) { rightExposedHalfTopPoints.push(centerPoint) } if (rightTopCnt === 1) { leftExposedHalfTopPoints.push(centerPoint) } return } if (leftTopCnt + rightTopCnt === 0) { exposedTop++ } }) // 완전 노출 하면 계산 const groupPoints = groupCoordinates(centerPoints, modules[0], direction) groupPoints.forEach((group) => { let maxY = group.reduce((acc, cur) => (acc.y > cur.y ? acc : cur)).y let minY = group.reduce((acc, cur) => (acc.y < cur.y ? acc : cur)).y let maxX = group.reduce((acc, cur) => (acc.x > cur.x ? acc : cur)).x let minX = group.reduce((acc, cur) => (acc.x < cur.x ? acc : cur)).x let maxYCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.y - maxY) < 2) let minYCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.y - minY) < 2) let maxXCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.x - maxX) < 2) let minXCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.x - minX) < 2) switch (direction) { case 'south': exposedBottom += maxYCenterPoints.length break case 'north': exposedBottom += minYCenterPoints.length break case 'east': exposedBottom += maxXCenterPoints.length break case 'west': exposedBottom += minXCenterPoints.length break } }) return { exposedBottom, exposedHalfBottom, exposedTop, exposedHalfTop, touchDimension, halfTouchDimension, exposedBottomPoints, leftExposedHalfBottomPoints, rightExposedHalfBottomPoints, leftExposedHalfTopPoints, rightExposedHalfTopPoints, centerPoints, } } // polygon 내부 cell들의 centerPoint 배열을 그룹화 해서 반환 const groupCoordinates = (points, moduleExample, direction) => { const groups = [] const visited = new Set() const width = Math.floor(moduleExample.width) const height = Math.floor(moduleExample.height) const horizonPadding = 0 // 가로 패딩 const verticalPadding = 0 // 세로 패딩 function isAdjacent(p1, p2) { const dx = Math.abs(p1.x - p2.x) const dy = Math.abs(p1.y - p2.y) return ( (Math.abs(width + horizonPadding - dx) < 2 && dy < 2) || (dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 || (Math.abs(dx - width / 2 + horizonPadding / 2) < 2 && Math.abs(dy - height + verticalPadding) < 2) ) } function dfs(point, group) { visited.add(`${point.x},${point.y}`) group.push(point) for (const nextPoint of points) { const key = `${nextPoint.x},${nextPoint.y}` if (!visited.has(key) && isAdjacent(point, nextPoint)) { dfs(nextPoint, group) } } } for (const point of points) { const key = `${point.x},${point.y}` if (!visited.has(key)) { const group = [] dfs(point, group) groups.push(group) } } return groups } // 각도에 따른 길이 반환 function getTrestleLength(length, degree) { if (roofSizeSet !== 1) { // 복시도 입력이 아닌경우 그냥 길이 return return length } const radians = (degree * Math.PI) / 180 return length * Math.cos(radians) } // 견적서 아이템 조회 api parameter 생성 const getTrestleParams = (surface, exposedBottomModules) => { const result = calculateForApi(surface) const eaveBar = canvas.getObjects().filter((obj) => obj.surfaceId === surface.id && obj.name === 'eaveBar') const halfEaveBar = canvas.getObjects().filter((obj) => obj.surfaceId === surface.id && obj.name === 'halfEaveBar') const rackList = canvas.getObjects().filter((obj) => obj.surfaceId === surface.id && obj.name === 'rack') const smartRackList = canvas.getObjects().filter((obj) => obj.surfaceId === surface.id && obj.name === 'smartRack') // smartRackList을 smartRackId 기준으로 그룹화 한 배열 const smartRackGroup = smartRackList.reduce((acc, cur) => { if (!acc[cur.smartRackId]) { acc[cur.smartRackId] = [] } acc[cur.smartRackId].push(cur) return acc }, {}) const bracketList = canvas.getObjects().filter((obj) => obj.surfaceId === surface.id && obj.name === 'bracket') let rackParams = rackList.map((rack, index) => { return { seq: index, itemId: rack.rackId, rackFittingCnt: rack.supFitQty, rackRows: rack.rackRowsCd, } }) const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId) const roofMaterialIndex = parent.roofMaterial.index const moduleSelection = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex) const { common, module } = moduleSelectionData // const { constTp } = construction let { cvrPlvrYn, rackYn } = surface.trestleDetail const { trestleMkrCd, constMthdCd, roofBaseCd } = moduleSelection.trestle const { illuminationTp, instHt, stdSnowLd, stdWindSpeed } = common const { constTp } = moduleSelection.construction const { addRoof } = moduleSelection return { moduleTpCd: module.itemTp, roofMatlCd: parent.roofMaterial.roofMatlCd, mixMatlNo: module.mixMatlNo, raftBaseCd: addRoof.raft ?? addRoof.raftBaseCd, inclCd: addRoof.pitch, roofPitch: !addRoof.roofPchBase ? addRoof.roofPchBase : Number(addRoof.roofPchBase), exposedLowerBottomTotCnt: result.exposedBottom, // 노출 최하면 갯수 exposedHalfBottomTotCnt: result.exposedHalfBottom, // 노출 반하면 갯수 exposedTopTotCnt: result.exposedTop, // 노출 상면 총 수 exposedHalfTopTotCnt: result.exposedHalfTop, // 노출 반상면 총 수 exposedBottomTotCnt: result.exposedBottomPoints.length, // 노출 하면 수 touchedSurfaceTotCnt: result.touchDimension, // 접면 총 수 touchedHalfSurfaceTotCnt: result.halfTouchDimension, // 반접면 총 수 moduleTotCnt: surface.modules.length, // 모듈 총 수 moduleRowsTotCnt: surface.moduleRowsTotCnt, // 모듈 총 단 수 eavesTotCnt: eaveBar.length, eavesHalfTotCnt: halfEaveBar.length, rackYn, racks: rackParams, rackTotCnt: rackList.length ?? 0 + smartRackGroup.length ?? 0, rackFittingTotCnt: bracketList.length, moduleRows: getMostLeftModules(surface, exposedBottomModules), cvrYn: moduleSelection.construction.setupCover ? 'Y' : 'N', snowGdYn: moduleSelection.construction.setupSnowCover ? 'Y' : 'N', plvrYn: cvrPlvrYn, // modules: getModules(surface), // 2025-02-06 api 수정 trestleMkrCd, constMthdCd, roofBaseCd, illuminationTp, instHt, stdSnowLd, stdWindSpeed, constTp, } } // 전체 모듈 파라미터 생성 const getModulesParam = (allModules) => { const params = allModules.map((module, index) => { return { moduleTpCd: module.moduleInfo.itemTp, moduleItemId: module.moduleInfo.itemId, } }) //params를 moduleTpCd, moduleItemId로 그룹화 하면서 cnt를 계산 const groupedParams = params.reduce((acc, cur) => { const key = `${cur.moduleTpCd}-${cur.moduleItemId}` if (!acc[key]) { acc[key] = { moduleTpCd: cur.moduleTpCd, moduleItemId: cur.moduleItemId, cnt: 0, } } acc[key].cnt++ return acc }, {}) // map 형태의 데이터를 배열로 변환 return Object.values(groupedParams).map((groupedParam, index) => { return { moduleTpCd: groupedParam.moduleTpCd, moduleItemId: groupedParam.moduleItemId, moduleCnt: groupedParam.cnt, } }) } // 키값의 차이가 2이하인 경우 한개로 합침 function mergeCloseKeys(data) { // 키 값을 숫자로 변환 후 정렬 const keys = Object.keys(data) .map(parseFloat) .sort((a, b) => a - b) const result = {} let baseKey = keys[0] result[baseKey.toString()] = [...data[baseKey.toString()]] for (let i = 1; i < keys.length; i++) { const currentKey = keys[i] const prevKey = baseKey if (Math.abs(currentKey - prevKey) <= 2) { // 키 차이가 2 이하인 경우 병합 result[baseKey.toString()].push(...data[currentKey.toString()]) } else { // 새로운 그룹 시작 baseKey = currentKey result[baseKey.toString()] = [...data[currentKey.toString()]] } } return result } // 가장 왼쪽에 있는 모듈을 기준으로 같은 단에 있는 모듈들 파라미터 생성 const getMostLeftModules = (surface, exposedBottomModules) => { const { direction, modules, isChidory } = surface const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId) const roofMaterialIndex = parent.roofMaterial.index const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction let isEaveBar = construction.setupCover let isSnowGuard = construction.setupSnowCover let { rackYn, cvrPlvrYn, moduleIntvlHor, moduleIntvlVer, rackQty, lessSupFitQty } = surface.trestleDetail if (rackYn === 'N') { rackQty = lessSupFitQty } // 같은 단에 있는 모듈들의 리스트 let sameLineModuleList = [] let result = [] if (direction === 'south') { // 모듈의 top으로 groupBy let groupedByTop = modules.reduce((acc, module) => { const key = module.top if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) groupedByTop = mergeCloseKeys(groupedByTop) // groupedByTop의 키값을 기준으로 정렬한 데이터를 배열로 변환 sameLineModuleList = Object.values(groupedByTop).sort((a, b) => b[0].top - a[0].top) } else if (direction === 'north') { let groupedByTop = modules.reduce((acc, module) => { const key = module.top if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) groupedByTop = mergeCloseKeys(groupedByTop) sameLineModuleList = Object.values(groupedByTop).sort((a, b) => a[0].top - b[0].top) } else if (direction === 'east') { let groupedByLeft = modules.reduce((acc, module) => { const key = module.left if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) groupedByLeft = mergeCloseKeys(groupedByLeft) sameLineModuleList = Object.values(groupedByLeft).sort((a, b) => b[0].left - a[0].left) } else if (direction === 'west') { let groupedByLeft = modules.reduce((acc, module) => { const key = module.left if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) //groupedByLeft의 키값의 차이가 2이하인 경우 같은 라인으로 합친다. groupedByLeft = mergeCloseKeys(groupedByLeft) sameLineModuleList = Object.values(groupedByLeft).sort((a, b) => a[0].left - b[0].left) } sameLineModuleList.forEach((modules, index) => { const moduleRowResultData = { seq: index, moduleItemId: modules[0].moduleInfo.itemId, moduleTpCd: modules[0].moduleInfo.itemTp, moduleCnt: modules.length, exposedBottomCnt: 0, exposedHalfBottomCnt: 0, exposedTopCnt: 0, exposedHalfTopCnt: 0, touchedSurfaceCnt: 0, touchedHalfSurfaceCnt: 0, exposedBottomBracketCnt: 0, exposedHalfBottomBracketCnt: 0, exposedTopBracketCnt: 0, exposedHalfTopBracketCnt: 0, touchedSurfaceBracketCnt: 0, touchedHalfSurfaceBracketCnt: 0, eavesCnt: 0, eavesHalfCnt: 0, exposedSideEavesCnt: 0, } if (direction === 'south') { modules.sort((a, b) => a.left - b.left) } else if (direction === 'north') { modules.sort((a, b) => b.left - a.left) } else if (direction === 'east') { modules.sort((a, b) => a.top - b.top) } else if (direction === 'west') { modules.sort((a, b) => b.top - a.top) } // 모듈 하면,최하면 등 구해야함 modules.forEach((module, index) => { // 해당 모듈 주변에 다른 모듈이 있는지 확인 let { bottomModule, topModule, halfBottomLeftModule, halfBottomRightModule, halfTopLeftModule, halfTopRightModule, leftModule, rightModule, bottomLeftModule, bottomRightModule, } = findSideModule(module, surface) if (bottomModule) { moduleRowResultData.touchedSurfaceCnt++ if (rackYn === 'N') { moduleRowResultData.touchedSurfaceBracketCnt += rackQty } } if (!bottomModule) { if (halfBottomLeftModule && halfBottomRightModule) { moduleRowResultData.touchedSurfaceCnt++ if (rackYn === 'N') { moduleRowResultData.touchedSurfaceBracketCnt += rackQty } } else if ((halfBottomLeftModule && !halfBottomRightModule) || (!halfBottomLeftModule && halfBottomRightModule)) { moduleRowResultData.touchedHalfSurfaceCnt++ moduleRowResultData.exposedHalfBottomCnt++ if (rackYn === 'N') { if (rackQty !== 3) { // 3개가 아닌경우는 반반 moduleRowResultData.exposedHalfBottomBracketCnt += rackQty / 2 moduleRowResultData.touchedHalfSurfaceBracketCnt += rackQty / 2 } else { // 3개인 경우 왼쪽 아래가 없는 경우 touched는 2개, exposed는 1개 if (!halfBottomLeftModule && findSamePointInBottom(exposedBottomModules, module, direction)) { moduleRowResultData.exposedHalfBottomBracketCnt += 2 moduleRowResultData.touchedHalfSurfaceBracketCnt += 1 } else if (!halfBottomLeftModule && !findSamePointInBottom(exposedBottomModules, module, direction)) { // 왼쪽 아래가 없고, 하단에 같은 점이 없는 경우는 touched는 1개, exposed는 2개 moduleRowResultData.exposedHalfBottomBracketCnt += 1 moduleRowResultData.touchedHalfSurfaceBracketCnt += 2 } else if (!halfBottomRightModule && findSamePointInBottom(exposedBottomModules, module, direction)) { // 오른쪽 아래가 없고, 하단에 같은 점이 있는 경우는 exposed는 2개, touched는 1개 moduleRowResultData.exposedHalfBottomBracketCnt += 1 moduleRowResultData.touchedHalfSurfaceBracketCnt += 2 } else if (!halfBottomRightModule && !findSamePointInBottom(exposedBottomModules, module, direction)) { // 오른쪽 아래가 없고, 하단에 같은 점이 없는 경우는 exposed는 1개, touched는 2개 moduleRowResultData.exposedHalfBottomBracketCnt += 2 moduleRowResultData.touchedHalfSurfaceBracketCnt += 1 } } } if (cvrPlvrYn === 'Y') { moduleRowResultData.eavesHalfCnt++ if (bottomLeftModule || bottomRightModule || halfBottomLeftModule || halfBottomRightModule) { //처마커버 한개 노출 추가 moduleRowResultData.exposedSideEavesCnt++ } } } else { moduleRowResultData.exposedBottomCnt++ if (rackYn === 'N') { moduleRowResultData.exposedBottomBracketCnt += rackQty } if (isEaveBar) { moduleRowResultData.eavesCnt++ if ((rightModule && !leftModule) || (!rightModule && leftModule)) { // 둘중 하나가 없는경우는 처마커버 노출 추가 moduleRowResultData.exposedSideEavesCnt++ } else if (!rightModule && !leftModule) { // 양쪽 둘다 없는경우는 처마커버 노출 2개 추가 moduleRowResultData.exposedSideEavesCnt += 2 } } } } if (!topModule) { if ((halfTopLeftModule && !halfTopRightModule) || (!halfTopLeftModule && halfTopRightModule)) { moduleRowResultData.exposedHalfTopCnt++ if (rackYn === 'N') { if (rackQty !== 3) { moduleRowResultData.exposedHalfTopBracketCnt += rackQty / 2 } } } else if (!halfTopLeftModule && !halfTopRightModule) { moduleRowResultData.exposedTopCnt++ if (rackYn === 'N') { moduleRowResultData.exposedTopBracketCnt += rackQty } } } }) result.push(moduleRowResultData) }) canvas.renderAll() return result } // 해당 모듈이 해당 설치면에서 상하좌우, 대각선에 있는지 확인 const findSideModule = (module, surface) => { const { direction, modules } = surface let { rackYn, cvrPlvrYn, moduleIntvlHor, moduleIntvlVer, rackQty, lessSupFitQty } = surface.trestleDetail const centerPoints = getCenterPoints(surface) const horizontal = ['south', 'north'].includes(direction) ? moduleIntvlHor : moduleIntvlVer const vertical = ['south', 'north'].includes(direction) ? moduleIntvlVer : moduleIntvlHor const maxX = MODULE_MARGIN const maxY = MODULE_MARGIN let { widthArr, heightArr } = centerPoints[0] let width = widthArr.reduce((acc, num) => acc + num, 0) / widthArr.length let height = heightArr.reduce((acc, num) => acc + num, 0) / heightArr.length let { x, y } = { ...module.getCenterPoint() } let halfBottomLeftPoint let halfBottomRightPoint let halfTopLeftPoint let halfTopRightPoint let leftPoint let rightPoint let bottomLeftPoint let bottomRightPoint let bottomModule let topModule let halfBottomLeftModule let halfBottomRightModule let halfTopLeftModule let halfTopRightModule let leftModule let rightModule let bottomLeftModule let bottomRightModule switch (direction) { case 'south': width = width + horizontal height = height + vertical heightArr.forEach((h) => { if (!bottomModule) { bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + h)) < maxY) } if (!topModule) { topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - h)) < maxY) } if (!halfBottomLeftModule) { halfBottomLeftPoint = { x: x - width / 2, y: y + h } halfBottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomLeftPoint.y) < maxY, ) } if (!halfBottomRightModule) { halfBottomRightPoint = { x: x + width / 2, y: y + h } halfBottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomRightPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomRightPoint.y) < maxY, ) } if (!halfTopLeftModule) { halfTopLeftPoint = { x: x - width / 2, y: y - h } halfTopLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfTopLeftPoint.y) < maxY, ) } if (!halfTopRightModule) { halfTopRightPoint = { x: x + width / 2, y: y - h } halfTopRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopRightPoint.x) < maxX && Math.abs(centerPoint.y - halfTopRightPoint.y) < maxY, ) } if (!leftModule) { leftPoint = { x: x - width, y: y } leftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - leftPoint.x) < maxX && Math.abs(centerPoint.y - leftPoint.y) < maxY, ) } if (!rightModule) { rightPoint = { x: x + width, y: y } rightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - rightPoint.x) < maxX && Math.abs(centerPoint.y - rightPoint.y) < maxY, ) } if (!bottomLeftModule) { bottomLeftPoint = { x: x - width, y: y + h } bottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ) } if (!bottomRightModule) { bottomRightPoint = { x: x + width, y: y + h } bottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ) } }) break case 'north': width = width + horizontal height = height + vertical heightArr.forEach((h) => { if (!bottomModule) { bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - h)) < maxY) } if (!topModule) { topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + h)) < maxY) } if (!halfBottomLeftModule) { halfBottomLeftPoint = { x: x + width / 2, y: y - h } halfBottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomLeftPoint.y) < maxY, ) } if (!halfBottomRightModule) { halfBottomRightPoint = { x: x - width / 2, y: y - h } halfBottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomRightPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomRightPoint.y) < maxY, ) } if (!halfTopLeftModule) { halfTopLeftPoint = { x: x + width / 2, y: y + h } halfTopLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfTopLeftPoint.y) < maxY, ) } if (!halfTopRightModule) { halfTopRightPoint = { x: x - width / 2, y: y + h } halfTopRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopRightPoint.x) < maxX && Math.abs(centerPoint.y - halfTopRightPoint.y) < maxY, ) } if (!leftModule) { leftPoint = { x: x + width, y: y } leftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - leftPoint.x) < maxX && Math.abs(centerPoint.y - leftPoint.y) < maxY, ) } if (!rightModule) { rightPoint = { x: x - width, y: y } rightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - rightPoint.x) < maxX && Math.abs(centerPoint.y - rightPoint.y) < maxY, ) } if (!bottomLeftModule) { bottomLeftPoint = { x: x + width, y: y - h } bottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ) } if (!bottomRightModule) { bottomRightPoint = { x: x - width, y: y - h } bottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ) } }) break case 'east': widthArr.forEach((w) => { if (!bottomModule) { bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + w)) < maxX && Math.abs(centerPoint.y - y) < maxY) } if (!topModule) { topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - w)) < maxX && Math.abs(centerPoint.y - y) < maxY) } if (!halfBottomLeftModule) { halfBottomLeftPoint = { x: x + w, y: y - height / 2 } halfBottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomLeftPoint.y) < maxY, ) } if (!halfBottomRightModule) { halfBottomRightPoint = { x: x + w, y: y - height / 2 } halfBottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomRightPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomRightPoint.y) < maxY, ) } if (!halfTopLeftModule) { halfTopLeftPoint = { x: x - w, y: y + height / 2 } halfTopLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfTopLeftPoint.y) < maxY, ) } if (!halfTopRightModule) { halfTopRightPoint = { x: x - w, y: y - height / 2 } halfTopRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopRightPoint.x) < maxX && Math.abs(centerPoint.y - halfTopRightPoint.y) < maxY, ) } if (!leftModule) { leftPoint = { x: x, y: y + height } leftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - leftPoint.x) < maxX && Math.abs(centerPoint.y - leftPoint.y) < maxY, ) } if (!rightModule) { rightPoint = { x: x, y: y - height } rightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - rightPoint.x) < maxX && Math.abs(centerPoint.y - rightPoint.y) < maxY, ) } if (!bottomLeftModule) { bottomLeftPoint = { x: x + w, y: y + height } bottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ) } if (!bottomRightModule) { bottomRightPoint = { x: x + w, y: y - height } bottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ) } }) break case 'west': widthArr.forEach((w) => { if (!bottomModule) { bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - w)) < maxX && Math.abs(centerPoint.y - y) < maxY) } if (!topModule) { topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + w)) < maxX && Math.abs(centerPoint.y - y) < maxY) } if (!halfBottomLeftModule) { halfBottomLeftPoint = { x: x - w, y: y - height / 2 } halfBottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomLeftPoint.y) < maxY, ) } if (!halfBottomRightModule) { halfBottomRightPoint = { x: x - w, y: y + height / 2 } halfBottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomRightPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomRightPoint.y) < maxY, ) } if (!halfTopLeftModule) { halfTopLeftPoint = { x: x + w, y: y - height / 2 } halfTopLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfTopLeftPoint.y) < maxY, ) } if (!halfTopRightModule) { halfTopRightPoint = { x: x + w, y: y + height / 2 } halfTopRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopRightPoint.x) < maxX && Math.abs(centerPoint.y - halfTopRightPoint.y) < maxY, ) } if (!leftModule) { leftPoint = { x: x, y: y - height } leftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - leftPoint.x) < maxX && Math.abs(centerPoint.y - leftPoint.y) < maxY, ) } if (!rightModule) { rightPoint = { x: x, y: y + height } rightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - rightPoint.x) < maxX && Math.abs(centerPoint.y - rightPoint.y) < maxY, ) } if (!bottomLeftModule) { bottomLeftPoint = { x: x - w, y: y - height } bottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ) } if (!bottomRightModule) { bottomRightPoint = { x: x - w, y: y + height } bottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ) } }) break } return { bottomModule, topModule, halfBottomLeftModule, halfBottomRightModule, halfTopLeftModule, halfTopRightModule, leftModule, rightModule, bottomLeftModule, bottomRightModule, } } const clear = () => { canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) .forEach((obj) => { obj.isComplete = false }) canvas.getObjects().forEach((obj) => { if (obj.name === 'eaveBar' || obj.name === 'rack' || obj.name === 'halfEaveBar' || obj.name === 'smartRack' || obj.name === 'bracket') { canvas.remove(obj) } }) resetCircuits() } // 전모듈 의 회로번호 visible false 처리 // 가대 설치 전 필요 const setViewCircuitNumberTexts = (visible) => { const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber') circuitNumberTexts.forEach((text) => { text.visible = visible }) canvas.renderAll() } // 가대 설치 완료 전,후 모든 surface의 isComplete를 변경 const setAllModuleSurfaceIsComplete = (bool) => { const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) surfaces.forEach((surface) => { surface.isComplete = bool }) } // 배치면 전체에 가대 설치 여부 const isAllComplete = () => { const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) return surfaces.every((surface) => surface.isComplete) } return { apply, getTrestleParams, clear, setViewCircuitNumberTexts, getEstimateData, setAllModuleSurfaceIsComplete, isAllComplete } }