import { useRecoilState, useRecoilValue } from 'recoil' import { canvasState, currentAngleTypeSelector } from '@/store/canvasAtom' import { POLYGON_TYPE } from '@/common/common' import { moduleSelectionDataState } from '@/store/selectedModuleOptions' import { getDegreeByChon, getTrestleLength } from '@/util/canvas-util' import { v4 as uuidv4 } from 'uuid' import { useMasterController } from '@/hooks/common/useMasterController' import { estimateParamAtom } from '@/store/estimateAtom' // 회로 및 가대설정 export const useTrestle = () => { const canvas = useRecoilValue(canvasState) const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터 const { getQuotationItem } = useMasterController() const [estimateParam, setEstimateParam] = useRecoilState(estimateParamAtom) const currentAngleType = useRecoilValue(currentAngleTypeSelector) const apply = () => { 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 const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction if (!construction) { alert('앞에서 셋팅 안됨') return } 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 } = surface.trestleDetail if (!rack) { //25/01/16 기준 랙이 없는 경우는 그냥 안그려준다. return } const rackInfos = Object.keys(rack).map((key) => { return { key, value: rack[key] } }) 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.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) => { //TODO : 방향별로 처마력바 설치해야함 const bottomPoints = findTopTwoPoints([...module.points], direction) if (!bottomPoints) return const eaveBar = new fabric.Line([bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y], { parent: surface, name: 'eaveBar', stroke: 'blue', strokeWidth: 4, selectable: false, surfaceId: surface.id, parentId: module.id, }) canvas.add(eaveBar) canvas.renderAll() }) if (isChidory && cvrPlvrYn === 'Y') { leftExposedHalfBottomModules.forEach((module) => { const bottomPoints = findTopTwoPoints([...module.points], 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: 'halfEaveBar', stroke: 'blue', strokeWidth: 4, selectable: false, surfaceId: surface.id, parentId: module.id, }) 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: 'halfEaveBar', stroke: 'blue', strokeWidth: 4, selectable: false, parentId: module.id, }) 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 //우선 절반을 나눈 뒤 왼쪽부터 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ 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++ 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++ 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++ 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) { // 바로 위 모듈을 찾는다. centerRows++ x = nextModule.x y = nextModule.y } else { hasNextModule = false } } // 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다. const leftRacks = rackInfos.find((rack) => { return rack.value.moduleRows === leftRows })?.value.racks // 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다. const rightRacks = rackInfos.find((rack) => { return rack.value.moduleRows === rightRows })?.value.racks // 해당 rack으로 그려준다. const centerRacks = rackInfos.find((rack) => { return rack.value.moduleRows === centerRows })?.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() } //TODO : 방향별로 가대 설치해야함 let leftRows = 1 let hasNextModule = true let findLeft = true //우선 절반을 나눈 뒤 왼쪽부터 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. leftRows++ 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++ x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } // 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다. const leftRacks = rackInfos.find((rack) => { return rack.value.moduleRows === leftRows })?.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() } //TODO : 방향별로 가대 설치해야함 let rightRows = 1 let hasNextModule = true let findRight = true // 오른쪽 찾는다. while (hasNextModule) { //바로 위에 있는지 확인한다. let nextModule = findNextModule({ x, y, width, height, horizontal, vertical }, centerPoints, direction) if (nextModule) { // 바로 위 모듈을 찾는다. rightRows++ 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++ x = nextModule.x y = nextModule.y } else { hasNextModule = false } } } // 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다. const rightRacks = rackInfos.find((rack) => { return rack.value.moduleRows === rightRows })?.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) surface.set({ quotationParam }) }) return setEstimateData() } catch (e) { return false } } // itemList 조회 후 estimateParam에 저장 const setEstimateData = async () => { const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //surfaces.pcses들을 배열로 묶는다 const pcses = surfaces[0].pcses surfaces.forEach((surface, index) => { if (index !== 0) { pcses.concat(surface.pcses) } }) const params = { trestles: surfaces.map((surface) => surface.quotationParam), pcses } //견적서 itemList 조회 const res = await getQuotationItem(params) if (!res.data) { return false } const itemList = res.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, pitch: slope, moduleCompass, surfaceCompass } = parent const roofMaterialIndex = parent.roofMaterial.index const { nameJp: roofMaterialIdMulti } = roofMaterial const moduleSelection = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex) const { constTp: constructSpecification, constTpJp: constructSpecificationMulti } = moduleSelection.construction const { trestleMkrCd, constMthdCd: supportMethodId, roofBaseCd, trestleMkrCdJp: supportMeaker, constMthdCdJp: supportMethodIdMulti, } = moduleSelection.trestle 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: roofMaterial.roofMatlCd, supportMethodId, constructSpecification, constructSpecificationMulti, roofMaterialIdMulti, supportMethodIdMulti, supportMeaker, slope, classType: currentAngleType === 'slope' ? '0' : '1', angle: getDegreeByChon(slope), azimuth: surfaceCompass ?? moduleCompass ?? 0, moduleList, } }) // circuitItemList 중복제거 circuitItemList = circuitItemList.filter((item, index) => circuitItemList.indexOf(item) === index) circuitItemList = circuitItemList.map((circuitId) => { return { itemId: circuitId, } }) setEstimateParam({ ...estimateParam, itemList, northArrangement, roofSurfaceList, circuitItemList }) // 정상적으로 완료 되면 true 반환 return true } 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, width, height, horizontal, vertical } = { ...currentPoint } width = width + horizontal height = height + vertical let maxX = 2 + horizontal * 3 let maxY = 2 + vertical * 3 let result switch (direction) { case 'south': { result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) break } case 'north': { result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) break } case 'east': { result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) break } case 'west': { 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, width, height, horizontal, vertical } = { ...currentPoint } let result let topLeftPoint let maxX = 2 + horizontal * 3 let maxY = 2 + vertical * 3 switch (direction) { case 'south': { width = width + horizontal height = height + vertical topLeftPoint = { x: x - width / 2, y: y - height } break } case 'north': { width = width + horizontal height = height + vertical topLeftPoint = { x: x + width / 2, y: y + height } break } case 'east': { topLeftPoint = { x: x - width, y: y + height / 2 } break } case 'west': { topLeftPoint = { x: x + width, y: y - height / 2 } break } } result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY) return result } const findNextRightModule = (currentPoint, centerPoints, direction) => { let { x, y, width, height, horizontal, vertical } = { ...currentPoint } width = width + horizontal height = height + vertical let result let topRightPoint let maxX = 2 + horizontal * 3 let maxY = 2 + vertical * 3 switch (direction) { case 'south': { topRightPoint = { x: x + width / 2, y: y - height } break } case 'north': { topRightPoint = { x: x - width / 2, y: y + height } break } case 'east': { topRightPoint = { x: x - width, y: y - height / 2 } break } case 'west': { topRightPoint = { x: x + width, y: y + height / 2 } break } } result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY) return result } 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) 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: 'smartRack', stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'top', }) canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointY -= rackLength + 8 } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY - rackLength], { name: '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, 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: 'smartRack', stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'left', }) canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointX -= rackLength } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX - rackLength, startPointY], { name: 'rack', stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, strokeWidth: 4, selectable: false, 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: 'smartRack', stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'right', }) canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointX += rackLength + 8 } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX + rackLength, startPointY], { name: 'rack', stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, 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: 'smartRack', stroke: 'red', strokeWidth: 4, selectable: false, shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, seq, smartRackId, rackId: itemId, direction: 'right', }) canvas.add(rack) canvas.renderAll() } else if (setRackTpCd === 'INTVL') { startPointY += rackLength + 8 } }) } else { const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY + rackLength], { name: 'rack', stroke: 'red', shadow: { color: 'black', // Outline color blur: 10, offsetX: 0, offsetY: 0, }, parentId: module.id, surfaceId: surface.id, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackRowsCd, rackId: itemId, direction: 'bottom', }) canvas.add(rack) canvas.renderAll() } startPointY += rackLength + 8 }) break } } } 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) => { const { x1, y1, x2, y2, direction, supFitQty, supFitIntvlPct, rackLen } = rack const bracketLength = 10 if (direction === 'top') { const result = getBracketPoints(supFitQty, supFitIntvlPct) result.forEach((percent) => { const bracket = new fabric.Rect({ left: x2 - bracketLength / 3, top: y2 + (rackLen / 10) * percent, fill: 'green', name: 'bracket', parentId: rack.parentId, 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 bracket = new fabric.Rect({ left: x2 + (rackLen / 10) * percent, top: y2 - bracketLength / 3, fill: 'green', name: 'bracket', parentId: rack.parentId, 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 bracket = new fabric.Rect({ left: x2 - (rackLen / 10) * percent, top: y2 - bracketLength / 3, fill: 'green', parentId: rack.parentId, surfaceId: surface.id, name: '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 bracket = new fabric.Rect({ left: x2 - bracketLength / 3, top: y2 - (rackLen / 10) * percent, fill: 'green', name: 'bracket', parentId: rack.parentId, 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 const direction = surface.direction rackQty = 3 rackIntvlPct = 10 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)) { // 해당 모듈과 같은 위치 맨 아래에 모듈이 없는 경우 하나 더 설치 필요 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) => { const { x, y } = module.getCenterPoint() return exposedBottomModules.find((exposedBottomModule) => { const { x: exposedX, y: exposedY } = exposedBottomModule.getCenterPoint() return Math.abs(x - exposedX) < 2 || Math.abs(y - exposedY) < 2 }) } 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: 'bracket', parentId: module.id, surfaceId: module.surfaceId, width: 10, height: 10, selectable: false, }) canvas.add(bracket) canvas.renderAll() if (direction === 'south') { startPointY -= height + moduleIntvlVer } else if (direction === 'north') { startPointY += height + moduleIntvlVer } else if (direction === 'east') { startPointX -= width + moduleIntvlHor } else if (direction === 'west') { startPointX += width + moduleIntvlHor } } } 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 calculateForApi = (moduleSurface) => { const centerPoints = [] 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 = 2 + horizontal * 3 const maxY = 2 + vertical * 3 modules.forEach((module, index) => { module.tempIndex = index const { x, y } = module.getCenterPoint() const { width, height } = { ...module } centerPoints.push({ x, y, width: Math.floor(width), height: Math.floor(height), index }) }) 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 } = { ...centerPoint } // centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인 let bottomCell let bottomLeftPoint let bottomRightPoint let leftBottomCnt let rightBottomCnt switch (direction) { case 'south': width = width + horizontal height = height + vertical bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) bottomLeftPoint = { x: x - width / 2, y: y + height } bottomRightPoint = { x: x + width / 2, y: y + height } break case 'north': width = width + horizontal height = height + vertical bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) bottomLeftPoint = { x: x + width / 2, y: y - height } bottomRightPoint = { x: x - width / 2, y: y - height } break case 'east': bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < maxX && Math.abs(centerPoint.y - y) < maxY) width = width + horizontal bottomLeftPoint = { x: x + width, y: y + height / 2 } bottomRightPoint = { x: x + width, y: y - height / 2 } break case 'west': bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) width = width + horizontal bottomLeftPoint = { x: x - width, y: y - height / 2 } bottomRightPoint = { x: x - width, y: y + height / 2 } break } if (bottomCell.length === 1) { return } // 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다. leftBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - bottomLeftPoint.y) < maxY, ).length rightBottomCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < maxX && Math.abs(centerPoint.y - bottomRightPoint.y) < maxY, ).length 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 } = { ...centerPoint } let topCell let topLeftPoint let topRightPoint let leftTopCnt let rightTopCnt switch (direction) { case 'south': width = width + horizontal height = height + vertical topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) topLeftPoint = { x: x - width / 2, y: y - height } topRightPoint = { x: x + width / 2, y: y - height } break case 'north': height = height + vertical topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) topLeftPoint = { x: x + width / 2, y: y + height } topRightPoint = { x: x - width / 2, y: y + height } break case 'east': width = width + horizontal topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) topLeftPoint = { x: x - width, y: y + height / 2 } topRightPoint = { x: x - width, y: y - height / 2 } break case 'west': width = width + horizontal topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < maxX && Math.abs(centerPoint.y - y) < maxY) topLeftPoint = { x: x + width, y: y - height / 2 } topRightPoint = { x: x + width, y: y + height / 2 } break } if (topCell.length === 1) { touchDimension++ return } leftTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < maxX && Math.abs(centerPoint.y - topLeftPoint.y) < maxY, ).length rightTopCnt = centerPoints.filter( (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < maxX && Math.abs(centerPoint.y - topRightPoint.y) < maxY, ).length 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 } // 견적서 아이템 조회 api parameter 생성 const getTrestleParams = (surface) => { 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, inclCd: addRoof.pitch, roofPitch: 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, rackFittingCnt: bracketList.length, moduleRows: getMostLeftModules(surface), cvrYn: moduleSelection.construction.setupCover ? 'Y' : 'N', snowGdYn: moduleSelection.construction.setupSnowCover ? 'Y' : 'N', plvrYn: cvrPlvrYn, modules: getModules(surface), trestleMkrCd, constMthdCd, roofBaseCd, illuminationTp, instHt, stdSnowLd, stdWindSpeed, constTp, } } const getModules = (surface) => { const { modules } = surface const params = modules.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, } }) } // 가장 왼쪽에 있는 모듈을 기준으로 같은 단에 있는 모듈들 파라미터 생성 const getMostLeftModules = (surface) => { const { direction, modules } = 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 const groupedByTop = modules.reduce((acc, module) => { const key = module.top if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) // groupedByTop의 키값을 기준으로 정렬한 데이터를 배열로 변환 sameLineModuleList = Object.values(groupedByTop).sort((a, b) => b[0].top - a[0].top) } else if (direction === 'north') { const groupedByTop = modules.reduce((acc, module) => { const key = module.top if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) sameLineModuleList = Object.values(groupedByTop).sort((a, b) => a[0].top - b[0].top) } else if (direction === 'east') { const groupedByLeft = modules.reduce((acc, module) => { const key = module.left if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) sameLineModuleList = Object.values(groupedByLeft).sort((a, b) => a[0].left - b[0].left) } else if (direction === 'west') { const groupedByLeft = modules.reduce((acc, module) => { const key = module.left if (!acc[key]) { acc[key] = [] } acc[key].push(module) return acc }, {}) sameLineModuleList = Object.values(groupedByLeft).sort((a, b) => b[0].left - a[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 (!bottomModule) { if (halfBottomLeftModule && halfBottomRightModule) { moduleRowResultData.touchedSurfaceCnt++ } else if ((halfBottomLeftModule && !halfBottomRightModule) || (!halfBottomLeftModule && halfBottomRightModule)) { moduleRowResultData.touchedHalfSurfaceCnt++ moduleRowResultData.exposedHalfBottomCnt++ if (cvrPlvrYn === 'Y') { moduleRowResultData.eavesHalfCnt++ if (bottomLeftModule || bottomRightModule || halfBottomLeftModule || halfBottomRightModule) { //처마커버 한개 노출 추가 moduleRowResultData.exposedSideEavesCnt++ } } } else { moduleRowResultData.exposedBottomCnt++ if (isEaveBar) { moduleRowResultData.eavesCnt++ if ((rightModule && !leftModule) || (!rightModule && leftModule)) { // 둘중 하나가 없는경우는 처마커버 노출 추가 moduleRowResultData.exposedSideEavesCnt++ } } } } if (!topModule) { if ((halfTopLeftModule && !halfTopRightModule) || (!halfTopLeftModule && halfTopRightModule)) { moduleRowResultData.exposedHalfTopCnt++ } else if (!halfTopLeftModule && !halfTopRightModule) { moduleRowResultData.exposedTopCnt++ } } }) result.push(moduleRowResultData) }) return result } // 해당 모듈이 해당 설치면에서 상하좌우, 대각선에 있는지 확인 const findSideModule = (module, surface) => { const { direction, modules } = surface let { rackYn, cvrPlvrYn, moduleIntvlHor, moduleIntvlVer, rackQty, lessSupFitQty } = surface.trestleDetail const centerPoints = modules.map((module) => { return module.getCenterPoint() }) const horizontal = ['south', 'north'].includes(direction) ? moduleIntvlHor : moduleIntvlVer const vertical = ['south', 'north'].includes(direction) ? moduleIntvlVer : moduleIntvlHor const maxX = 2 + horizontal * 3 const maxY = 2 + vertical * 3 let { width, height } = { ...module } 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 bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) halfBottomLeftPoint = { x: x - width / 2, y: y + height } halfBottomRightPoint = { x: x + width / 2, y: y + height } topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) halfTopLeftPoint = { x: x - width / 2, y: y - height } halfTopRightPoint = { x: x + width / 2, y: y - height } leftPoint = { x: x - width, y: y } rightPoint = { x: x + width, y: y } bottomLeftModule = { x: x - width, y: y + height } bottomRightModule = { x: x + width, y: y + height } break case 'north': width = width + horizontal height = height + vertical bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y - height)) < maxY) halfBottomLeftPoint = { x: x + width / 2, y: y - height } halfBottomRightPoint = { x: x - width / 2, y: y - height } topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < maxX && Math.abs(centerPoint.y - (y + height)) < maxY) halfTopLeftPoint = { x: x + width / 2, y: y + height } halfTopRightPoint = { x: x - width / 2, y: y + height } leftPoint = { x: x + width, y: y } rightPoint = { x: x - width, y: y } bottomLeftModule = { x: x + width, y: y - height } bottomRightModule = { x: x - width, y: y - height } break case 'east': bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + width)) < maxX && Math.abs(centerPoint.y - y) < maxY) width = width + horizontal halfBottomLeftPoint = { x: x + width, y: y + height / 2 } halfBottomRightPoint = { x: x + width, y: y - height / 2 } topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) halfTopLeftPoint = { x: x - width, y: y + height / 2 } halfTopRightPoint = { x: x - width, y: y - height / 2 } leftPoint = { x: x, y: y + height } rightPoint = { x: x, y: y - height } bottomLeftModule = { x: x + width, y: y + height } bottomRightModule = { x: x + width, y: y - height } break case 'west': bottomModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - width)) < maxX && Math.abs(centerPoint.y - y) < maxY) width = width + horizontal halfBottomLeftPoint = { x: x - width, y: y - height / 2 } halfBottomRightPoint = { x: x - width, y: y + height / 2 } topModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + width)) < maxX && Math.abs(centerPoint.y - y) < maxY) halfTopLeftPoint = { x: x + width, y: y - height / 2 } halfTopRightPoint = { x: x + width, y: y + height / 2 } leftPoint = { x: x, y: y - height } rightPoint = { x: x, y: y + height } bottomLeftModule = { x: x - width, y: y - height } bottomRightModule = { x: x - width, y: y + height } break } halfTopLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfTopLeftPoint.y) < maxY, ) halfTopRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfTopRightPoint.x) < maxX && Math.abs(centerPoint.y - halfTopRightPoint.y) < maxY, ) halfBottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomLeftPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomLeftPoint.y) < maxY, ) halfBottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - halfBottomRightPoint.x) < maxX && Math.abs(centerPoint.y - halfBottomRightPoint.y) < maxY, ) leftModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - leftPoint.x) < maxX && Math.abs(centerPoint.y - leftPoint.y) < maxY) rightModule = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - rightPoint.x) < maxX && Math.abs(centerPoint.y - rightPoint.y) < maxY) bottomLeftModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomLeftModule.x) < maxX && Math.abs(centerPoint.y - bottomLeftModule.y) < maxY, ) bottomRightModule = centerPoints.find( (centerPoint) => Math.abs(centerPoint.x - bottomRightModule.x) < maxX && Math.abs(centerPoint.y - bottomRightModule.y) < maxY, ) return { bottomModule, topModule, halfBottomLeftModule, halfBottomRightModule, halfTopLeftModule, halfTopRightModule, leftModule, rightModule, bottomLeftModule, bottomRightModule, } } const clear = () => { canvas.getObjects().forEach((obj) => { if (obj.name === 'eaveBar' || obj.name === 'rack' || obj.name === 'halfEaveBar' || obj.name === 'smartRack' || obj.name === 'bracket') { canvas.remove(obj) } }) } return { apply, getTrestleParams, clear } }