회로 할당 작업중

This commit is contained in:
hyojun.choi 2025-01-14 18:24:59 +09:00
parent 982738939a
commit e885e7b939
4 changed files with 759 additions and 246 deletions

View File

@ -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]

View File

@ -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))

View File

@ -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])

View File

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