import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { POLYGON_TYPE } from '@/common/common' import { moduleSelectionDataState } from '@/store/selectedModuleOptions' // 회로 및 가대설정 export const useTrestle = () => { const canvas = useRecoilValue(canvasState) const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터 const apply = () => { //처마력바가 체크되어 있는 경우 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') { 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 isEaveBar = construction.setupCover let isSnowGuard = construction.setupSnowCover const direction = parent.direction const rack = surface.trestleDetail.rack let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn } = surface.trestleDetail rackYn = 'N' rackQty = 5 cvrPlvrYn = 'Y' if (!rack) { //25/01/16 기준 랙이 없는 경우는 그냥 안그려준다. return } const rackInfos = Object.keys(rack).map((key) => { return { key, value: rack[key] } }) const result = calculateForApi(surface) 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 canvas .getObjects() .filter((obj) => ['eaveBar', 'halfEaveBar'].includes(obj.name) && obj.parent === surface) .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, 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, 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 // 가대 설치를 위한 가장 아래 모듈로부터 위로 몇단인지 계산 // 오른쪽,왼쪽 둘 다 아래에 아무것도 없는, 처마 커버를 필요로 하는 모듈 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() } //TODO : 방향별로 가대 설치해야함 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 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 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 // 해당 rack으로 그려준다. if (rackYn === 'Y') { drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R', rackYn) } module.set({ rightRows }) }) if (rackYn === 'N') { // rack이 없을경우 installBracketWithOutRack(surface, exposedBottomModules, leftExposedHalfBottomModules, rightExposedHalfBottomPoints, isChidory) } else if (rackYn === 'Y') { installBracket(surface) } }) } 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 } = module 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 } = rackInfo const rackLength = rackLen / 10 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, supFitQty, supFitIntvlPct, rackLen, rackId: itemId, direction: 'top', }) canvas.add(rack) canvas.renderAll() startPointY -= rackLength + 3 }) break } case 'east': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo const rackLength = rackLen / 10 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, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackYn, rackId: itemId, direction: 'left', }) canvas.add(rack) canvas.renderAll() startPointX -= rackLength + 3 }) break } case 'west': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo const rackLength = rackLen / 10 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, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackId: itemId, direction: 'right', }) canvas.add(rack) canvas.renderAll() startPointX += rackLength + 3 }) break } case 'north': { rackInfos.forEach((rackInfo) => { const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo const rackLength = rackLen / 10 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, strokeWidth: 4, selectable: false, supFitQty, supFitIntvlPct, rackLen, rackId: itemId, direction: 'bottom', }) canvas.add(rack) canvas.renderAll() startPointY += rackLength + 3 }) break } } } const installBracket = (surface) => { const modules = surface.modules const racks = [] modules.forEach((module) => { canvas .getObjects() .filter((obj) => obj.name === 'rack') .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, 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, 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, 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, width: bracketLength, height: bracketLength, selectable: false, }) canvas.add(bracket) }) canvas.renderAll() } }) } //랙 없음 인 경우 지지금구 설치 const installBracketWithOutRack = (surface, exposedBottomModules, leftExposedHalfBottomModules, rightExposedHalfBottomPoints, isChidory) => { let { rackQty, rackIntvlPct, moduleIntvlHor, moduleIntvlVer } = surface.trestleDetail rackQty = 3 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 (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 === 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 === 4) { drawBracketWithOutRack(module, rackIntvlPct / 3, module.rightRows + 1, 'R', surface.direction, moduleIntvlHor, moduleIntvlVer) } }) } 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, 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) { throw new Error('Number of points must be at least 2') } 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 } return { apply } }