qcast-front/src/hooks/module/useModuleBasicSetting.js
2024-11-27 17:16:07 +09:00

964 lines
36 KiB
JavaScript

import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { QLine } from '@/components/fabric/QLine'
import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
import * as turf from '@turf/turf'
import { EventContext } from '@/app/floor-plan/EventProvider'
export function useModuleBasicSetting() {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent()
// const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const [flowModuleLine, setFlowModuleLine] = useState({})
let selectedModuleInstSurfaceArray = []
const makeModuleInstArea = () => {
//지붕 객체 반환
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
if (!roofs) {
return
}
roofs.forEach((roof) => {
setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경
const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset
//모듈설치영역?? 생성
let setupSurface = new QPolygon(offsetPoints, {
stroke: 'red',
fill: 'transparent',
strokeDashArray: [10, 4],
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
selectable: true,
parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌
name: POLYGON_TYPE.MODULE_SETUP_SURFACE,
flowDirection: roof.direction,
flipX: roof.flipX,
flipY: roof.flipY,
})
setupSurface.setViewLengthText(false)
canvas.add(setupSurface)
if (setupSurface.flowDirection === 'south' || setupSurface.flowDirection === 'north') {
setFlowModuleLine(bottomTopFlowLine(setupSurface))
} else {
setFlowModuleLine(leftRightFlowLine(setupSurface))
}
//지붕면 선택 금지
roof.set({
selectable: false,
})
//모듈설치면 클릭이벤트
addTargetMouseEventListener('mousedown', setupSurface, function () {
toggleSelection(setupSurface)
})
})
}
//설치 범위 지정 클릭 이벤트
const toggleSelection = (setupSurface) => {
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
//최초 선택일때
if (!isExist) {
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
setupSurface.set({
...setupSurface,
strokeWidth: 3,
strokeDashArray: [0],
fill: 'transparent',
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
//중복으로 들어가는걸 방지하기 위한 코드
canvas?.renderAll()
selectedModuleInstSurfaceArray.push(setupSurface)
} else {
//선택후 재선택하면 선택안됨으로 변경
setupSurface.set({
...setupSurface,
fill: 'transparent',
strokeDashArray: [10, 4],
strokeWidth: 1,
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
const removeIndex = setupSurface.parentId
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
}
canvas?.renderAll()
setModuleSetupSurface([...selectedModuleInstSurfaceArray])
}
/**
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
* 확인 후 셀을 이동시킴
*/
const manualModuleSetup = () => {
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
const batchObjects = canvas
?.getObjects()
.filter(
(obj) =>
obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
obj.name === BATCH_TYPE.SHADOW,
) //도머s 객체
if (moduleSetupSurfaces.length !== 0) {
let tempModule
let manualDrawModules = moduleIsSetup // 앞에서 자동으로 했을때 추가됨
let inside = false
let turfPolygon
let flowDirection
let trestlePolygon
addCanvasMouseEventListener('mouse:move', (e) => {
//마우스 이벤트 삭제 후 재추가
const mousePoint = canvas.getPointer(e.e)
for (let i = 0; i < moduleSetupSurfaces.length; i++) {
turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i])
trestlePolygon = moduleSetupSurfaces[i]
flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향
let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113
let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172
const points = [
{ x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
{ x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
{ x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
{ x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
]
const turfPoints = coordToTurfPolygon(points)
if (turf.booleanWithin(turfPoints, turfPolygon)) {
let isDrawing = false
if (isDrawing) return
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
tempModule = new fabric.Rect({
fill: 'white',
stroke: 'black',
strokeWidth: 1,
width: width,
height: height,
left: mousePoint.x - width / 2,
top: mousePoint.y - height / 2,
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
opacity: 0.8,
name: 'tempModule',
parentId: moduleSetupSurfaces[i].parentId,
})
canvas?.add(tempModule) //움직여가면서 추가됨
/**
* 스냅기능
*/
let snapDistance = 10
let cellSnapDistance = 20
const trestleLeft = moduleSetupSurfaces[i].left
const trestleTop = moduleSetupSurfaces[i].top
const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX
const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY
const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
// 작은 폴리곤의 경계 좌표 계산
const smallLeft = tempModule.left
const smallTop = tempModule.top
const smallRight = smallLeft + tempModule.width * tempModule.scaleX
const smallBottom = smallTop + tempModule.height * tempModule.scaleY
const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2
const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2
/**
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
*/
if (manualDrawModules) {
manualDrawModules.forEach((cell) => {
const holdCellLeft = cell.left
const holdCellTop = cell.top
const holdCellRight = holdCellLeft + cell.width * cell.scaleX
const holdCellBottom = holdCellTop + cell.height * cell.scaleY
const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2
const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2
//설치된 셀에 좌측에 스냅
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
tempModule.left = holdCellLeft - width - 0.5
}
//설치된 셀에 우측에 스냅
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
tempModule.left = holdCellRight + 0.5
}
//설치된 셀에 위쪽에 스냅
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
tempModule.top = holdCellTop - height - 0.5
}
//설치된 셀에 밑쪽에 스냅
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
tempModule.top = holdCellBottom + 0.5
}
//가운데 -> 가운데
if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX - width / 2
}
//왼쪽 -> 가운데
if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX
}
// 오른쪽 -> 가운데
if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX - width
}
//세로 가운데 -> 가운데
if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY - height / 2
}
//위쪽 -> 가운데
if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY
}
//아랫쪽 -> 가운데
if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY - height
}
})
}
// 위쪽 변에 스냅
if (Math.abs(smallTop - trestleTop) < snapDistance) {
tempModule.top = trestleTop
}
// 아래쪽 변에 스냅
if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY
}
// 왼쪽변에 스냅
if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
tempModule.left = trestleLeft
}
//오른쪽 변에 스냅
if (Math.abs(smallRight - trestleRight) < snapDistance) {
tempModule.left = trestleRight - tempModule.width * tempModule.scaleX
}
if (flowDirection === 'south' || flowDirection === 'north') {
// 모듈왼쪽이 세로중앙선에 붙게 스냅
if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2
}
// 모듈이 가운데가 세로중앙선에 붙게 스냅
if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2
}
// 모듈오른쪽이 세로중앙선에 붙게 스냅
if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
}
} else {
// 모듈이 가로중앙선에 스냅
if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) {
tempModule.top = bigCenterY - tempModule.height / 2
}
if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
}
// 모듈 밑면이 가로중앙선에 스냅
if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY
}
}
tempModule.setCoords()
canvas?.renderAll()
inside = true
break
} else {
inside = false
}
}
if (!inside) {
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule'))
canvas?.renderAll()
}
})
addCanvasMouseEventListener('mouse:up', (e) => {
let isIntersection = true
if (!inside) return
if (tempModule) {
const rectPoints = [
{ x: tempModule.left + 0.5, y: tempModule.top + 0.5 },
{ x: tempModule.left + 0.5 + tempModule.width * tempModule.scaleX, y: tempModule.top + 0.5 },
{
x: tempModule.left + tempModule.width * tempModule.scaleX + 0.5,
y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5,
},
{ x: tempModule.left + 0.5, y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5 },
]
tempModule.set({ points: rectPoints })
const tempTurfModule = polygonToTurfPolygon(tempModule)
//도머 객체를 가져옴
if (batchObjects) {
batchObjects.forEach((object) => {
let dormerTurfPolygon
if (object.type === 'group') {
//도머는 그룹형태임
dormerTurfPolygon = batchObjectGroupToTurfPolygon(object)
} else {
//개구, 그림자
dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object))
}
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
//겹치면 안됨
if (intersection) {
alert('도머위에 모듈을 올릴 수 없습니다.')
isIntersection = false
}
})
}
if (!isIntersection) return
tempModule.setCoords() //좌표 재정렬
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
if (!isOverlap) {
//안겹치면 넣는다
tempModule.setCoords()
tempModule.set({ name: 'module', fill: '#BFFD9F' })
manualDrawModules.push(tempModule) //모듈배열에 추가
//해당 모듈에 프로퍼티로 넣는다
trestlePolygon.set({
modules: manualDrawModules,
})
} else {
alert('셀끼리 겹치면 안되죠?')
}
} else {
alert('나갔죠?!!')
}
}
})
}
}
//자동 모듈 설치(그리드 방식)
const autoModuleSetup = (placementRef) => {
const isChidori = placementRef.isChidori.current
const setupLocation = placementRef.setupLocation.current
const isMaxSetup = placementRef.isMaxSetup.current
initEvent()
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
?.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것
const batchObjects = canvas
?.getObjects()
.filter(
(obj) =>
obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
obj.name === BATCH_TYPE.SHADOW,
) //도머s 객체
if (moduleSetupSurfaces.length === 0) {
alert('선택된 모듈 설치면이 없습니다.')
return
}
if (moduleIsSetup.length > 0) {
alert('기존 모듈은 제거됩니다.')
moduleIsSetup.forEach((module) => {
canvas?.remove(module)
})
}
notSelectedTrestlePolygons.forEach((obj) => {
if (obj.modules) {
obj.modules.forEach((module) => {
canvas?.remove(module)
})
obj.modules = []
}
})
const moduleSetupArray = []
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
moduleSetupSurface.fire('mousedown')
const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
return acc.length > cur.length ? acc : cur
})
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
const containsBatchObjects = batchObjects.filter((batchObject) => {
let convertBatchObject
if (batchObject.type === 'group') {
//도머는 그룹형태임
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
} else {
//개구, 그림자
batchObject.set({
points: rectToPolygon(batchObject),
})
canvas?.renderAll() // set된걸 바로 적용하기 위해
convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환
}
// 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface)
})
let difference = turfModuleSetupSurface //기본 객체(면형상)
if (containsBatchObjects.length > 0) {
//turf로 도머를 제외시키는 로직
for (let i = 0; i < containsBatchObjects.length; i++) {
let convertBatchObject
if (containsBatchObjects[i].type === 'group') {
convertBatchObject = batchObjectGroupToTurfPolygon(containsBatchObjects[i])
} else {
convertBatchObject = polygonToTurfPolygon(containsBatchObjects[i])
}
}
}
let width = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 172.2 : 113.4
let height = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 113.4 : 172.2
//배치면때는 방향쪽으로 패널이 넓게 누워져야함
if (moduleSetupSurface.flowDirection !== undefined) {
width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4
height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2
}
let square
let startPoint, endPoint
if (setupLocation === 'eaves') {
if (moduleSetupSurface.flowDirection === 'south') {
startPoint = flowModuleLine.find((obj) => obj.target === 'bottom')
endPoint = flowModuleLine.find((obj) => obj.target === 'top')
const totalHeight = endPoint.y1 - startPoint.y1
const diffHeight = Math.abs(totalHeight / height)
let leftMargin = 0
let bottomMargin = 0
for (let i = 0; i < diffHeight; i++) {
leftMargin = i === 0 ? 1 : 0
bottomMargin = i === 0 ? 0 : 1
square = [
[startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
[startPoint.x1 + leftMargin, startPoint.y1 - bottomMargin],
[startPoint.x1 + leftMargin + width, startPoint.y1 - bottomMargin],
[startPoint.x1 + leftMargin + width, startPoint.y1 - height - bottomMargin],
[startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
]
const squarePolygon = turf.polygon([square])
//설치면 안에 있는지 확인
const disjointFromTrestle =
turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
if (disjointFromTrestle) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
if (containsBatchObjects.length > 0) {
let convertBatchObject
//도머가 있으면 적용되는 로직
const isDisjoint = containsBatchObjects.every((batchObject) => {
if (batchObject.type === 'group') {
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
} else {
convertBatchObject = polygonToTurfPolygon(batchObject)
}
return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면...
})
if (isDisjoint) {
const tempModule = new QPolygon(points, {
fill: '#BFFD9F',
stroke: 'black',
strokeWidth: 0.1,
selectable: true, // 선택 가능하게 설정
lockMovementX: false, // X 축 이동 잠금
lockMovementY: false, // Y 축 이동 잠금
lockRotation: false, // 회전 잠금
lockScalingX: false, // X 축 크기 조정 잠금
lockScalingY: false, // Y 축 크기 조정 잠금
opacity: 0.8,
parentId: moduleSetupSurface.parentId,
name: 'module',
})
tempModule.setViewLengthText(false)
canvas?.add(tempModule)
moduleSetupArray.push(tempModule)
}
} else {
//도머가 없을땐 그냥 그림
const tempModule = new QPolygon(points, {
fill: '#BFFD9F',
stroke: 'black',
selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
opacity: 0.8,
parentId: moduleSetupSurface.parentId,
name: 'module',
})
canvas?.add(tempModule)
moduleSetupArray.push(tempModule)
}
startPoint = { x1: points[0].x, y1: points[0].y, x2: points[3].x, y2: points[3].y }
}
}
}
} else if (setupLocation === 'ridge') {
} else {
}
moduleSetupSurface.set({ modules: moduleSetupArray })
})
setModuleIsSetup(moduleSetupArray)
console.log(calculateForApi(moduleSetupArray))
}
const calculateForApi = (moduleSetupArray) => {
const centerPoints = []
moduleSetupArray.forEach((module, index) => {
module.tempIndex = index
const { x, y } = module.getCenterPoint()
const { width, height } = module
centerPoints.push({ x, y, width, height, index })
const circle = new fabric.Circle({
radius: 5,
fill: 'red',
name: 'redCircle',
left: x - 5,
top: y - 5,
index: index,
selectable: false,
})
canvas.add(circle)
})
//완전 노출 하면
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가 있는지 확인
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
if (bottomCell.length === 1) {
touchDimension++
return
}
const bottomLeftPoint = { x: x - width / 2, y: y + height }
const bottomRightPoint = { x: x + width / 2, y: y + height }
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
const leftBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
).length
const rightBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
).length
if (leftBottomCnt + rightBottomCnt === 2) {
touchDimension++
return
}
if (leftBottomCnt + rightBottomCnt === 1) {
halfTouchDimension++
exposedHalfBottom++
return
}
if (leftBottomCnt + rightBottomCnt === 0) {
exposedBottom++
return
}
})
// 노출상면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
if (topCell.length === 1) {
return
}
const topLeftPoint = { x: x - width / 2, y: y - height }
const topRightPoint = { x: x + width / 2, y: y - height }
const leftTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
const rightTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
if (leftTopCnt + rightTopCnt === 1) {
exposedHalfTop++
return
}
if (leftTopCnt + rightTopCnt === 0) {
exposedTop++
return
}
})
return {
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
}
}
const coordToTurfPolygon = (points) => {
const coordinates = points.map((point) => [point.x, point.y])
coordinates.push(coordinates[0])
return turf.polygon([coordinates])
}
const polygonToTurfPolygon = (object) => {
let coordinates
coordinates = object.points.map((point) => [point.x, point.y])
coordinates.push(coordinates[0])
return turf.polygon(
[coordinates],
{},
{
parentId: object.parentId,
},
)
}
const batchObjectGroupToTurfPolygon = (group) => {
const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon')
let allPoints = []
polygons.forEach((obj) => allPoints.push(...obj.get('points')))
const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y])))
const hull = turf.concave(points, { tolerance: 0.1 })
return hull
}
const bottomTopFlowLine = (surface) => {
const flowArray = []
const bottomFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
flowArray.push(bottomFlow)
const topFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
flowArray.push(topFlow)
let idx = 0
let rtnObjArray = []
flowArray.forEach((center) => {
const linesArray = new Array()
surface.lines.filter((line) => {
if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
linesArray.push(line)
}
})
let coords = []
if (center.index === 0) {
coords = [
{ x: linesArray[0].x2, y: linesArray[0].y2 },
{ x: center.x1, y: center.y1 },
{ x: linesArray[1].x1, y: linesArray[1].y1 },
]
} else {
coords = [
{ x: linesArray[0].x1, y: linesArray[0].y1 },
{ x: center.x1, y: center.y1 },
{ x: linesArray[1].x2, y: linesArray[1].y2 },
]
}
const adjust1 = coords[0].x - coords[1].x
const height1 = coords[1].y - coords[0].y
const angle1 = Math.abs(Math.round(Math.atan(height1 / adjust1) * (180 / Math.PI) * 1000) / 1000)
const adjust2 = coords[2].x - coords[1].x
const height2 = coords[2].y - coords[1].y
const angle2 = Math.abs(Math.round(Math.atan(height2 / adjust2) * (180 / Math.PI) * 1000) / 1000)
const angle3 = 180 - (angle1 + angle2)
const charlie = 173.3 + 3 // 평행선길이 약간 여유를 줌
const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
const sign = Math.sign(coords[0].y - coords[1].y) // 진행방향
const top = coords[1].y + sign * h // 변경되는 높이 좌표 값
// const line3 = new QLine([coords[1].x, coords[1].y, coords[1].x, top], {
// stroke: 'blue',
// strokeWidth: 1,
// selectable: true,
// })
// // canvas?.add(line3)
const pointX1 = coords[0].x + ((coords[0].y - top) / (coords[0].y - coords[1].y)) * (coords[1].x - coords[0].x)
const pointY1 = top
const pointX2 = coords[2].x + ((coords[2].y - top) / (coords[2].y - coords[1].y)) * (coords[1].x - coords[2].x)
const pointY2 = top
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
})
canvas?.add(finalLine)
canvas?.renderAll()
const rtnObj = { target: idx === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
rtnObjArray.push(rtnObj)
++idx
})
return rtnObjArray
}
const leftRightFlowLine = (surface) => {
const flowArray = []
const leftFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
flowArray.push(leftFlow)
const rightFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
flowArray.push(rightFlow)
let idx = 0
let rtnObjArray = []
flowArray.forEach((center) => {
const linesArray = surface.lines.filter((line) => {
if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
return line
}
})
let coords = []
if (center.index === 0) {
coords = [
{ x: linesArray[1].x1, y: linesArray[1].y1 },
{ x: center.x1, y: center.y1 },
{ x: linesArray[0].x2, y: linesArray[0].y2 },
]
} else {
coords = [
{ x: linesArray[0].x1, y: linesArray[0].y1 },
{ x: center.x1, y: center.y1 },
{ x: linesArray[1].x2, y: linesArray[1].y2 },
]
}
const adjust1 = coords[0].x - coords[1].x
const height1 = coords[1].y - coords[0].y
const angle1 = Math.abs(Math.round(Math.atan(adjust1 / height1) * (180 / Math.PI) * 1000) / 1000)
const adjust2 = coords[2].x - coords[1].x
const height2 = coords[2].y - coords[1].y
const angle2 = Math.abs(Math.round(Math.atan(adjust2 / height2) * (180 / Math.PI) * 1000) / 1000)
const angle3 = 180 - (angle1 + angle2)
const charlie = 173.3 + 3 // 평행선길이 약간 여유를줌
const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
const sign = Math.sign(coords[0].x - coords[1].x) // 진행방향
const top = coords[1].x + sign * h // 변경되는 높이 좌표 값
// const line3 = new QLine([coords[1].x, coords[1].y, top, coords[1].y], {
// stroke: 'blue',
// strokeWidth: 1,
// selectable: true,
// })
// canvas?.add(line3)
const pointX1 = top
const pointY1 = coords[0].y + ((coords[0].x - top) / (coords[0].x - coords[1].x)) * (coords[1].y - coords[0].y)
const pointX2 = top
const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y)
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
})
canvas?.add(finalLine)
canvas?.renderAll()
const rtnObj = { target: idx === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
rtnObjArray.push(rtnObj)
++idx
})
}
const findSetupSurfaceMaxLines = (surface) => {
const leftFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
return { x1: line.x1, y1: line.y1 }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
const rightFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
return { x1: line.x1, y1: line.y1 }
}
return acc
},
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
const topFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
return { x1: line.x1, y1: line.y1 }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
const bottomFlow = surface.lines.reduce(
(acc, line, index) => {
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
return { x1: line.x1, y1: line.y1 }
}
return acc
},
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
const obj = {
left: leftFlow,
right: rightFlow,
top: topFlow,
bottom: bottomFlow,
}
return obj
}
return {
makeModuleInstArea,
manualModuleSetup,
autoModuleSetup,
}
}