모듈 기본세팅 수동 작업 추가
This commit is contained in:
parent
fee77f8563
commit
d222c60b89
@ -28,3 +28,4 @@ Allpainted : allPainted
|
||||
치수선: dimensionLine
|
||||
복도치수: planeSize
|
||||
실제치수: actualSize
|
||||
모듈설치면: moduleSetupSurface
|
||||
@ -111,6 +111,7 @@ export const POLYGON_TYPE = {
|
||||
ROOF: 'roof',
|
||||
WALL: 'wall',
|
||||
TRESTLE: 'trestle',
|
||||
MODULE_SETUP_SURFACE: 'moduleSetupSurface',
|
||||
}
|
||||
|
||||
export const SAVE_KEY = [
|
||||
|
||||
@ -5,18 +5,21 @@ import Module from '@/components/floor-plan/modal/basic/step/Module'
|
||||
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
|
||||
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
|
||||
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasSettingState } from '@/store/canvasAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
|
||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
|
||||
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [tabNum, setTabNum] = useState(1)
|
||||
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
|
||||
const canvasSetting = useRecoilValue(canvasSettingState)
|
||||
const orientationRef = useRef(null)
|
||||
|
||||
const { initEvent } = useEvent()
|
||||
const { makeModuleInstArea, manualModuleSetup, autoModuleSetup } = useModuleBasicSetting()
|
||||
const handleBtnNextStep = () => {
|
||||
if (tabNum === 1) {
|
||||
orientationRef.current.handleNextStep()
|
||||
@ -24,6 +27,14 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
setTabNum(tabNum + 1)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
makeModuleInstArea()
|
||||
|
||||
return () => {
|
||||
initEvent()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lx-2`}>
|
||||
@ -64,8 +75,12 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
)}
|
||||
{tabNum === 3 && (
|
||||
<>
|
||||
<button className="btn-frame modal mr5">{getMessage('modal.module.basic.setting.passivity.placement')}</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.module.basic.setting.auto.placement')}</button>
|
||||
<button className="btn-frame modal mr5" onClick={manualModuleSetup}>
|
||||
{getMessage('modal.module.basic.setting.passivity.placement')}
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={autoModuleSetup}>
|
||||
{getMessage('modal.module.basic.setting.auto.placement')}{' '}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
595
src/hooks/module/useModuleBasicSetting.js
Normal file
595
src/hooks/module/useModuleBasicSetting.js
Normal file
@ -0,0 +1,595 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
import offsetPolygon from '@/util/qpolygon-utils'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
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'
|
||||
|
||||
export function useModuleBasicSetting() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const setModuleInstSurface = useSetRecoilState(moduleSetupSurfaceState)
|
||||
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
|
||||
const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
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
|
||||
//모듈설치영역?? 생성
|
||||
const trestle = 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,
|
||||
})
|
||||
|
||||
canvas.add(trestle)
|
||||
//지붕면 선택 금지
|
||||
roof.set({
|
||||
selectable: false,
|
||||
})
|
||||
|
||||
//모듈설치면 클릭이벤트
|
||||
addTargetMouseEventListener('mousedown', trestle, function () {
|
||||
toggleSelection(trestle)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//설치 범위 지정 클릭 이벤트
|
||||
const toggleSelection = (polygon) => {
|
||||
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === polygon.parentId)
|
||||
//최초 선택일때
|
||||
if (!isExist) {
|
||||
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
|
||||
polygon.set({
|
||||
...polygon,
|
||||
strokeWidth: 3,
|
||||
strokeDashArray: [0],
|
||||
fill: 'transparent',
|
||||
})
|
||||
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
||||
//중복으로 들어가는걸 방지하기 위한 코드
|
||||
|
||||
canvas?.renderAll()
|
||||
selectedModuleInstSurfaceArray.push(polygon)
|
||||
} else {
|
||||
//선택후 재선택하면 선택안됨으로 변경
|
||||
polygon.set({
|
||||
...polygon,
|
||||
fill: 'transparent',
|
||||
strokeDashArray: [10, 4],
|
||||
strokeWidth: 1,
|
||||
})
|
||||
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
||||
|
||||
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
|
||||
const removeIndex = polygon.parentId
|
||||
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
|
||||
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
|
||||
}
|
||||
|
||||
canvas?.renderAll()
|
||||
setModuleInstSurface([...selectedModuleInstSurfaceArray])
|
||||
}
|
||||
|
||||
/**
|
||||
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
|
||||
* 확인 후 셀을 이동시킴
|
||||
*/
|
||||
const manualModuleSetup = () => {
|
||||
const trestlePolygons = 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 (trestlePolygons.length !== 0) {
|
||||
let fabricPolygon = null
|
||||
let inside = false
|
||||
let turfPolygon
|
||||
let manualDrawCells = moduleIsSetup // 앞에서 자동으로 했을때 추가됨
|
||||
let direction
|
||||
let trestlePolygon
|
||||
addCanvasMouseEventListener('mouse:move', (e) => {
|
||||
//마우스 이벤트 삭제 후 재추가
|
||||
const mousePoint = canvas.getPointer(e.e)
|
||||
|
||||
for (let i = 0; i < trestlePolygons.length; i++) {
|
||||
turfPolygon = polygonToTurfPolygon(trestlePolygons[i])
|
||||
trestlePolygon = trestlePolygons[i]
|
||||
direction = trestlePolygons[i].direction //도형의 방향
|
||||
let width = direction === 'south' || direction === 'north' ? 172 : 113
|
||||
let height = direction === 'south' || direction === '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')) //움직일때 일단 지워가면서 움직임
|
||||
|
||||
fabricPolygon = 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: trestlePolygons[i].parentId,
|
||||
})
|
||||
|
||||
canvas?.add(fabricPolygon) //움직여가면서 추가됨
|
||||
|
||||
/**
|
||||
* 스냅기능
|
||||
*/
|
||||
let snapDistance = 10
|
||||
let cellSnapDistance = 20
|
||||
|
||||
const trestleLeft = trestlePolygons[i].left
|
||||
const trestleTop = trestlePolygons[i].top
|
||||
const trestleRight = trestleLeft + trestlePolygons[i].width * trestlePolygons[i].scaleX
|
||||
const trestleBottom = trestleTop + trestlePolygons[i].height * trestlePolygons[i].scaleY
|
||||
const bigCenterY = (trestleTop + trestleTop + trestlePolygons[i].height) / 2
|
||||
|
||||
// 작은 폴리곤의 경계 좌표 계산
|
||||
const smallLeft = fabricPolygon.left
|
||||
const smallTop = fabricPolygon.top
|
||||
const smallRight = smallLeft + fabricPolygon.width * fabricPolygon.scaleX
|
||||
const smallBottom = smallTop + fabricPolygon.height * fabricPolygon.scaleY
|
||||
const smallCenterX = smallLeft + (fabricPolygon.width * fabricPolygon.scaleX) / 2
|
||||
const smallCenterY = smallTop + (fabricPolygon.height * fabricPolygon.scaleX) / 2
|
||||
|
||||
/**
|
||||
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
|
||||
*/
|
||||
if (manualDrawCells) {
|
||||
manualDrawCells.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) {
|
||||
fabricPolygon.left = holdCellLeft - width - 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 우측에 스냅
|
||||
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
||||
fabricPolygon.left = holdCellRight + 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 위쪽에 스냅
|
||||
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
|
||||
fabricPolygon.top = holdCellTop - height - 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 밑쪽에 스냅
|
||||
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
|
||||
fabricPolygon.top = holdCellBottom + 0.5
|
||||
}
|
||||
//가운데 -> 가운데
|
||||
if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX - width / 2
|
||||
}
|
||||
//왼쪽 -> 가운데
|
||||
if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX
|
||||
}
|
||||
// 오른쪽 -> 가운데
|
||||
if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX - width
|
||||
}
|
||||
//세로 가운데 -> 가운데
|
||||
if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY - height / 2
|
||||
}
|
||||
//위쪽 -> 가운데
|
||||
if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY
|
||||
}
|
||||
//아랫쪽 -> 가운데
|
||||
if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY - height
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 위쪽 변에 스냅
|
||||
if (Math.abs(smallTop - trestleTop) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop
|
||||
}
|
||||
|
||||
// 아래쪽 변에 스냅
|
||||
if (Math.abs(smallTop + fabricPolygon.height * fabricPolygon.scaleY - (trestleTop + trestlePolygons[i].height)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height - fabricPolygon.height * fabricPolygon.scaleY
|
||||
}
|
||||
|
||||
// 왼쪽변에 스냅
|
||||
if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft
|
||||
}
|
||||
//오른쪽 변에 스냅
|
||||
if (Math.abs(smallRight - trestleRight) < snapDistance) {
|
||||
fabricPolygon.left = trestleRight - fabricPolygon.width * fabricPolygon.scaleX
|
||||
}
|
||||
|
||||
if (direction === 'south' || direction === 'north') {
|
||||
// 모듈왼쪽이 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallLeft - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2
|
||||
}
|
||||
|
||||
// 모듈이 가운데가 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallCenterX - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - (fabricPolygon.width * fabricPolygon.scaleX) / 2
|
||||
}
|
||||
|
||||
// 모듈오른쪽이 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallRight - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - fabricPolygon.width * fabricPolygon.scaleX
|
||||
}
|
||||
} else {
|
||||
// 모듈이 가로중앙선에 스냅
|
||||
if (Math.abs(smallTop + fabricPolygon.height / 2 - bigCenterY) < snapDistance) {
|
||||
fabricPolygon.top = bigCenterY - fabricPolygon.height / 2
|
||||
}
|
||||
|
||||
if (Math.abs(smallTop - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2
|
||||
}
|
||||
// 모듈 밑면이 가로중앙선에 스냅
|
||||
if (Math.abs(smallBottom - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2 - fabricPolygon.height * fabricPolygon.scaleY
|
||||
}
|
||||
}
|
||||
|
||||
fabricPolygon.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 (fabricPolygon) {
|
||||
const rectPoints = [
|
||||
{ x: fabricPolygon.left + 0.5, y: fabricPolygon.top + 0.5 },
|
||||
{ x: fabricPolygon.left + 0.5 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.5 },
|
||||
{
|
||||
x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.5,
|
||||
y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5,
|
||||
},
|
||||
{ x: fabricPolygon.left + 0.5, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5 },
|
||||
]
|
||||
|
||||
fabricPolygon.set({ points: rectPoints })
|
||||
const tempTurfModule = polygonToTurfPolygon(fabricPolygon)
|
||||
|
||||
//도머 객체를 가져옴
|
||||
if (batchObjects) {
|
||||
batchObjects.forEach((object) => {
|
||||
const dormerTurfPolygon = polygonToTurfPolygon(object) //turf객체로 변환
|
||||
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
|
||||
//겹치면 안됨
|
||||
if (intersection) {
|
||||
alert('도머위에 모듈을 올릴 수 없습니다.')
|
||||
isIntersection = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!isIntersection) return
|
||||
|
||||
fabricPolygon.setCoords() //좌표 재정렬
|
||||
|
||||
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
|
||||
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
|
||||
const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell))) //겹치는지 확인
|
||||
if (!isOverlap) {
|
||||
//안겹치면 넣는다
|
||||
fabricPolygon.setCoords()
|
||||
fabricPolygon.set({ name: 'cell', fill: '#BFFD9F' })
|
||||
manualDrawCells.push(fabricPolygon) //모듈배열에 추가
|
||||
//해당 모듈에 프로퍼티로 넣는다
|
||||
trestlePolygon.set({
|
||||
modules: manualDrawCells,
|
||||
})
|
||||
} else {
|
||||
alert('셀끼리 겹치면 안되죠?')
|
||||
}
|
||||
} else {
|
||||
alert('나갔죠?!!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const coordToTurfPolygon = (points) => {
|
||||
const coordinates = points.map((point) => [point.x, point.y])
|
||||
coordinates.push(coordinates[0])
|
||||
return turf.polygon([coordinates])
|
||||
}
|
||||
|
||||
const polygonToTurfPolygon = (polygon) => {
|
||||
const coordinates = polygon.points.map((point) => [point.x, point.y])
|
||||
coordinates.push(coordinates[0])
|
||||
return turf.polygon(
|
||||
[coordinates],
|
||||
{},
|
||||
{
|
||||
parentId: polygon.parentId,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const autoModuleSetup = () => {
|
||||
const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle' && obj.selected)
|
||||
const notSelectedTrestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle' && !obj.selected)
|
||||
|
||||
const dormerTrestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'dormerTrestle') //도머 객체
|
||||
|
||||
if (trestlePolygons.length === 0) {
|
||||
alert('가대가 없습니다.')
|
||||
return
|
||||
}
|
||||
|
||||
if (drewRoofCells.length > 0) {
|
||||
alert('기존 셀은 제거됩니다.')
|
||||
}
|
||||
|
||||
notSelectedTrestlePolygons.forEach((trestle) => {
|
||||
trestle.cells.forEach((cell) => {
|
||||
canvas?.remove(cell)
|
||||
})
|
||||
trestle.cells = []
|
||||
})
|
||||
|
||||
const drawCellsArray = []
|
||||
trestlePolygons.forEach((trestle, index) => {
|
||||
trestle.fire('mousedown')
|
||||
let maxLengthLine = trestle.lines.reduce((acc, cur) => {
|
||||
return acc.length > cur.length ? acc : cur
|
||||
})
|
||||
|
||||
const turfTrestlePolygon = polygonToTurfPolygon(trestle) //폴리곤을 turf 객체로 변환
|
||||
|
||||
const containsDormerTrestlePolygons = dormerTrestlePolygons.filter((dormerTrestle) => {
|
||||
// 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
|
||||
return (
|
||||
turf.booleanContains(turfTrestlePolygon, polygonToTurfPolygon(dormerTrestle)) ||
|
||||
turf.booleanWithin(polygonToTurfPolygon(dormerTrestle), turfTrestlePolygon)
|
||||
)
|
||||
})
|
||||
|
||||
let difference = turfTrestlePolygon //기본 객체(면형상)
|
||||
|
||||
if (containsDormerTrestlePolygons.length > 0) {
|
||||
//turf로 도머를 제외시키는 로직
|
||||
for (let i = 0; i < containsDormerTrestlePolygons.length; i++) {
|
||||
if (i === 0) {
|
||||
difference = turf.difference(turf.featureCollection([turfTrestlePolygon, polygonToTurfPolygon(containsDormerTrestlePolygons[i])])) //한 면에 도머가 1개일때
|
||||
} else {
|
||||
if (difference) {
|
||||
difference = turf.difference(turf.featureCollection([difference, polygonToTurfPolygon(containsDormerTrestlePolygons[i])])) //한면에 도머가 여러개일때 계속 제외시킴
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bbox = turf.bbox(difference)
|
||||
let width = maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left' ? 172.2 : 113.4
|
||||
let height = maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left' ? 113.4 : 172.2
|
||||
|
||||
//배치면때는 방향쪽으로 패널이 넓게 누워져야함
|
||||
if (trestle.direction !== undefined) {
|
||||
width = trestle.direction === 'south' || trestle.direction === 'north' ? 172.2 : 113.4
|
||||
height = trestle.direction === 'south' || trestle.direction === 'north' ? 113.4 : 172.2
|
||||
}
|
||||
const cols = Math.floor((bbox[2] - bbox[0]) / width)
|
||||
const rows = Math.floor((bbox[3] - bbox[1]) / height)
|
||||
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
for (let row = 0; row <= rows; row++) {
|
||||
let x = 0,
|
||||
y = 0,
|
||||
square = [],
|
||||
margin = 0
|
||||
|
||||
if (trestle.direction !== undefined) {
|
||||
//배치면 처림 방향이 정해져있는 경우
|
||||
|
||||
if (trestle.direction === 'south' || trestle.direction === 'north') {
|
||||
//남,북
|
||||
margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨
|
||||
if (trestle.direction === 'south') {
|
||||
//남쪽
|
||||
x = col === 0 ? trestle.left + margin : bbox[0] + col * width + margin //상하 위치 기준이면 좌우 가운데 정렬한다
|
||||
y = bbox[3] - row * height
|
||||
} else {
|
||||
//북쪽
|
||||
x = col === 0 ? trestle.left + margin : bbox[0] + col * width + margin
|
||||
y = bbox[1] + row * height
|
||||
}
|
||||
} else if (trestle.direction === 'east' || trestle.direction === 'west') {
|
||||
//동쪽
|
||||
margin = (bbox[3] - bbox[1] - rows * height) / 2
|
||||
if (trestle.direction === 'east') {
|
||||
x = bbox[2] - col * width
|
||||
y = rows === 0 ? trestle.top + margin : bbox[1] + row * height + margin //좌우 위치 기준이면 상하 가운데 정렬한다
|
||||
} else {
|
||||
x = bbox[0] + col * width
|
||||
y = rows === 0 ? trestle.top + margin : bbox[1] + row * height + margin
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//방향이 없는 경우 ex) 템플릿
|
||||
x = bbox[0] + col * width
|
||||
y = bbox[1] + row * height
|
||||
}
|
||||
|
||||
square = [
|
||||
[x, y],
|
||||
[x + width, y],
|
||||
[x + width, y + height],
|
||||
[x, y + height],
|
||||
[x, y],
|
||||
]
|
||||
|
||||
const squarePolygon = turf.polygon([square])
|
||||
|
||||
const disjointFromTrestle = turf.booleanContains(turfTrestlePolygon, squarePolygon) || turf.booleanWithin(squarePolygon, turfTrestlePolygon)
|
||||
|
||||
if (disjointFromTrestle) {
|
||||
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
||||
const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
||||
|
||||
if (containsDormerTrestlePolygons.length > 0) {
|
||||
//도머가 있으면 적용되는 로직
|
||||
const isDisjoint = containsDormerTrestlePolygons.some((dormerTrestle) => {
|
||||
return turf.booleanDisjoint(squarePolygon, polygonToTurfPolygon(dormerTrestle)) //도머가 여러개일수있으므로 겹치는게 있다면...
|
||||
})
|
||||
if (isDisjoint) {
|
||||
const fabricPolygon = new QPolygon(points, {
|
||||
fill: '#BFFD9F',
|
||||
stroke: 'black',
|
||||
selectable: true, // 선택 가능하게 설정
|
||||
lockMovementX: false, // X 축 이동 잠금
|
||||
lockMovementY: false, // Y 축 이동 잠금
|
||||
lockRotation: false, // 회전 잠금
|
||||
lockScalingX: false, // X 축 크기 조정 잠금
|
||||
lockScalingY: false, // Y 축 크기 조정 잠금
|
||||
opacity: 0.8,
|
||||
parentId: trestle.parentId,
|
||||
})
|
||||
canvas?.add(fabricPolygon)
|
||||
drawCellsArray.push(fabricPolygon)
|
||||
}
|
||||
} else {
|
||||
//도머가 없을땐 그냥 그림
|
||||
const fabricPolygon = 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: trestle.parentId,
|
||||
lineCol: col,
|
||||
lineRow: row,
|
||||
})
|
||||
canvas?.add(fabricPolygon)
|
||||
drawCellsArray.push(fabricPolygon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let drawRoofCells
|
||||
// if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') {
|
||||
// drawRoofCells = trestle.fillCell({ width: 113.4, height: 172.2, padding: 0 })
|
||||
// trestle.direction = 'south'
|
||||
// } else {
|
||||
// drawRoofCells = trestle.fillCell({ width: 172.2, height: 113.4, padding: 0 })
|
||||
// trestle.direction = 'east'
|
||||
// }
|
||||
|
||||
// drawRoofCells.forEach((cell) => {
|
||||
// drawCellsArray.push(cell)
|
||||
// })
|
||||
|
||||
/**
|
||||
* 추후에 가대까지 완료하면 그룹시켜버림
|
||||
*/
|
||||
// const groupCellObj = canvas
|
||||
// ?.getObjects()
|
||||
// .filter(
|
||||
// (obj) =>
|
||||
// obj?.parentId === trestle.parentId ||
|
||||
// obj?.id === trestle.parentId ||
|
||||
// (obj?.name === 'arrow' && obj?.parent.id === trestle.parentId) ||
|
||||
// (obj?.name === 'directionText' && obj?.parent.parent.id === trestle.parentId),
|
||||
// )
|
||||
|
||||
// console.log('groupCellObj', groupCellObj)
|
||||
|
||||
// canvas?.add(
|
||||
// new fabric.Group(groupCellObj, {
|
||||
// name: 'cellGroup',
|
||||
// originX: 'center',
|
||||
// originY: 'center',
|
||||
// }),
|
||||
// )
|
||||
})
|
||||
|
||||
setDrewRoofCells(drawCellsArray)
|
||||
}
|
||||
|
||||
return {
|
||||
makeModuleInstArea,
|
||||
manualModuleSetup,
|
||||
autoModuleSetup,
|
||||
}
|
||||
}
|
||||
@ -225,7 +225,9 @@ export function useCanvasEvent() {
|
||||
if (deselected?.length > 0) {
|
||||
deselected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'black' })
|
||||
if (obj.name !== 'moduleInstSurface') {
|
||||
obj.set({ stroke: 'black' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -219,6 +219,12 @@ export function useEvent() {
|
||||
mouseEventListeners.current.length = 0 // 배열 초기화
|
||||
}
|
||||
|
||||
const addTargetMouseEventListener = (eventType, target, handler) => {
|
||||
target.off(eventType)
|
||||
target.on(eventType, handler)
|
||||
mouseEventListeners.current.push({ eventType, handler })
|
||||
}
|
||||
|
||||
/**
|
||||
* document 이벤트의 경우 이 함수를 통해서만 등록
|
||||
* @param eventType
|
||||
@ -264,6 +270,7 @@ export function useEvent() {
|
||||
return {
|
||||
addDocumentEventListener,
|
||||
addCanvasMouseEventListener,
|
||||
addTargetMouseEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
|
||||
@ -363,3 +363,24 @@ export const showAngleUnitSelector = selector({
|
||||
return roofAngleSet === 'slope' ? '寸' : '°'
|
||||
},
|
||||
})
|
||||
|
||||
export const moduleSetupSurfaceState = atom({
|
||||
key: 'moduleSetupSurfaceState',
|
||||
default: [],
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const moduleInstSurfaceSelector = selector({
|
||||
key: 'moduleInstSurfaceSelector',
|
||||
get: ({ get, parentId }) => {
|
||||
const moduleSetupSurfaceStateValue = get(moduleSetupSurfaceState)
|
||||
return moduleSetupSurfaceStateValue.filter((obj) => obj.parentId === parentId)
|
||||
},
|
||||
})
|
||||
|
||||
//셀 그린 이후에 생성하는 state
|
||||
export const moduleIsSetupState = atom({
|
||||
key: 'moduleIsSetupState',
|
||||
default: [],
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -792,7 +792,7 @@ export const rectToPolygon = (rect) => {
|
||||
}
|
||||
|
||||
//면형상 선택 클릭시 지붕 패턴 입히기
|
||||
export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
export function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false) {
|
||||
const ratio = window.devicePixelRatio || 1
|
||||
|
||||
let width = 265 / 10
|
||||
@ -820,6 +820,12 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4
|
||||
ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
|
||||
|
||||
if (trestleMode) {
|
||||
ctx.strokeStyle = 'black'
|
||||
ctx.lineWidth = 0.2
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
|
||||
if (polygon.direction === 'east' || polygon.direction === 'west') {
|
||||
offset = roofStyle === 1 ? 0 : patternSize.height / 2
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
@ -830,7 +836,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
|
||||
@ -842,7 +848,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(xStart, y) // 선 시작점
|
||||
ctx.lineTo(xEnd, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
|
||||
}
|
||||
}
|
||||
@ -855,7 +861,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(0, y) // 선 시작점
|
||||
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
|
||||
}
|
||||
|
||||
@ -868,7 +874,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user