From e885e7b93939dd53e3caf12bec17b5b9242d1b26 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 14 Jan 2025 18:24:59 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EB=A1=9C=20=ED=95=A0=EB=8B=B9=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 5 + .../circuitTrestle/CircuitTrestleSetting.jsx | 19 +- src/hooks/module/useModuleBasicSetting.js | 241 ------ src/hooks/module/useTrestle.js | 740 ++++++++++++++++++ 4 files changed, 759 insertions(+), 246 deletions(-) create mode 100644 src/hooks/module/useTrestle.js diff --git a/src/common/common.js b/src/common/common.js index a28542e9..c9c60f48 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -164,6 +164,11 @@ export const SAVE_KEY = [ 'moduleCompass', 'isFixed', 'modules', + 'rackLen', + 'itemId', + 'supFitQty', + 'supFitIntvlPct', + 'rackLen', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index b14bfa31..124c0450 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -12,12 +12,16 @@ import { get } from 'react-hook-form' import { correntObjectNoState } from '@/store/settingAtom' import { useRecoilValue } from 'recoil' import { GlobalDataContext } from '@/app/GlobalDataProvider' + import { useRecoilState } from 'recoil' import { powerConditionalState } from '@/store/circuitTrestleAtom' import { POLYGON_TYPE } from '@/common/common' import { useSwal } from '@/hooks/useSwal' import { canvasState } from '@/store/canvasAtom' +import { trestleDetailData } from '@/common/example' +import { useTrestle } from '@/hooks/module/useTrestle' + const ALLOCATION_TYPE = { AUTO: 'auto', PASSIVITY: 'passivity', @@ -29,12 +33,17 @@ export default function CircuitTrestleSetting({ id }) { // 탭 번호 2: 회로 할당 const [tabNum, setTabNum] = useState(1) const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO) - const canvas = useRecoilValue(canvasState) - const { swalFire } = useSwal() - const apply = () => { - closePopup(id) - } + const [makers, setMakers] = useState([]) + const [series, setSeries] = useState([]) + const [models, setModels] = useState([]) + const [selectedMaker, setSelectedMaker] = useState(null) + const [selectedModels, setSelectedModels] = useState(null) + const [selectedSeries, setSelectedSeries] = useState(null) + const correntObjectNo = useRecoilValue(correntObjectNoState) + const { getPcsMakerList } = useMasterController() + const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext) + const { apply } = useTrestle() useEffect(() => { // console.log(canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)) diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 03035cda..dbe19919 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -1136,247 +1136,6 @@ export function useModuleBasicSetting() { // calculateForApi() } - const calculateForApi = () => { - const moduleSufaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) - - const results = [] - - moduleSufaces.forEach((moduleSurface) => { - const centerPoints = [] - const direction = moduleSurface.direction - const modules = moduleSurface.modules - - 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 - // 노출하면 체크 - centerPoints.forEach((centerPoint, index) => { - const { 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': - bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2) - bottomLeftPoint = { x: x - width / 2, y: y + height } - bottomRightPoint = { x: x + width / 2, y: y + height } - break - case 'north': - bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2) - 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)) < 2 && Math.abs(centerPoint.y - y) < 2) - 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)) < 2 && Math.abs(centerPoint.y - y) < 2) - 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) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2, - ).length - rightBottomCnt = centerPoints.filter( - (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2, - ).length - - if (leftBottomCnt + rightBottomCnt === 1) { - exposedHalfBottom++ - return - } - }) - // 노출상면 체크 - - centerPoints.forEach((centerPoint, index) => { - const { x, y, width, height } = centerPoint - - let topCell - let topLeftPoint - let topRightPoint - let leftTopCnt - let rightTopCnt - - switch (direction) { - case 'south': - topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2) - topLeftPoint = { x: x - width / 2, y: y - height } - topRightPoint = { x: x + width / 2, y: y - height } - break - case 'north': - topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2) - topLeftPoint = { x: x + width / 2, y: y + height } - topRightPoint = { x: x - width / 2, y: y + height } - break - case 'east': - topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2) - topLeftPoint = { x: x - width, y: y + height / 2 } - topRightPoint = { x: x - width, y: y - height / 2 } - break - case 'west': - topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2) - 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) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2, - ).length - rightTopCnt = centerPoints.filter( - (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2, - ).length - - if (leftTopCnt + rightTopCnt === 2) { - touchDimension++ - return - } - - if (leftTopCnt + rightTopCnt === 1) { - exposedHalfTop++ - halfTouchDimension++ - return - } - if (leftTopCnt + rightTopCnt === 0) { - exposedTop++ - } - }) - // 완전 노출 하면 계산 - - /*const cells = canvas.getObjects().filter((obj) => polygon.id === obj.parentId) - const points = cells.map((cell) => { - return cell.getCenterPoint() - })*/ - 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 maxYCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.y - maxY) < 2) - let minYCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.y - minY) < 2) - let maxXCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.x - maxX) < 2) - let minXCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.x - minX) < 2) - - switch (direction) { - case 'south': - exposedBottom += maxYCenterPoint.length - break - case 'north': - exposedBottom += minYCenterPoint.length - break - case 'east': - exposedBottom += maxXCenterPoint.length - break - case 'west': - exposedBottom += minXCenterPoint.length - break - } - }) - - results.push({ - exposedBottom, - exposedHalfBottom, - exposedTop, - exposedHalfTop, - touchDimension, - halfTouchDimension, - }) - console.log({ - direction, - exposedBottom, - exposedHalfBottom, - exposedTop, - exposedHalfTop, - touchDimension, - halfTouchDimension, - }) - }) - - return results - } - - // 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 - } - const coordToTurfPolygon = (points) => { const coordinates = points.map((point) => [point.x, point.y]) coordinates.push(coordinates[0]) diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js new file mode 100644 index 00000000..4c3e9718 --- /dev/null +++ b/src/hooks/module/useTrestle.js @@ -0,0 +1,740 @@ +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 = () => { + // rack은 맵 형태의 객체인데 key value 형태로 되어있다. + // 이때 이 형태를 [{key, value}, ...] 형태로 바꿔야 함. + /*const rackInfos = Object.keys(rack).map((key) => { + return { key, value: rack[key] } + })*/ + + //처마력바가 체크되어 있는 경우 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) + } + }) + + 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 + let isEaveBar = construction.setupCover + let isSnowGuard = construction.setupSnowCover + + const rack = surface.trestleDetail.rack + const { rackQty, rackIntvlPct } = surface.trestleDetail + 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 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) + if (isExposedBottom) { + exposedBottomModules.push(module) + } + if (isLeftExposedHalfBottom) { + leftExposedHalfBottomModules.push(module) + } + if (isRightExposedHalfBottom) { + rightExposedHalfBottomPoints.push(module) + } + }) + + canvas + .getObjects() + .filter((obj) => obj.name === 'eaveBar') + .forEach((obj) => { + canvas.remove(obj) + }) + + if (isEaveBar) { + // 처마력바설치 true인 경우 설치 + exposedBottomModules.forEach((module) => { + //TODO : 방향별로 처마력바 설치해야함 + const bottomPoints = findTopTwoPoints([...module.points]) + if (!bottomPoints) return + const eaveBar = new fabric.Line([bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y], { + name: 'eaveBar', + stroke: 'blue', + strokeWidth: 4, + selectable: false, + }) + canvas.add(eaveBar) + canvas.renderAll() + }) + } + + // 가대 설치를 위한 가장 아래 모듈로부터 위로 몇단인지 계산 + // 오른쪽,왼쪽 둘 다 아래에 아무것도 없는, 처마 커버를 필요로 하는 모듈 + exposedBottomModules.forEach((module) => { + const { width, height } = module + let { x: startX, y: startY } = { ...module.getCenterPoint() } + let { x, y } = { ...module.getCenterPoint() } + const direction = 'south' + //TODO : 방향별로 가대 설치해야함 + + let leftRows = 1 + let rightRows = 1 + let centerRows = 1 + let hasNextModule = true + let findLeft = true + let findRight = true + + //우선 절반을 나눈 뒤 왼쪽부터 찾는다. + while (hasNextModule) { + //바로 위에 있는지 확인한다. + let nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2 + }) + + if (nextModule) { + // 바로 위 모듈을 찾는다. + leftRows++ + x = nextModule.x + y = nextModule.y + } else { + // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. + if (findLeft) { + const topLeftPoint = { x: x - width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2 + }) + findLeft = false + } else { + const topRightPoint = { x: x + width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2 + }) + findLeft = true + } + + if (nextModule) { + // 바로 위 모듈을 찾는다. + leftRows++ + x = nextModule.x + y = nextModule.y + } else { + hasNextModule = false + } + } + } + + hasNextModule = true + x = startX + y = startY + + // 오른쪽 찾는다. + while (hasNextModule) { + //바로 위에 있는지 확인한다. + let nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2 + }) + + if (nextModule) { + // 바로 위 모듈을 찾는다. + rightRows++ + x = nextModule.x + y = nextModule.y + } else { + // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. + if (findRight) { + const topRightPoint = { x: x + width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2 + }) + findRight = false + } else { + const topLeftPoint = { x: x - width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2 + }) + findRight = true + } + + if (nextModule) { + // 바로 위 모듈을 찾는다. + rightRows++ + x = nextModule.x + y = nextModule.y + } else { + hasNextModule = false + } + } + } + + hasNextModule = true + x = startX + y = startY + + // 센터 찾는다. + while (hasNextModule) { + //바로 위에 있는지 확인한다. + let nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2 + }) + + 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 + + drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L') + drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R') + + if (rackQty === 3) { + //rack 갯수가 3개인 경우는 중간렉도 추가해줘야함 + drawRacks(centerRacks, rackQty, rackIntvlPct, module, direction, 'C') + } + }) + // 왼쪽아래에 모듈이 없는 모듈들 + leftExposedHalfBottomModules.forEach((module) => { + const { width, height } = module + let { x: startX, y: startY } = { ...module.getCenterPoint() } + let { x, y } = { ...module.getCenterPoint() } + const direction = 'south' + //TODO : 방향별로 가대 설치해야함 + + let leftRows = 1 + let hasNextModule = true + let findLeft = true + + //우선 절반을 나눈 뒤 왼쪽부터 찾는다. + while (hasNextModule) { + //바로 위에 있는지 확인한다. + let nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2 + }) + + if (nextModule) { + // 바로 위 모듈을 찾는다. + leftRows++ + x = nextModule.x + y = nextModule.y + } else { + // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. + if (findLeft) { + const topLeftPoint = { x: x - width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2 + }) + findLeft = false + } else { + const topRightPoint = { x: x + width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2 + }) + 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 + + drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L') + }) + // 오른쪽 아래에 모듈이 없는 모듈들 + rightExposedHalfBottomPoints.forEach((module) => { + const { width, height } = module + let { x: startX, y: startY } = { ...module.getCenterPoint() } + let { x, y } = { ...module.getCenterPoint() } + const direction = 'south' + //TODO : 방향별로 가대 설치해야함 + + let rightRows = 1 + let hasNextModule = true + let findRight = true + + // 오른쪽 찾는다. + while (hasNextModule) { + //바로 위에 있는지 확인한다. + let nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2 + }) + + if (nextModule) { + // 바로 위 모듈을 찾는다. + rightRows++ + x = nextModule.x + y = nextModule.y + } else { + // 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다. + if (findRight) { + const topRightPoint = { x: x + width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2 + }) + findRight = false + } else { + const topLeftPoint = { x: x - width / 2, y: y - height } + nextModule = centerPoints.find((centerPoint) => { + return Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2 + }) + 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으로 그려준다. + + drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R') + }) + }) + + /*switch (rackYn) { + case 'Y': { + // rack이 Y일 경우 rackQty가 필요함 + + break + } + case 'N': { + break + } + }*/ + // 지지금구 설치 + installBracket() + } + + const drawRacks = (rackInfos, rackQty, rackIntvlPct, module, direction = 'south', l) => { + const { width, height } = module + + let startPointX, startPointY + + switch (l) { + case 'L': { + // 왼쪽부분 시작 점 + if (direction === 'south') { + const x = module.left + const y = module.top + module.height / 2 + startPointX = x + width / rackIntvlPct + startPointY = y + height / 2 + break + } + } + + case 'R': { + // 오른쪽부분 시작 점 + if (direction === 'south') { + const x = module.left + module.width + const y = module.top + module.height / 2 + startPointX = x - width / rackIntvlPct + startPointY = y + height / 2 + break + } + } + case 'C': { + // 중간부분 시작점 + if (direction === 'south') { + startPointX = x + startPointY = y + height / 2 + 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, + supFitQty, + supFitIntvlPct, + rackLen, + rackId: itemId, + direction: 'top', + }) + + canvas.add(rack) + canvas.renderAll() + + startPointY -= rackLength + 3 + }) + + break + } + } + } + + const installBracket = () => { + const racks = canvas.getObjects().filter((obj) => obj.name === 'rack') + // name이 bracket인 객체를 찾아서 삭제 + canvas.getObjects().forEach((obj) => { + if (obj.name === 'bracket') { + canvas.remove(obj) + } + }) + canvas.renderAll() + + racks.forEach((rack) => { + const { x1, y1, x2, y2, direction, supFitQty, supFitIntvlPct, rackLen } = rack + + const moduleLength = 10 + + if (direction === 'top') { + const result = getBracketPoints(supFitQty, supFitIntvlPct) + + result.forEach((percent) => { + const bracket = new fabric.Rect({ + left: x2 - moduleLength / 3, + top: y2 + (rackLen / 10) * percent, + fill: 'green', + name: 'bracket', + width: moduleLength, + height: moduleLength, + selectable: false, + }) + + canvas.add(bracket) + }) + canvas.renderAll() + } + }) + } + + 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) { + points.sort((a, b) => b.y - a.y) + + return points.slice(0, 2) + } + + const calculateForApi = (moduleSurface) => { + const centerPoints = [] + const direction = moduleSurface.direction + const modules = moduleSurface.modules + + 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 = [] + + centerPoints.forEach((centerPoint, index) => { + const { 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': + bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2) + bottomLeftPoint = { x: x - width / 2, y: y + height } + bottomRightPoint = { x: x + width / 2, y: y + height } + break + case 'north': + bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2) + 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)) < 2 && Math.abs(centerPoint.y - y) < 2) + 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)) < 2 && Math.abs(centerPoint.y - y) < 2) + 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) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2, + ).length + rightBottomCnt = centerPoints.filter( + (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2, + ).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) => { + const { x, y, width, height } = centerPoint + + let topCell + let topLeftPoint + let topRightPoint + let leftTopCnt + let rightTopCnt + + switch (direction) { + case 'south': + topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2) + topLeftPoint = { x: x - width / 2, y: y - height } + topRightPoint = { x: x + width / 2, y: y - height } + break + case 'north': + topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2) + topLeftPoint = { x: x + width / 2, y: y + height } + topRightPoint = { x: x - width / 2, y: y + height } + break + case 'east': + topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2) + topLeftPoint = { x: x - width, y: y + height / 2 } + topRightPoint = { x: x - width, y: y - height / 2 } + break + case 'west': + topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2) + 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) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2, + ).length + rightTopCnt = centerPoints.filter( + (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2, + ).length + + if (leftTopCnt + rightTopCnt === 2) { + touchDimension++ + return + } + + if (leftTopCnt + rightTopCnt === 1) { + exposedHalfTop++ + halfTouchDimension++ + return + } + if (leftTopCnt + rightTopCnt === 0) { + exposedTop++ + } + }) + // 완전 노출 하면 계산 + + /*const cells = canvas.getObjects().filter((obj) => polygon.id === obj.parentId) + const points = cells.map((cell) => { + return cell.getCenterPoint() + })*/ + 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, + 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 } +}