2700 lines
116 KiB
JavaScript
2700 lines
116 KiB
JavaScript
import { useState } from 'react'
|
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
|
import { canvasSettingState, canvasState, checkedModuleState, currentObjectState, isManualModuleSetupState } from '@/store/canvasAtom'
|
|
import { rectToPolygon, polygonToTurfPolygon, calculateVisibleModuleHeight, getDegreeByChon, toFixedWithoutRounding } from '@/util/canvas-util'
|
|
import { roofDisplaySelector } from '@/store/settingAtom'
|
|
import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils'
|
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
|
import { moduleSetupSurfaceState } from '@/store/canvasAtom'
|
|
import { useEvent } from '@/hooks/useEvent'
|
|
import { POLYGON_TYPE, BATCH_TYPE, LINE_TYPE } from '@/common/common'
|
|
import * as turf from '@turf/turf'
|
|
import { useSwal } from '@/hooks/useSwal'
|
|
import { compasDegAtom } from '@/store/orientationAtom'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
|
import { useEffect } from 'react'
|
|
import { useMessage } from '@/hooks/useMessage'
|
|
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
|
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
|
import { useMode } from '@/hooks/useMode'
|
|
|
|
export function useModuleBasicSetting(tabNum) {
|
|
const canvas = useRecoilValue(canvasState)
|
|
const { getMessage } = useMessage()
|
|
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
|
const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
|
|
const { addCanvasMouseEventListener, initEvent, removeMouseEvent, addTargetMouseEventListener } = useEvent()
|
|
const { swalFire } = useSwal()
|
|
const compasDeg = useRecoilValue(compasDegAtom)
|
|
const { setSurfaceShapePattern } = useRoofFn()
|
|
const checkedModule = useRecoilValue(checkedModuleState)
|
|
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
|
|
|
|
const canvasSetting = useRecoilValue(canvasSettingState)
|
|
|
|
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
|
|
const [trestleDetailParams, setTrestleDetailParams] = useState([])
|
|
const [trestleDetailList, setTrestleDetailList] = useState([])
|
|
const selectedModules = useRecoilValue(selectedModuleState)
|
|
const { getTrestleDetailList } = useMasterController()
|
|
const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false)
|
|
|
|
const setCurrentObject = useSetRecoilState(currentObjectState)
|
|
const { setModuleStatisticsData } = useCircuitTrestle()
|
|
const { createRoofPolygon, createMarginPolygon, createPaddingPolygon } = useMode()
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
//수동 설치시 초기화
|
|
removeMouseEvent('mouse:up')
|
|
removeMouseEvent('mouse:move')
|
|
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
|
|
}
|
|
}, [])
|
|
|
|
//모듈 선택에서 선택된 값들 넘어옴
|
|
const makeModuleInitArea = () => {
|
|
if (isObjectNotEmpty(moduleSelectionData) && tabNum === 3) {
|
|
if (canvasSetting.roofSizeSet !== '3') {
|
|
const common = moduleSelectionData.common
|
|
const roofConstructions = moduleSelectionData.roofConstructions
|
|
|
|
if (roofConstructions && roofConstructions.length > 0) {
|
|
const listParams = roofConstructions.map((item) => {
|
|
return {
|
|
...common,
|
|
roofMatlCd: item.trestle.roofMatlCd,
|
|
trestleMkrCd: item.trestle.trestleMkrCd,
|
|
constMthdCd: item.trestle.constMthdCd,
|
|
roofBaseCd: item.trestle.roofBaseCd,
|
|
constTp: item.construction.constTp,
|
|
mixMatlNo: selectedModules.mixMatlNo,
|
|
roofPitch: item.addRoof.hajebichi ? item.addRoof.hajebichi : 0,
|
|
inclCd: String(item.addRoof.pitch),
|
|
roofIndex: item.addRoof.index,
|
|
workingWidth: item.addRoof.lenBase,
|
|
raftBaseCd: item.trestle.raftBaseCd,
|
|
}
|
|
})
|
|
setTrestleDetailParams(listParams)
|
|
|
|
//북면 설치 가능 판매점
|
|
if (moduleSelectionData.common.saleStoreNorthFlg === '1') {
|
|
setSaleStoreNorthFlg(true)
|
|
}
|
|
}
|
|
} else {
|
|
//육지붕 일경우에는 바로 배치면 설치LL
|
|
canvas
|
|
.getObjects()
|
|
.filter((roof) => roof.name === 'roof')
|
|
.forEach((roof) => {
|
|
makeModuleInstArea(roof, null)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
//가대 상세 데이터 조회
|
|
const getTrestleDetailListData = async () => {
|
|
const trestleDetailList = await getTrestleDetailList(trestleDetailParams)
|
|
if (trestleDetailList.length > 0) {
|
|
setTrestleDetailList(trestleDetailList)
|
|
}
|
|
}
|
|
|
|
//가대 상세 데이터 파라메터 담기면 실행
|
|
useEffect(() => {
|
|
if (trestleDetailParams.length > 0) {
|
|
getTrestleDetailListData(trestleDetailParams)
|
|
}
|
|
}, [trestleDetailParams])
|
|
|
|
//가대 상세 데이터 들어오면 실행
|
|
useEffect(() => {
|
|
if (trestleDetailList.length > 0) {
|
|
console.log('trestleDetailList', trestleDetailList)
|
|
//지붕을 가져옴
|
|
canvas
|
|
.getObjects()
|
|
.filter((roof) => roof.name === 'roof')
|
|
.forEach((roof) => {
|
|
if (!roof.roofMaterial) return
|
|
const roofIndex = roof.roofMaterial.index //지붕의 지붕재의 순번
|
|
trestleDetailList.forEach((detail) => {
|
|
if (detail.data !== null) {
|
|
if (Number(detail.data.roofIndex) === roofIndex) {
|
|
//roof에 상세 데이터 추가
|
|
roof.set({ trestleDetail: detail.data })
|
|
roof.lines.forEach((line) => {
|
|
line.attributes = { ...line.attributes, offset: getOffset(detail.data, line.attributes.type) }
|
|
})
|
|
//배치면 설치 영역
|
|
makeModuleInstArea(roof, detail.data)
|
|
//surface에 상세 데이터 추가
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}, [trestleDetailList])
|
|
|
|
const getOffset = (data, type) => {
|
|
switch (type) {
|
|
case LINE_TYPE.WALLLINE.EAVES:
|
|
return data.eaveIntvl / 10
|
|
case LINE_TYPE.WALLLINE.GABLE:
|
|
return data.kerabaIntvl / 10
|
|
case LINE_TYPE.SUBLINE.RIDGE:
|
|
case LINE_TYPE.WALLLINE.SHED:
|
|
return data.ridgeIntvl / 10
|
|
default:
|
|
return 200 / 10
|
|
}
|
|
}
|
|
//선택 배치면 배열`
|
|
let selectedModuleInstSurfaceArray = []
|
|
|
|
//가대 상세 데이터 기준으로 모듈 설치 배치면 생성
|
|
const makeModuleInstArea = (roof, trestleDetail) => {
|
|
//지붕 객체 반환
|
|
|
|
if (tabNum == 3) {
|
|
if (!roof) {
|
|
return
|
|
}
|
|
|
|
//도머등 오브젝트 객체가 있으면 아웃라인 낸다
|
|
const batchObjects = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(obj) =>
|
|
obj.name === BATCH_TYPE.OPENING ||
|
|
obj.name === BATCH_TYPE.SHADOW ||
|
|
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
|
obj.name === BATCH_TYPE.PENTAGON_DORMER,
|
|
) //도머s 객체
|
|
|
|
//도머도 외곽을 따야한다
|
|
const batchObjectOptions = {
|
|
stroke: 'red',
|
|
fill: 'transparent',
|
|
strokeDashArray: [10, 4],
|
|
strokeWidth: 1,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
selectable: true,
|
|
name: POLYGON_TYPE.OBJECT_SURFACE,
|
|
originX: 'center',
|
|
originY: 'center',
|
|
}
|
|
|
|
//도머등 오브젝트 객체가 있으면 아웃라인 낸다
|
|
batchObjects.forEach((obj) => {
|
|
//도머일때
|
|
if (obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) {
|
|
const groupPoints = obj.groupPoints
|
|
const offsetObjects = offsetPolygon(groupPoints, 10)
|
|
const dormerOffset = new QPolygon(offsetObjects, batchObjectOptions)
|
|
dormerOffset.setViewLengthText(false)
|
|
canvas.add(dormerOffset) //모듈설치면 만들기
|
|
} else {
|
|
//개구, 그림자일때
|
|
const points = obj.points
|
|
const offsetObjects = offsetPolygon(points, 10)
|
|
const offset = new QPolygon(offsetObjects, batchObjectOptions)
|
|
offset.setViewLengthText(false)
|
|
canvas.add(offset) //모듈설치면 만들기
|
|
}
|
|
})
|
|
|
|
const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id)
|
|
if (isExistSurface) {
|
|
addTargetMouseEventListener('mousedown', isExistSurface, function () {
|
|
toggleSelection(isExistSurface)
|
|
})
|
|
} else {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, true, roof.roofMaterial) //패턴 변경
|
|
// let offsetPoints = createPaddingPolygon(createRoofPolygon(roof.points), roof.lines).vertices //안쪽 offset
|
|
let offsetPoints = null
|
|
console.log(roof, roof.getCurrentPoints())
|
|
const polygon = createRoofPolygon(roof.getCurrentPoints())
|
|
const originPolygon = new QPolygon(roof.getCurrentPoints(), { fontSize: 0 })
|
|
|
|
let result = createPaddingPolygon(polygon, roof.lines).vertices
|
|
|
|
//margin polygon 의 point가 기준 polygon의 밖에 있는지 판단한다.
|
|
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
|
|
|
if (canvasSetting.roofSizeSet == '3') {
|
|
//육지붕일때는 그냥 하드코딩
|
|
offsetPoints = offsetPolygon(roof.points, -30) //육지붕일때
|
|
} else {
|
|
//육지붕이 아닐때
|
|
if (allPointsOutside) {
|
|
offsetPoints = createMarginPolygon(polygon, roof.lines).vertices
|
|
} else {
|
|
offsetPoints = createPaddingPolygon(polygon, roof.lines).vertices
|
|
}
|
|
}
|
|
|
|
//모듈설치영역?? 생성
|
|
|
|
const surfaceId = uuidv4()
|
|
let isNorth = false
|
|
|
|
if (canvasSetting.roofSizeSet != '3') {
|
|
//북면이 있지만
|
|
if (roof.directionText && roof.directionText.indexOf('北') > -1) {
|
|
//북쪽일때 해당 서북서, 동북동은 제외한다고 한다
|
|
if (!(roof.directionText.indexOf('西北西') > -1 || roof.directionText.indexOf('東北東') > -1)) {
|
|
isNorth = true
|
|
}
|
|
}
|
|
}
|
|
//모듈설치면 생성
|
|
let setupSurface = new QPolygon(offsetPoints, {
|
|
stroke: 'red',
|
|
fill: 'rgba(255,255,255,0.1)',
|
|
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,
|
|
direction: roof.direction,
|
|
flipX: roof.flipX,
|
|
flipY: roof.flipY,
|
|
surfaceId: surfaceId,
|
|
originX: 'center',
|
|
originY: 'center',
|
|
modules: [],
|
|
roofMaterial: roof.roofMaterial,
|
|
trestleDetail: trestleDetail,
|
|
isNorth: isNorth,
|
|
perPixelTargetFind: true,
|
|
// angle: -compasDeg,
|
|
})
|
|
|
|
setupSurface.setViewLengthText(false)
|
|
canvas.add(setupSurface) //모듈설치면 만들기
|
|
|
|
//지붕면 선택 금지
|
|
roof.set({
|
|
selectable: false, //선택 금지
|
|
// evented: false, //클릭 이벤트도 금지
|
|
})
|
|
|
|
canvas.renderAll()
|
|
|
|
//바로 들어올때
|
|
const setupModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
|
const eaveBars = canvas.getObjects().filter((obj) => obj.name === 'eaveBar' || obj.name === 'halfEaveBar')
|
|
const racks = canvas.getObjects().filter((obj) => obj.name === 'rack' || obj.name === 'smartRack')
|
|
const brackets = canvas.getObjects().filter((obj) => obj.name === 'brackets')
|
|
setupModules.forEach((obj) => {
|
|
canvas.bringToFront(obj)
|
|
})
|
|
|
|
eaveBars.forEach((obj) => {
|
|
canvas.bringToFront(obj)
|
|
})
|
|
racks.forEach((obj) => {
|
|
canvas.bringToFront(obj)
|
|
})
|
|
brackets.forEach((obj) => {
|
|
canvas.bringToFront(obj)
|
|
})
|
|
|
|
//모듈설치면 클릭이벤트
|
|
addTargetMouseEventListener('mousedown', setupSurface, function () {
|
|
toggleSelection(setupSurface)
|
|
})
|
|
}
|
|
}
|
|
|
|
//설치 범위 지정 클릭 이벤트
|
|
const toggleSelection = (setupSurface) => {
|
|
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
|
|
//최초 선택일때
|
|
if (!isExist) {
|
|
//설치면이 북면이고 북면설치 허용점이 아니면
|
|
if (setupSurface.isNorth && !saleStoreNorthFlg) {
|
|
swalFire({ text: getMessage('module.not.batch.north'), icon: 'warning' })
|
|
return
|
|
}
|
|
|
|
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
|
|
setupSurface.set({ ...setupSurface, strokeWidth: 3, strokeDashArray: [0], fill: 'rgba(255,255,255,0.1)' })
|
|
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
|
//중복으로 들어가는걸 방지하기 위한 코드
|
|
|
|
canvas?.renderAll()
|
|
selectedModuleInstSurfaceArray.push(setupSurface)
|
|
|
|
setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] })
|
|
} else {
|
|
//선택후 재선택하면 선택안됨으로 변경
|
|
setupSurface.set({ ...setupSurface, fill: 'rgba(255,255,255,0.1)', strokeDashArray: [10, 4], strokeWidth: 1 })
|
|
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
|
|
|
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
|
|
const removeIndex = setupSurface.parentId
|
|
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
|
|
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
|
|
setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] })
|
|
}
|
|
|
|
canvas?.renderAll()
|
|
setModuleSetupSurface([...selectedModuleInstSurfaceArray])
|
|
}
|
|
}
|
|
|
|
//모듈,회로에서 다른메뉴 -> 배치면으로 갈 경수 초기화
|
|
const restoreModuleInstArea = () => {
|
|
//설치면 삭제
|
|
const setupArea = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE || obj.name === POLYGON_TYPE.OBJECT_SURFACE)
|
|
|
|
//모듈 삭제 및 초기화
|
|
setupArea.forEach((obj) => {
|
|
if (isObjectNotEmpty(obj.modules) && obj.modules.length > 0) {
|
|
obj.modules.forEach((module) => {
|
|
canvas.remove(module)
|
|
})
|
|
}
|
|
canvas.remove(obj)
|
|
obj.modules = []
|
|
})
|
|
|
|
//지붕패턴 변경
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
|
roofs.forEach((roof) => {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, false, roof.roofMaterial) //패턴 변경
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (canvasSetting.roofSizeSet !== '3') {
|
|
if (isObjectNotEmpty(moduleSelectionData) && moduleSelectionData.common.saleStoreNorthFlg === '1') {
|
|
setSaleStoreNorthFlg(true)
|
|
}
|
|
}
|
|
}, [isManualModuleSetup])
|
|
|
|
/**
|
|
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
|
|
* 확인 후 셀을 이동시킴
|
|
*/
|
|
const manualModuleSetup = (placementRef) => {
|
|
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
|
|
|
|
if (isManualModuleSetup) {
|
|
if (checkedModule.length === 0) {
|
|
swalFire({ text: getMessage('module.place.select.module') })
|
|
setIsManualModuleSetup(!isManualModuleSetup)
|
|
return
|
|
}
|
|
|
|
if (checkedModule.length > 1) {
|
|
swalFire({ text: getMessage('module.place.select.one.module') })
|
|
setIsManualModuleSetup(!isManualModuleSetup)
|
|
return
|
|
}
|
|
|
|
const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체
|
|
//수동모드 모듈 설치면 선택 잠금
|
|
moduleSetupSurfaces.forEach((obj) => {
|
|
obj.set({ selectable: false, evented: false })
|
|
})
|
|
|
|
//모듈 기본 옵션
|
|
const moduleOptions = {
|
|
fill: checkedModule[0].color,
|
|
stroke: 'black',
|
|
strokeWidth: 0.3,
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: true, // X 축 이동 잠금
|
|
lockMovementY: true, // Y 축 이동 잠금
|
|
lockRotation: true, // 회전 잠금
|
|
lockScalingX: true, // X 축 크기 조정 잠금
|
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
|
name: POLYGON_TYPE.MODULE,
|
|
}
|
|
|
|
if (moduleSetupSurfaces.length !== 0) {
|
|
let tempModule
|
|
let manualDrawModules = []
|
|
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]
|
|
manualDrawModules = moduleSetupSurfaces[i].modules // 앞에서 자동으로 했을때 추가됨
|
|
flowDirection = moduleSetupSurfaces[i].direction //도형의 방향
|
|
|
|
const moduleWidth = Number(checkedModule[0].longAxis) / 10
|
|
const moduleHeight = Number(checkedModule[0].shortAxis) / 10
|
|
let tmpWidth = flowDirection === 'south' || flowDirection === 'north' ? moduleWidth : moduleHeight
|
|
let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth
|
|
|
|
let { width, height } =
|
|
canvasSetting.roofSizeSet == '1'
|
|
? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection)
|
|
: { width: tmpWidth, height: tmpHeight }
|
|
|
|
const points = [
|
|
{
|
|
x: toFixedWithoutRounding(mousePoint.x, 2) + toFixedWithoutRounding(width / 2, 2),
|
|
y: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2),
|
|
},
|
|
{
|
|
x: toFixedWithoutRounding(mousePoint.x, 2) + toFixedWithoutRounding(width / 2, 2),
|
|
y: toFixedWithoutRounding(mousePoint.y, 2) + toFixedWithoutRounding(height / 2, 2),
|
|
},
|
|
{
|
|
x: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2),
|
|
y: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2),
|
|
},
|
|
{
|
|
x: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2),
|
|
y: toFixedWithoutRounding(mousePoint.y, 2) + toFixedWithoutRounding(height / 2, 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: 0.3,
|
|
width: toFixedWithoutRounding(width, 2),
|
|
height: toFixedWithoutRounding(height, 2),
|
|
left: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2),
|
|
top: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2),
|
|
selectable: false,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
name: 'tempModule',
|
|
parentId: moduleSetupSurfaces[i].parentId,
|
|
})
|
|
|
|
//북면이고 북면설치상점이 아니면 그냥 return
|
|
if (trestlePolygon.isNorth && !saleStoreNorthFlg) {
|
|
return
|
|
} else {
|
|
canvas?.add(tempModule) //움직여가면서 추가됨
|
|
}
|
|
|
|
/**
|
|
* 스냅기능
|
|
*/
|
|
let snapDistance = flowDirection === 'south' || flowDirection === 'north' ? 70 : 40
|
|
let sideSnapDistance = 15
|
|
let trestleSnapDistance = 15
|
|
|
|
let intvHor =
|
|
flowDirection === 'south' || flowDirection === 'north'
|
|
? moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10
|
|
: moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10
|
|
let intvVer =
|
|
flowDirection === 'south' || flowDirection === 'north'
|
|
? moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10
|
|
: moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10
|
|
|
|
const trestleLeft = toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
|
|
const trestleTop = toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
|
|
const trestleRight =
|
|
toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
|
|
const trestleBottom =
|
|
toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
|
|
const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
|
|
|
|
// 이동하는 모듈의 경계 좌표
|
|
const smallLeft = toFixedWithoutRounding(tempModule.left, 2)
|
|
const smallTop = toFixedWithoutRounding(tempModule.top, 2)
|
|
const smallRight = smallLeft + toFixedWithoutRounding(tempModule.width, 2)
|
|
const smallBottom = smallTop + toFixedWithoutRounding(tempModule.height, 2)
|
|
const smallCenterX = smallLeft + toFixedWithoutRounding(tempModule.width / 2, 2)
|
|
const smallCenterY = smallTop + toFixedWithoutRounding(tempModule.height / 2, 2)
|
|
|
|
/**
|
|
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
|
|
*/
|
|
if (manualDrawModules) {
|
|
manualDrawModules.forEach((cell) => {
|
|
const holdCellLeft = toFixedWithoutRounding(cell.left, 2)
|
|
const holdCellTop = toFixedWithoutRounding(cell.top, 2)
|
|
const holdCellRight = holdCellLeft + toFixedWithoutRounding(cell.width, 2)
|
|
const holdCellBottom = holdCellTop + toFixedWithoutRounding(cell.height, 2)
|
|
const holdCellCenterX = holdCellLeft + toFixedWithoutRounding(cell.width / 2, 2)
|
|
const holdCellCenterY = holdCellTop + toFixedWithoutRounding(cell.height / 2, 2)
|
|
|
|
//흐름방향따라 달라야 한다.
|
|
if (flowDirection === 'south' || flowDirection === 'north') {
|
|
if (Math.abs(smallCenterX - holdCellCenterX) < snapDistance) {
|
|
//움직이는 모듈 가운데 -> 설치 모듈 가운데
|
|
tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
|
|
}
|
|
|
|
//움직이는 모듈왼쪽 -> 가운데
|
|
if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) {
|
|
tempModule.left = holdCellCenterX + intvHor / 2
|
|
}
|
|
// 오른쪽 -> 가운데
|
|
if (Math.abs(smallRight - holdCellCenterX) < snapDistance) {
|
|
tempModule.left = holdCellCenterX - width - intvHor / 2
|
|
}
|
|
|
|
//설치된 셀에 좌측에 스냅
|
|
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
|
|
// console.log('모듈 좌측 스냅')
|
|
tempModule.left = holdCellLeft - width - intvHor
|
|
}
|
|
|
|
//설치된 셀에 우측에 스냅
|
|
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
|
// console.log('모듈 우측 스냅')
|
|
tempModule.left = holdCellRight + intvHor
|
|
}
|
|
//설치된 셀에 위쪽에 스냅
|
|
if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
|
|
tempModule.top = holdCellTop - height - intvVer
|
|
}
|
|
|
|
//설치된 셀에 밑쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
|
|
tempModule.top = holdCellBottom + intvVer
|
|
}
|
|
|
|
//설치된 셀에 윗쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
|
|
tempModule.top = holdCellTop
|
|
}
|
|
} else {
|
|
//흐름방향 west, east
|
|
|
|
//가운데 가운데
|
|
if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
|
|
tempModule.top = holdCellCenterY - toFixedWithoutRounding(width / 2, 2)
|
|
}
|
|
|
|
//위쪽 -> 가운데
|
|
if (Math.abs(smallTop - holdCellCenterY) < snapDistance) {
|
|
// console.log('holdCellCenterX', holdCellCenterX)
|
|
// console.log('smallLeft', smallLeft)
|
|
|
|
// console.log('모듈 센터에 스냅')
|
|
tempModule.top = holdCellCenterY + intvHor / 2
|
|
|
|
// console.log('tempModule.left', tempModule.left)
|
|
}
|
|
// 밑 -> 가운데
|
|
if (Math.abs(smallBottom - holdCellCenterY) < snapDistance) {
|
|
tempModule.top = holdCellCenterY - height - intvHor / 2
|
|
}
|
|
|
|
//설치된 셀에 좌측에 스냅
|
|
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
|
|
// console.log('모듈 좌측 스냅')
|
|
tempModule.left = holdCellLeft - width - intvHor
|
|
}
|
|
|
|
//설치된 셀에 우측에 스냅
|
|
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
|
// console.log('모듈 우측 스냅')
|
|
tempModule.left = holdCellRight + intvHor
|
|
}
|
|
//설치된 셀에 위쪽에 스냅
|
|
if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
|
|
tempModule.top = holdCellTop - height - intvVer
|
|
}
|
|
|
|
//설치된 셀에 밑쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
|
|
tempModule.top = holdCellBottom + intvVer
|
|
}
|
|
|
|
//설치된 셀에 윗쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
|
|
tempModule.top = holdCellTop
|
|
}
|
|
|
|
if (Math.abs(smallLeft - holdCellLeft) < snapDistance) {
|
|
tempModule.left = holdCellLeft
|
|
}
|
|
}
|
|
|
|
//가운데 -> 가운대
|
|
|
|
//세로 가운데 -> 가운데
|
|
// if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
|
|
// tempModule.top = holdCellCenterY - toFixedWithoutRounding(height / 2, 1)
|
|
// }
|
|
// //위쪽 -> 가운데
|
|
// if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
|
|
// tempModule.top = holdCellCenterY
|
|
})
|
|
}
|
|
|
|
// 위쪽 변에 스냅
|
|
if (Math.abs(smallTop - trestleTop) < trestleSnapDistance) {
|
|
tempModule.top = trestleTop + 1
|
|
}
|
|
|
|
// 아래쪽 변에 스냅
|
|
if (Math.abs(smallBottom - trestleBottom) < trestleSnapDistance) {
|
|
tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height - 1
|
|
}
|
|
|
|
// 왼쪽변에 스냅
|
|
if (Math.abs(smallLeft - trestleLeft) < trestleSnapDistance) {
|
|
tempModule.left = trestleLeft + 1
|
|
}
|
|
//오른쪽 변에 스냅
|
|
if (Math.abs(smallRight - trestleRight) < trestleSnapDistance) {
|
|
tempModule.left = trestleRight - tempModule.width - 1
|
|
}
|
|
|
|
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)) < trestleSnapDistance) {
|
|
// tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width / 2
|
|
// }
|
|
// 모듈오른쪽이 세로중앙선에 붙게 스냅
|
|
// if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < trestleSnapDistance) {
|
|
// tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
|
|
// }
|
|
} else {
|
|
// 모듈이 가로중앙선에 스냅
|
|
// if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < trestleSnapDistance) {
|
|
// tempModule.top = bigCenterY - tempModule.height / 2
|
|
// }
|
|
// if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < trestleSnapDistance) {
|
|
// tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
|
|
// }
|
|
// 모듈 밑면이 가로중앙선에 스냅
|
|
// if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < trestleSnapDistance) {
|
|
// tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY
|
|
// }
|
|
}
|
|
|
|
tempModule.setCoords()
|
|
canvas?.renderAll()
|
|
inside = true
|
|
break
|
|
} else {
|
|
inside = false
|
|
}
|
|
}
|
|
|
|
if (!inside) {
|
|
// tempModule.set({ fill: 'red' })
|
|
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: toFixedWithoutRounding(tempModule.left, 2), y: toFixedWithoutRounding(tempModule.top, 2) },
|
|
{
|
|
x: toFixedWithoutRounding(tempModule.left, 2) + toFixedWithoutRounding(tempModule.width, 2),
|
|
y: toFixedWithoutRounding(tempModule.top, 2),
|
|
},
|
|
{
|
|
x: toFixedWithoutRounding(tempModule.left, 2) + toFixedWithoutRounding(tempModule.width, 2),
|
|
y: toFixedWithoutRounding(tempModule.top, 2) + toFixedWithoutRounding(tempModule.height, 2),
|
|
},
|
|
{
|
|
x: toFixedWithoutRounding(tempModule.left, 2),
|
|
y: toFixedWithoutRounding(tempModule.top, 2) + toFixedWithoutRounding(tempModule.height, 2),
|
|
},
|
|
]
|
|
|
|
tempModule.set({ points: rectPoints })
|
|
const tempTurfModule = polygonToTurfPolygon(tempModule)
|
|
|
|
//도머 객체를 가져옴
|
|
if (batchObjects) {
|
|
batchObjects.forEach((object) => {
|
|
let dormerTurfPolygon = polygonToTurfPolygon(object, true)
|
|
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
|
|
//겹치면 안됨
|
|
if (intersection) {
|
|
swalFire({ text: getMessage('module.place.overobject') })
|
|
isIntersection = false
|
|
}
|
|
})
|
|
}
|
|
|
|
if (!isIntersection) return
|
|
|
|
tempModule.setCoords() //좌표 재정렬
|
|
if (turf.booleanContains(turfPolygon, tempTurfModule) || turf.booleanWithin(tempTurfModule, turfPolygon)) {
|
|
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
|
|
const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module, true))) //겹치는지 확인
|
|
if (!isOverlap) {
|
|
canvas?.remove(tempModule)
|
|
|
|
//안겹치면 넣는다
|
|
// tempModule.setCoords()
|
|
moduleOptions.surfaceId = trestlePolygon.id
|
|
|
|
// console.log('tempModule.points', tempModule.points)
|
|
|
|
let manualModule = new QPolygon(tempModule.points, {
|
|
...moduleOptions,
|
|
moduleInfo: checkedModule[0],
|
|
left: toFixedWithoutRounding(tempModule.left, 2),
|
|
top: toFixedWithoutRounding(tempModule.top, 2),
|
|
width: toFixedWithoutRounding(tempModule.width, 2),
|
|
height: toFixedWithoutRounding(tempModule.height, 2),
|
|
toFixed: 2,
|
|
})
|
|
|
|
canvas?.add(manualModule)
|
|
manualDrawModules.push(manualModule)
|
|
setModuleStatisticsData()
|
|
// getModuleStatistics()
|
|
} else {
|
|
swalFire({ text: getMessage('module.place.overlab') })
|
|
}
|
|
} else {
|
|
swalFire({ text: getMessage('module.place.out') })
|
|
}
|
|
}
|
|
})
|
|
}
|
|
} else {
|
|
if (moduleSetupSurfaces) {
|
|
//수동모드 해제시 모듈 설치면 선택 잠금
|
|
moduleSetupSurfaces.forEach((obj) => {
|
|
obj.set({ selectable: true, evented: true })
|
|
})
|
|
}
|
|
|
|
removeMouseEvent('mouse:up')
|
|
removeMouseEvent('mouse:move')
|
|
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
|
|
}
|
|
}
|
|
|
|
//자동 모듈 설치(그리드 방식)
|
|
const autoModuleSetup = (placementRef) => {
|
|
initEvent() //마우스 이벤트 초기화
|
|
|
|
if (checkedModule.length === 0) {
|
|
swalFire({ text: getMessage('module.place.select.module') })
|
|
return
|
|
}
|
|
|
|
const isChidori = placementRef.isChidori.current === 'true' ? true : false
|
|
const setupLocation = placementRef.setupLocation.current
|
|
const isMaxSetup = placementRef.isMaxSetup.current === 'true' ? true : false
|
|
|
|
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 === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체
|
|
|
|
if (moduleSetupSurfaces.length === 0) {
|
|
swalFire({ text: getMessage('module.place.no.surface') })
|
|
return
|
|
}
|
|
|
|
//어짜피 자동으로 누르면 선택안된데도 다 날아간다
|
|
canvas.getObjects().forEach((obj) => {
|
|
if (obj.name === 'module') {
|
|
canvas.remove(obj)
|
|
}
|
|
})
|
|
|
|
// if (moduleIsSetup.length > 0) {
|
|
// swalFire({ text: 'alert 아이콘 테스트입니다.', icon: 'error' })
|
|
// }
|
|
|
|
// moduleSetupSurfaces.forEach((obj) => {
|
|
// if (obj.modules) {
|
|
// obj.modules.forEach((module) => {
|
|
// canvas?.remove(module)
|
|
// })
|
|
// obj.modules = []
|
|
// }
|
|
// })
|
|
|
|
notSelectedTrestlePolygons.forEach((obj) => {
|
|
if (obj.modules) {
|
|
obj.modules.forEach((module) => {
|
|
canvas?.remove(module)
|
|
})
|
|
obj.modules = []
|
|
}
|
|
})
|
|
|
|
let moduleOptions = {
|
|
stroke: 'black',
|
|
strokeWidth: 0.3,
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: true, // X 축 이동 잠금
|
|
lockMovementY: true, // Y 축 이동 잠금
|
|
lockRotation: true, // 회전 잠금
|
|
lockScalingX: true, // X 축 크기 조정 잠금
|
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
|
name: 'module',
|
|
}
|
|
|
|
let leftMargin, bottomMargin, square, chidoriLength
|
|
|
|
//선택된 지붕안에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 포함되면 배열 반환
|
|
const objectsIncludeSurface = (turfModuleSetupSurface) => {
|
|
let containsBatchObjects = []
|
|
containsBatchObjects = batchObjects.filter((batchObject) => {
|
|
let convertBatchObject = polygonToTurfPolygon(batchObject)
|
|
// 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
|
|
return (
|
|
turf.booleanContains(turfModuleSetupSurface, convertBatchObject) ||
|
|
turf.booleanWithin(convertBatchObject, turfModuleSetupSurface) ||
|
|
turf.booleanOverlap(turfModuleSetupSurface, convertBatchObject)
|
|
)
|
|
})
|
|
|
|
return containsBatchObjects
|
|
}
|
|
|
|
// /**
|
|
// * 도머나 개구가 모듈에 걸치는지 확인하는 로직
|
|
// * @param {*} squarePolygon
|
|
// * @param {*} containsBatchObjects
|
|
// * @returns
|
|
// */
|
|
// const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => {
|
|
// let isDisjoint = false
|
|
//
|
|
// if (containsBatchObjects.length > 0) {
|
|
// let convertBatchObject
|
|
// //도머가 있으면 적용되는 로직
|
|
// isDisjoint = containsBatchObjects.every((batchObject) => {
|
|
// if (batchObject.type === 'group') {
|
|
// convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
|
|
// } else {
|
|
// convertBatchObject = polygonToTurfPolygon(batchObject)
|
|
// }
|
|
// /**
|
|
// * 도머가 여러개일수있으므로 겹치는게 있다면...
|
|
// * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환
|
|
// */
|
|
// return turf.booleanDisjoint(squarePolygon, convertBatchObject)
|
|
// })
|
|
// } else {
|
|
// isDisjoint = true
|
|
// }
|
|
// return isDisjoint
|
|
// }
|
|
|
|
/**
|
|
* 배치면 안에 있는지 확인
|
|
* @param {*} squarePolygon
|
|
* @param {*} turfModuleSetupSurface
|
|
* @returns
|
|
*/
|
|
const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => {
|
|
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
|
|
}
|
|
|
|
//흐름 방향이 남쪽(아래)
|
|
const downFlowSetupModule = (
|
|
surfaceMaxLines,
|
|
maxLengthLine,
|
|
moduleSetupArray,
|
|
moduleSetupSurface,
|
|
containsBatchObjects,
|
|
isCenter = false,
|
|
intvHor,
|
|
intvVer,
|
|
) => {
|
|
let setupModule = []
|
|
|
|
const trestleDetailData = moduleSetupSurface.trestleDetail
|
|
const moduleMaxCols = trestleDetailData.moduleMaxCols // 모듈 최대 가로 설치 갯수
|
|
|
|
let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
|
|
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
|
|
let isChidoriLine = false
|
|
let flowLines
|
|
|
|
checkedModule.forEach((module, moduleIndex) => {
|
|
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
|
|
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
|
|
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
|
|
|
|
//모듈의 넓이 높이를 가져옴 (복시도 촌수 적용)
|
|
//1번 깔았던 모듈 기준으로 잡야아함
|
|
let { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module)
|
|
|
|
if (moduleIndex === 0) {
|
|
flowLines = getFlowLines(moduleSetupSurface, height)
|
|
if (flowLines.bottom.type === 'curve') {
|
|
flowLines = getFlowLines(moduleSetupSurface, width)
|
|
}
|
|
}
|
|
|
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
|
const moduleArray = []
|
|
|
|
let calcAreaWidth = Math.abs(flowLines.right.x1 - flowLines.left.x1) //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
|
|
let calcModuleWidthCount = calcAreaWidth / (width + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
|
|
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
|
|
let calcModuleHeightCount = calcAreaHeight / (height + intvVer + 1)
|
|
|
|
let calcStartPoint = flowLines.right.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * width) / 2 : 0 //반씩 나눠서 중앙에 맞춤 bottom 기준으로 양변이 직선일때만 가운데 정렬
|
|
let startPointX = flowLines.left.x1 + calcStartPoint //시작점을 만든다
|
|
|
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
|
if (flowLines.left.type === 'curve' && flowLines.right.type === 'curve') {
|
|
startPointX = flowLines.left.x1 + (calcAreaWidth - totalModuleWidthCount * width) / 2
|
|
}
|
|
|
|
let heightMargin = 0
|
|
let widthMargin = 0
|
|
let chidoriLength = 0
|
|
|
|
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
|
if (moduleIndex > 0) {
|
|
// moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
|
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
|
}
|
|
|
|
for (let i = 0; i < calcModuleHeightCount; i++) {
|
|
let isInstall = false
|
|
let moduleY = flowLines.bottom.y1 - height * i - 1 //살짝 여유를 준다
|
|
|
|
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
|
|
if (moduleIndex > 0) {
|
|
moduleY = installedLastHeightCoord - intvVer
|
|
}
|
|
|
|
//첫번째는 붙여서 두번째는 마진을 주고 설치
|
|
heightMargin = i === 0 ? 0 : intvVer * i
|
|
|
|
for (let j = 0; j < totalModuleWidthCount; j++) {
|
|
let moduleX = startPointX + width * j + 1 //5정도 마진을 준다
|
|
widthMargin = j === 0 ? 0 : intvHor * j // 가로 마진값
|
|
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
|
if (isChidori) {
|
|
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : width / 2 - intvHor
|
|
}
|
|
|
|
//치도리 일때 는 짝수(1 기준) 일때만 치도리 라인으로 본다
|
|
if (isChidori && isChidoriLine) {
|
|
chidoriLength = width / 2 - intvHor
|
|
}
|
|
|
|
let square = [
|
|
[moduleX + widthMargin + chidoriLength, moduleY - height - heightMargin],
|
|
[moduleX + widthMargin + chidoriLength, moduleY - heightMargin],
|
|
[moduleX + width + widthMargin + chidoriLength, moduleY - heightMargin],
|
|
[moduleX + width + widthMargin + chidoriLength, moduleY - height - heightMargin],
|
|
[moduleX + widthMargin + chidoriLength, moduleY - height - heightMargin],
|
|
]
|
|
|
|
// console.log('square', square)
|
|
|
|
let squarePolygon = turf.polygon([square])
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
|
|
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
|
|
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
|
|
|
|
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
|
|
let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects)
|
|
|
|
if (disjointFromTrestle && isDisjoint) {
|
|
//최초 한번은 그냥 그린다
|
|
//겹치는지 확인해서 포함된 모듈만 그린다
|
|
canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
moduleArray.push(tempModule)
|
|
canvas.renderAll()
|
|
|
|
// ++installedModuleHeightCount
|
|
|
|
isInstall = true
|
|
//마지막에 설치된 모듈의 Y 좌표
|
|
installedLastHeightCoord = moduleY - height - heightMargin
|
|
} else {
|
|
//디버깅용
|
|
// tempModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
|
|
// canvas?.add(tempModule)
|
|
// canvas.renderAll()
|
|
}
|
|
}
|
|
if (isInstall) {
|
|
++installedModuleHeightCount
|
|
}
|
|
}
|
|
|
|
setupModule.push(moduleArray)
|
|
})
|
|
}
|
|
|
|
const topFlowSetupModule = (
|
|
surfaceMaxLines,
|
|
maxLengthLine,
|
|
moduleSetupArray,
|
|
moduleSetupSurface,
|
|
containsBatchObjects,
|
|
isCenter = false,
|
|
intvHor,
|
|
intvVer,
|
|
) => {
|
|
let setupModule = []
|
|
|
|
const trestleDetailData = moduleSetupSurface.trestleDetail
|
|
const moduleMaxCols = trestleDetailData.moduleMaxCols // 모듈 최대 가로 설치 갯수
|
|
|
|
let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
|
|
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
|
|
let isChidoriLine = false
|
|
let flowLines
|
|
|
|
checkedModule.forEach((module, moduleIndex) => {
|
|
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
|
|
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
|
|
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
|
|
// 혼합모듈 포함 총 모듈 설치 높이 갯수
|
|
const totalModuleMaxRows = tmpModuleData.moduleMaxRows
|
|
|
|
const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module)
|
|
|
|
if (moduleIndex === 0) {
|
|
flowLines = getFlowLines(moduleSetupSurface, height)
|
|
if (flowLines.top.type === 'curve') {
|
|
flowLines = getFlowLines(moduleSetupSurface, width)
|
|
}
|
|
}
|
|
|
|
//흐름 방향이 북쪽(위)
|
|
|
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
|
const moduleArray = []
|
|
|
|
let calcAreaWidth = flowLines.right.x1 - flowLines.left.x1 //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
|
|
let calcModuleWidthCount = calcAreaWidth / (width + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다 //??어쩔때는 붙고 어쩔때는 안붙고 멋대로???
|
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
|
|
let calcModuleHeightCount = calcAreaHeight / (height + intvVer + 1)
|
|
|
|
let calcStartPoint = flowLines.left.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * width) / 2 : 0 //반씩 나눠서 중앙에 맞춤 bottom 기준으로 양변이 직선일때만 가운데 정렬
|
|
let startPointX = flowLines.right.x1 - calcStartPoint //시작점을 만든다
|
|
|
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
|
if (flowLines.left.type === 'curve' && flowLines.right.type === 'curve') {
|
|
startPointX = flowLines.right.x1 - (calcAreaWidth - totalModuleWidthCount * width) / 2
|
|
}
|
|
|
|
let heightMargin = 0
|
|
let widthMargin = 0
|
|
let chidoriLength = 0
|
|
|
|
if (moduleIndex > 0) {
|
|
moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
|
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
|
}
|
|
|
|
for (let i = 0; i < calcModuleHeightCount; i++) {
|
|
let isInstall = false
|
|
let moduleY = flowLines.top.y1 + height * i //탑의 y점에서부터 아래로 그려 내려간다
|
|
|
|
if (moduleIndex > 0) {
|
|
moduleY = installedLastHeightCoord + intvVer + 1
|
|
}
|
|
|
|
heightMargin = i === 0 ? 0 : intvVer * i //모듈간에 마진이 있어 마진값도 넣음
|
|
for (let j = 0; j < totalModuleWidthCount; j++) {
|
|
//모듈 열수 만큼 반복
|
|
let moduleX = startPointX - width * j - 1 //시작점에서 우 -> 좌로 그려 내려간다
|
|
widthMargin = j === 0 ? 0 : intvHor * j
|
|
chidoriLength = 0
|
|
if (isChidori && !isMaxSetup) {
|
|
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : width / 2 - intvHor
|
|
}
|
|
|
|
if (isChidori && isChidoriLine) {
|
|
chidoriLength = width / 2 + intvHor
|
|
}
|
|
|
|
let square = [
|
|
[moduleX - widthMargin - chidoriLength, moduleY + heightMargin],
|
|
[moduleX - widthMargin - chidoriLength, moduleY + height + heightMargin],
|
|
[moduleX - width - widthMargin - chidoriLength, moduleY + height + heightMargin],
|
|
[moduleX - width - widthMargin - chidoriLength, moduleY + heightMargin],
|
|
[moduleX - widthMargin - chidoriLength, moduleY + heightMargin],
|
|
]
|
|
|
|
let squarePolygon = turf.polygon([square])
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
|
|
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
|
|
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
|
|
|
|
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
|
|
let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects)
|
|
|
|
if (disjointFromTrestle && isDisjoint) {
|
|
canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
moduleArray.push(tempModule)
|
|
canvas.renderAll()
|
|
|
|
isInstall = true
|
|
|
|
//마지막에 설치된 모듈의 Y 좌표
|
|
installedLastHeightCoord = moduleY + height + heightMargin
|
|
} else {
|
|
//디버깅용
|
|
// tempModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
|
|
// canvas?.add(tempModule)
|
|
// canvas.renderAll()
|
|
}
|
|
}
|
|
if (isInstall) {
|
|
++installedModuleHeightCount
|
|
}
|
|
}
|
|
|
|
setupModule.push(moduleArray)
|
|
})
|
|
}
|
|
|
|
//남, 북과 같은 로직으로 적용하려면 좌우는 열 -> 행 으로 그려야함
|
|
//변수명은 bottom 기준으로 작성하여 동일한 방향으로 진행한다
|
|
const leftFlowSetupModule = (
|
|
surfaceMaxLines,
|
|
maxLengthLine,
|
|
moduleSetupArray,
|
|
moduleSetupSurface,
|
|
containsBatchObjects,
|
|
isCenter = false,
|
|
intvHor,
|
|
intvVer,
|
|
) => {
|
|
let setupModule = []
|
|
|
|
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
|
const moduleMaxCols = trestleDetailData.moduleMaxCols // 모듈 최대 가로 설치 갯수
|
|
|
|
let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
|
|
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
|
|
let isChidoriLine = false
|
|
let flowLines
|
|
|
|
checkedModule.forEach((module, moduleIndex) => {
|
|
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
|
|
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
|
|
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
|
|
|
|
// 혼합모듈 포함 총 모듈 설치 높이 갯수
|
|
const totalModuleMaxRows = tmpModuleData.moduleMaxRows
|
|
|
|
//모듈의 넓이 높이를 가져옴 (복시도 촌수 적용)
|
|
//1번 깔았던 모듈 기준으로 잡야아함
|
|
const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module)
|
|
|
|
if (moduleIndex === 0) {
|
|
flowLines = getFlowLines(moduleSetupSurface, width)
|
|
if (flowLines.left.type === 'curve') {
|
|
flowLines = getFlowLines(moduleSetupSurface, height)
|
|
}
|
|
}
|
|
|
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
|
|
|
const moduleArray = []
|
|
|
|
let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
|
|
let calcModuleWidthCount = calcAreaWidth / (height + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
|
|
let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
|
|
|
|
let calcStartPoint = flowLines.bottom.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
|
|
let startPointX = flowLines.top.y1 + calcStartPoint //시작점을 만든다
|
|
|
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
|
if (flowLines.top.type === 'curve' && flowLines.bottom.type === 'curve') {
|
|
startPointX = flowLines.top.y1 + (calcAreaWidth - totalModuleWidthCount * height) / 2
|
|
}
|
|
|
|
let heightMargin = 0
|
|
let widthMargin = 0
|
|
let chidoriLength = 0
|
|
|
|
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
|
if (moduleIndex > 0) {
|
|
moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
|
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
|
}
|
|
|
|
for (let i = 0; i < calcModuleHeightCount; i++) {
|
|
let isInstall = false
|
|
let moduleY = flowLines.left.x1 + width * i + 1 //살짝 여유를 준다
|
|
|
|
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
|
|
if (moduleIndex > 0) {
|
|
moduleY = installedLastHeightCoord + intvHor
|
|
}
|
|
|
|
//첫번째는 붙여서 두번째는 마진을 주고 설치
|
|
heightMargin = i === 0 ? 0 : intvHor * i
|
|
|
|
for (let j = 0; j < totalModuleWidthCount; j++) {
|
|
let moduleX = startPointX + height * j + 1 //5정도 마진을 준다
|
|
widthMargin = j === 0 ? 0 : intvVer * j // 가로 마진값
|
|
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
|
if (isChidori && !isMaxSetup) {
|
|
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : height / 2 - intvVer
|
|
}
|
|
|
|
let square = [
|
|
[moduleY + heightMargin, moduleX + height + widthMargin + chidoriLength],
|
|
[moduleY + heightMargin, moduleX + widthMargin + chidoriLength],
|
|
[moduleY + width + heightMargin, moduleX + widthMargin + chidoriLength],
|
|
[moduleY + width + heightMargin, moduleX + height + widthMargin + chidoriLength],
|
|
[moduleY + heightMargin, moduleX + height + widthMargin + chidoriLength],
|
|
]
|
|
|
|
let squarePolygon = turf.polygon([square])
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
|
|
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
|
|
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
|
|
|
|
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
|
|
let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects)
|
|
|
|
if (disjointFromTrestle && isDisjoint) {
|
|
//최초 한번은 그냥 그린다
|
|
//겹치는지 확인해서 포함된 모듈만 그린다
|
|
canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
moduleArray.push(tempModule)
|
|
canvas.renderAll()
|
|
|
|
// ++installedModuleHeightCount
|
|
|
|
isInstall = true
|
|
//마지막에 설치된 모듈의 Y 좌표
|
|
installedLastHeightCoord = moduleY + width + widthMargin
|
|
} else {
|
|
//디버깅용
|
|
// tempModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
|
|
// canvas?.add(tempModule)
|
|
// canvas.renderAll()
|
|
}
|
|
}
|
|
|
|
if (isInstall) {
|
|
++installedModuleHeightCount
|
|
}
|
|
}
|
|
setupModule.push(moduleArray)
|
|
})
|
|
}
|
|
|
|
const rightFlowSetupModule = (
|
|
surfaceMaxLines,
|
|
maxLengthLine,
|
|
moduleSetupArray,
|
|
moduleSetupSurface,
|
|
containsBatchObjects,
|
|
isCenter = false,
|
|
intvHor,
|
|
intvVer,
|
|
) => {
|
|
let setupModule = []
|
|
|
|
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
|
const moduleMaxCols = trestleDetailData.moduleMaxCols // 모듈 최대 가로 설치 갯수
|
|
|
|
let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
|
|
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
|
|
let isChidoriLine = false
|
|
let flowLines
|
|
|
|
checkedModule.forEach((module, moduleIndex) => {
|
|
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
|
|
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
|
|
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
|
|
|
|
// 혼합모듈 포함 총 모듈 설치 높이 갯수
|
|
const totalModuleMaxRows = tmpModuleData.moduleMaxRows
|
|
|
|
//모듈의 넓이 높이를 가져옴 (복시도 촌수 적용)
|
|
//1번 깔았던 모듈 기준으로 잡야아함
|
|
const { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module)
|
|
|
|
if (moduleIndex === 0) {
|
|
flowLines = getFlowLines(moduleSetupSurface, width)
|
|
if (flowLines.right.type === 'curve') {
|
|
flowLines = getFlowLines(moduleSetupSurface, height)
|
|
}
|
|
}
|
|
|
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
|
const moduleArray = []
|
|
|
|
let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
|
|
let calcModuleWidthCount = calcAreaWidth / (height + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
|
|
let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
|
|
|
|
let calcStartPoint = flowLines.top.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
|
|
let startPointX = flowLines.bottom.y2 - calcStartPoint //시작점을 만든다
|
|
|
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
|
if (flowLines.top.type === 'curve' && flowLines.bottom.type === 'curve') {
|
|
startPointX = flowLines.bottom.y2 - (calcAreaWidth - totalModuleWidthCount * height) / 2
|
|
}
|
|
|
|
let heightMargin = 0
|
|
let widthMargin = 0
|
|
let chidoriLength = 0
|
|
|
|
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
|
if (moduleIndex > 0) {
|
|
moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
|
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
|
}
|
|
|
|
for (let i = 0; i < calcModuleHeightCount; i++) {
|
|
let isInstall = false
|
|
let moduleY = flowLines.right.x1 - width * i - 1 //살짝 여유를 준다
|
|
|
|
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
|
|
if (moduleIndex > 0) {
|
|
moduleY = installedLastHeightCoord - intvHor
|
|
}
|
|
|
|
//첫번째는 붙여서 두번째는 마진을 주고 설치
|
|
heightMargin = i === 0 ? 0 : intvHor * i
|
|
|
|
for (let j = 0; j < totalModuleWidthCount; j++) {
|
|
let moduleX = startPointX - height * j - 1 //5정도 마진을 준다
|
|
widthMargin = j === 0 ? 0 : intvVer * j // 가로 마진값
|
|
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
|
if (isChidori && !isMaxSetup) {
|
|
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : height / 2 - intvVer
|
|
}
|
|
|
|
//치도리 일때 는 짝수(1 기준) 일때만 치도리 라인으로 본다
|
|
// if (isChidori && isChidoriLine) {
|
|
// chidoriLength = width / 2 - height
|
|
// }
|
|
|
|
let square = [
|
|
[moduleY - heightMargin, moduleX - height - widthMargin - chidoriLength],
|
|
[moduleY - heightMargin, moduleX - widthMargin - chidoriLength],
|
|
[moduleY - width - heightMargin, moduleX - widthMargin - chidoriLength],
|
|
[moduleY - width - heightMargin, moduleX - height - widthMargin - chidoriLength],
|
|
[moduleY - heightMargin, moduleX - height - widthMargin - chidoriLength],
|
|
]
|
|
|
|
let squarePolygon = turf.polygon([square])
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
|
|
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
|
|
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
|
|
|
|
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
|
|
let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects)
|
|
|
|
if (disjointFromTrestle && isDisjoint) {
|
|
//최초 한번은 그냥 그린다
|
|
//겹치는지 확인해서 포함된 모듈만 그린다
|
|
canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
moduleArray.push(tempModule)
|
|
canvas.renderAll()
|
|
|
|
isInstall = true
|
|
//마지막에 설치된 모듈의 Y 좌표
|
|
installedLastHeightCoord = moduleY - width - heightMargin
|
|
} else {
|
|
//디버깅용
|
|
// tempModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
|
|
// canvas?.add(tempModule)
|
|
// canvas.renderAll()
|
|
}
|
|
}
|
|
|
|
if (isInstall) {
|
|
++installedModuleHeightCount
|
|
}
|
|
}
|
|
|
|
setupModule.push(moduleArray)
|
|
})
|
|
}
|
|
|
|
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
|
moduleSetupSurface.fire('mousedown')
|
|
const moduleSetupArray = []
|
|
|
|
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
|
|
const containsBatchObjects = objectsIncludeSurface(turfModuleSetupSurface) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직
|
|
const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
|
|
|
|
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
|
|
return acc.length > cur.length ? acc : cur
|
|
})
|
|
|
|
const flowDirection = moduleSetupSurface.direction
|
|
|
|
let intvHor =
|
|
flowDirection === 'south' || flowDirection === 'north'
|
|
? moduleSetupSurface.trestleDetail.moduleIntvlHor / 10
|
|
: moduleSetupSurface.trestleDetail.moduleIntvlVer / 10
|
|
let intvVer =
|
|
flowDirection === 'south' || flowDirection === 'north'
|
|
? moduleSetupSurface.trestleDetail.moduleIntvlVer / 10
|
|
: moduleSetupSurface.trestleDetail.moduleIntvlHor / 10
|
|
|
|
//처마면 배치
|
|
if (setupLocation === 'eaves') {
|
|
// 흐름방향이 남쪽일때
|
|
if (moduleSetupSurface.direction === 'south') {
|
|
downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'west') {
|
|
leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'east') {
|
|
rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'north') {
|
|
topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
} else if (setupLocation === 'ridge') {
|
|
//용마루
|
|
if (moduleSetupSurface.direction === 'south') {
|
|
topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'west') {
|
|
rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'east') {
|
|
leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'north') {
|
|
downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
|
|
}
|
|
} else if (setupLocation === 'center') {
|
|
//중가면
|
|
if (moduleSetupSurface.direction === 'south') {
|
|
downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'west') {
|
|
leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'east') {
|
|
rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
|
|
}
|
|
if (moduleSetupSurface.direction === 'north') {
|
|
topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
|
|
}
|
|
}
|
|
|
|
// const setupedModules = moduleSetupArray.filter((module, index) => {
|
|
// let disjointFromTrestle = checkModuleDisjointSurface(module.turfPoints, turfModuleSetupSurface)
|
|
// let isDisjoint = checkModuleDisjointObjects(module.turfPoints, containsBatchObjects)
|
|
|
|
// if (!(disjointFromTrestle && isDisjoint)) {
|
|
// canvas?.remove(module)
|
|
// // module.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 })
|
|
// return false
|
|
// } else {
|
|
// return module
|
|
// }
|
|
// })
|
|
|
|
// canvas?.renderAll()
|
|
|
|
//나간애들 제외하고 설치된 애들로 겹친애들 삭제 하기
|
|
moduleSetupArray.forEach((module, index) => {
|
|
if (isMaxSetup && index > 0) {
|
|
const isOverlap = turf.booleanOverlap(polygonToTurfPolygon(moduleSetupArray[index - 1]), polygonToTurfPolygon(module))
|
|
//겹치는지 확인
|
|
if (isOverlap) {
|
|
//겹쳐있으면 삭제
|
|
canvas?.remove(module)
|
|
// module.set({ fill: 'rgba(72, 161, 250, 0.4)', stroke: 'black', strokeWidth: 0.1 })
|
|
canvas.renderAll()
|
|
moduleSetupArray.splice(index, 1)
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
|
|
moduleSetupSurface.set({ modules: moduleSetupArray })
|
|
// getModuleStatistics()
|
|
setModuleStatisticsData()
|
|
// const moduleArray = [...moduleIsSetup]
|
|
// moduleArray.push({
|
|
// surfaceId: moduleSetupSurface.surfaceId,
|
|
// moduleSetupArray: setupedModules,
|
|
// })
|
|
// setModuleIsSetup(moduleArray)
|
|
})
|
|
// calculateForApi()
|
|
}
|
|
|
|
const coordToTurfPolygon = (points) => {
|
|
const coordinates = points.map((point) => [point.x, point.y])
|
|
coordinates.push(coordinates[0])
|
|
return turf.polygon([coordinates])
|
|
}
|
|
|
|
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, length) => {
|
|
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 rtnObjArray = []
|
|
flowArray.forEach((center, index) => {
|
|
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 = Number(length) + 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 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 })
|
|
|
|
// console.log(`index ${index} : finalLine`, pointX1, pointY1, pointX2, pointY2)
|
|
|
|
// canvas?.add(finalLine)
|
|
// canvas?.renderAll()
|
|
|
|
let rtnObj
|
|
//평평하면
|
|
if (alpha === 0 || beta === 0 || h === 0 || sign === 0) {
|
|
//꼭지점이 없고 평평할때 ex) 네모
|
|
let standardLine
|
|
if (index === 0) {
|
|
//bottom
|
|
standardLine = surface.lines.reduce((acc, line, index) => {
|
|
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.y2 > acc.y2)) {
|
|
return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index }
|
|
}
|
|
return acc
|
|
})
|
|
} else {
|
|
standardLine = surface.lines.reduce((acc, line, index) => {
|
|
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.y2 < acc.y2)) {
|
|
return { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, index: index }
|
|
}
|
|
return acc
|
|
})
|
|
}
|
|
rtnObj = {
|
|
target: index === 0 ? 'bottom' : 'top',
|
|
x1: standardLine.x1,
|
|
y1: standardLine.y1,
|
|
x2: standardLine.x2,
|
|
y2: standardLine.y2,
|
|
type: 'flat',
|
|
}
|
|
} else {
|
|
rtnObj = { target: index === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' }
|
|
}
|
|
|
|
rtnObjArray.push(rtnObj)
|
|
})
|
|
return rtnObjArray
|
|
}
|
|
|
|
const leftRightFlowLine = (surface, length) => {
|
|
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 rtnObjArray = []
|
|
flowArray.forEach((center, index) => {
|
|
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 = Number(length) + 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()
|
|
|
|
let rtnObj
|
|
//평평하면
|
|
if (alpha === 0 || beta === 0 || h === 0 || sign === 0) {
|
|
//꼭지점이 없고 평평할때 ex) 네모
|
|
let standardLine
|
|
if (index === 0) {
|
|
//bottom
|
|
standardLine = 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, x2: line.x2, y2: line.y2, index: index }
|
|
}
|
|
return acc
|
|
})
|
|
} else {
|
|
standardLine = 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, x2: line.x2, y2: line.y2, index: index }
|
|
}
|
|
return acc
|
|
})
|
|
}
|
|
rtnObj = {
|
|
target: index === 0 ? 'left' : 'right',
|
|
x1: standardLine.x1,
|
|
y1: standardLine.y1,
|
|
x2: standardLine.x2,
|
|
y2: standardLine.y2,
|
|
type: 'flat',
|
|
}
|
|
} else {
|
|
rtnObj = { target: index === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' }
|
|
}
|
|
rtnObjArray.push(rtnObj)
|
|
})
|
|
return rtnObjArray
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
const manualFlatroofModuleSetup = (placementFlatRef) => {
|
|
let moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
|
|
if (isManualModuleSetup) {
|
|
if (checkedModule.length === 0) {
|
|
swalFire({ text: getMessage('module.place.select.module') })
|
|
setIsManualModuleSetup(!isManualModuleSetup)
|
|
return
|
|
}
|
|
|
|
if (checkedModule.length > 1) {
|
|
swalFire({ text: getMessage('module.place.select.one.module') })
|
|
setIsManualModuleSetup(!isManualModuleSetup)
|
|
return
|
|
}
|
|
|
|
let flatBatchType = placementFlatRef.setupLocation.current.value
|
|
|
|
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 객체
|
|
|
|
let moduleOptions = {
|
|
fill: '#BFFD9F',
|
|
stroke: 'black',
|
|
strokeWidth: 0.3,
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: true, // X 축 이동 잠금
|
|
lockMovementY: true, // Y 축 이동 잠금
|
|
lockRotation: true, // 회전 잠금
|
|
lockScalingX: true, // X 축 크기 조정 잠금
|
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
|
parentId: moduleSetupSurface.parentId,
|
|
surfaceId: moduleSetupSurface.id,
|
|
name: POLYGON_TYPE.MODULE,
|
|
}
|
|
|
|
if (moduleSetupSurfaces.length !== 0) {
|
|
let tempModule
|
|
let manualDrawModules = []
|
|
let inside = false
|
|
let turfPolygon
|
|
let flowDirection
|
|
let trestlePolygon
|
|
|
|
//남쪽 선택
|
|
if (flatBatchType === 'excreta') {
|
|
//변별로 선택
|
|
const excretaLines = canvas.getObjects().filter((obj) => obj.name === 'flatExcretaLine')
|
|
excretaLines.forEach((obj) => {
|
|
if (obj.isSelected === true) {
|
|
const points1 = { x: obj.x1, y: obj.y1 }
|
|
const points2 = { x: obj.x2, y: obj.y2 }
|
|
const angle = calculateAngle(points1, points2)
|
|
|
|
//변별로 선택으로 되어있을때 모듈면을 회전시키기
|
|
const targetdSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0]
|
|
targetdSurface.angle = -angle
|
|
//변별로 선택되어있는 지붕도 회전시키기
|
|
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetdSurface.parentId)[0]
|
|
targetRoof.angle = -angle
|
|
|
|
targetRoof.fire('modified')
|
|
targetdSurface.fire('modified')
|
|
}
|
|
canvas.remove(obj)
|
|
})
|
|
} else {
|
|
moduleSetupSurfaces.forEach((surface) => {
|
|
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0]
|
|
if (targetRoof) targetRoof.angle = -compasDeg
|
|
surface.angle = -compasDeg
|
|
})
|
|
}
|
|
canvas.renderAll()
|
|
|
|
addCanvasMouseEventListener('mouse:move', (e) => {
|
|
//마우스 이벤트 삭제 후 재추가
|
|
const mousePoint = canvas.getPointer(e.e)
|
|
|
|
for (let i = 0; i < moduleSetupSurfaces.length; i++) {
|
|
turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i], true)
|
|
trestlePolygon = moduleSetupSurfaces[i]
|
|
manualDrawModules = moduleSetupSurfaces[i].modules // 앞에서 자동으로 했을때 추가됨
|
|
flowDirection = moduleSetupSurfaces[i].direction //도형의 방향
|
|
|
|
const moduleWidth = Number(checkedModule[0].longAxis) / 10
|
|
const moduleHeight = Number(checkedModule[0].shortAxis) / 10
|
|
|
|
let tmpWidth = flowDirection === 'south' || flowDirection === 'north' ? moduleWidth : moduleHeight
|
|
let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth
|
|
|
|
let { width, height } =
|
|
canvasSetting.roofSizeSet == '1'
|
|
? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection)
|
|
: { width: tmpWidth, height: tmpHeight }
|
|
|
|
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: 0.3,
|
|
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,
|
|
name: 'tempModule',
|
|
parentId: moduleSetupSurfaces[i].parentId,
|
|
})
|
|
|
|
canvas?.add(tempModule) //움직여가면서 추가됨
|
|
|
|
/**
|
|
* 스냅기능
|
|
*/
|
|
let snapDistance = 10
|
|
let cellSnapDistance = 50
|
|
|
|
let intvHor = flowDirection === 'south' || flowDirection === 'north' ? 1 : 3
|
|
let intvVer = flowDirection === 'south' || flowDirection === 'north' ? 3 : 1
|
|
|
|
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 - intvHor
|
|
}
|
|
|
|
//설치된 셀에 우측에 스냅
|
|
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
|
tempModule.left = holdCellRight + intvHor
|
|
}
|
|
|
|
//설치된 셀에 위쪽에 스냅
|
|
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
|
|
tempModule.top = holdCellTop - height - intvVer
|
|
}
|
|
|
|
//설치된 셀에 밑쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
|
|
tempModule.top = holdCellBottom + intvVer
|
|
}
|
|
//가운데 -> 가운데
|
|
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) {
|
|
// tempModule.set({ fill: 'red' })
|
|
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, y: tempModule.top },
|
|
{ x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top },
|
|
{ x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top + tempModule.height * tempModule.scaleY },
|
|
{ x: tempModule.left, y: tempModule.top + tempModule.height * tempModule.scaleY },
|
|
]
|
|
|
|
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) {
|
|
swalFire({ text: getMessage('module.place.overobject') })
|
|
isIntersection = false
|
|
}
|
|
})
|
|
}
|
|
|
|
if (!isIntersection) return
|
|
|
|
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
|
|
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
|
|
const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module, true))) //겹치는지 확인
|
|
if (!isOverlap) {
|
|
moduleOptions.surfaceId = trestlePolygon.id
|
|
let manualModule = new QPolygon(tempModule.points, { ...moduleOptions, moduleInfo: checkedModule[0] })
|
|
canvas?.add(manualModule)
|
|
manualDrawModules.push(manualModule)
|
|
setModuleStatisticsData()
|
|
} else {
|
|
swalFire({ text: getMessage('module.place.overlab') })
|
|
}
|
|
} else {
|
|
swalFire({ text: getMessage('module.place.out') })
|
|
}
|
|
}
|
|
})
|
|
}
|
|
// getModuleStatistics()
|
|
setModuleStatisticsData()
|
|
} else {
|
|
if (moduleSetupSurfaces) {
|
|
//수동모드 해제시 모듈 설치면 선택 잠금
|
|
moduleSetupSurfaces.forEach((obj) => {
|
|
obj.set({ selectable: true, evented: true })
|
|
})
|
|
}
|
|
|
|
removeMouseEvent('mouse:up')
|
|
removeMouseEvent('mouse:move')
|
|
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
|
|
}
|
|
}
|
|
|
|
const autoFlatroofModuleSetup = (placementFlatRef) => {
|
|
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
|
|
// }
|
|
|
|
//어짜피 자동으로 누르면 선택안된데도 다 날아간다
|
|
canvas.getObjects().forEach((obj) => {
|
|
if (obj.name === 'module') {
|
|
canvas.remove(obj)
|
|
}
|
|
})
|
|
|
|
notSelectedTrestlePolygons.forEach((obj) => {
|
|
if (obj.modules) {
|
|
obj.modules.forEach((module) => {
|
|
canvas?.remove(module)
|
|
})
|
|
obj.modules = []
|
|
}
|
|
})
|
|
|
|
const flatBatchType = placementFlatRef.setupLocation.current.value
|
|
|
|
//남쪽 선택
|
|
if (flatBatchType === 'excreta') {
|
|
//변별로 선택
|
|
const excretaLines = canvas.getObjects().filter((obj) => obj.name === 'flatExcretaLine')
|
|
excretaLines.forEach((obj) => {
|
|
if (obj.isSelected === true) {
|
|
const points1 = { x: obj.x1, y: obj.y1 }
|
|
const points2 = { x: obj.x2, y: obj.y2 }
|
|
const angle = calculateAngle(points1, points2)
|
|
|
|
// const targetdSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0]
|
|
const targetSurface = canvas
|
|
.getObjects()
|
|
.filter((surface) => surface.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && surface.surfaceId === obj.surfaceId)[0]
|
|
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetSurface.parentId)[0]
|
|
|
|
targetRoof.angle = -angle
|
|
targetSurface.angle = -angle
|
|
|
|
const newLines = createLinesFromPolygon(targetSurface.getCurrentPoints())
|
|
targetSurface.set({ lines: newLines })
|
|
|
|
targetRoof.fire('modified')
|
|
targetSurface.fire('modified')
|
|
targetRoof.setCoords()
|
|
targetSurface.setCoords()
|
|
moduleSetupSurfaces.push(targetSurface)
|
|
}
|
|
canvas.remove(obj)
|
|
})
|
|
} else {
|
|
moduleSetupSurfaces.forEach((surface) => {
|
|
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0]
|
|
if (targetRoof) targetRoof.angle = -compasDeg
|
|
surface.angle = -compasDeg
|
|
})
|
|
}
|
|
canvas.renderAll()
|
|
|
|
moduleSetupSurfaces.forEach((surface) => {
|
|
let currentPoints = surface.getCurrentPoints()
|
|
let lines = []
|
|
|
|
for (let i = 0; i < currentPoints.length; i++) {
|
|
const start = currentPoints[i]
|
|
const end = currentPoints[(i + 1) % currentPoints.length]
|
|
const line = new QLine([start.x, start.y, end.x, end.y], {})
|
|
lines.push(line)
|
|
}
|
|
|
|
surface.lines.forEach((targetLine, index) => {
|
|
targetLine.x1 = lines[index].x1
|
|
targetLine.y1 = lines[index].y1
|
|
targetLine.x2 = lines[index].x2
|
|
targetLine.y2 = lines[index].y2
|
|
})
|
|
|
|
// const flowLines = {
|
|
// bottom: bottomTopFlowLine(surface).find((obj) => obj.target === 'bottom'),
|
|
// top: bottomTopFlowLine(surface).find((obj) => obj.target === 'top'),
|
|
// left: leftRightFlowLine(surface).find((obj) => obj.target === 'left'),
|
|
// right: leftRightFlowLine(surface).find((obj) => obj.target === 'right'),
|
|
// }
|
|
|
|
// surface.set({ flowLines: flowLines })
|
|
})
|
|
|
|
let moduleOptions = {
|
|
fill: '#BFFD9F',
|
|
stroke: 'black',
|
|
strokeWidth: 0.3,
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: true, // X 축 이동 잠금
|
|
lockMovementY: true, // Y 축 이동 잠금
|
|
lockRotation: true, // 회전 잠금
|
|
lockScalingX: true, // X 축 크기 조정 잠금
|
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
|
parentId: moduleSetupSurface.parentId,
|
|
surfaceId: moduleSetupSurface.id,
|
|
name: POLYGON_TYPE.MODULE,
|
|
}
|
|
|
|
let leftMargin, bottomMargin, square
|
|
|
|
//선택된 지붕안에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 포함되면 배열 반환
|
|
const objectsIncludeSurface = (turfModuleSetupSurface) => {
|
|
let containsBatchObjects = []
|
|
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)
|
|
})
|
|
|
|
return containsBatchObjects
|
|
}
|
|
|
|
/**
|
|
* 배치면 안에 있는지 확인
|
|
* @param {*} squarePolygon
|
|
* @param {*} turfModuleSetupSurface
|
|
* @returns
|
|
*/
|
|
const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => {
|
|
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
|
|
}
|
|
|
|
let moduleGroup = []
|
|
|
|
const flatRoofDownFlowSetupModule = (
|
|
surfaceMaxLines,
|
|
maxLengthLine,
|
|
moduleSetupArray,
|
|
moduleSetupSurface,
|
|
intvHor,
|
|
intvVer,
|
|
containsBatchObjects,
|
|
) => {
|
|
let setupModule = []
|
|
|
|
let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
|
|
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
|
|
let flowLines
|
|
|
|
checkedModule.forEach((module, moduleIndex) => {
|
|
//모듈의 넓이 높이를 가져옴 (복시도 촌수 적용)
|
|
//1번 깔았던 모듈 기준으로 잡야아함
|
|
let { width, height } = getModuleWidthHeight(maxLengthLine, moduleSetupSurface, module)
|
|
|
|
if (moduleIndex === 0) {
|
|
flowLines = getFlowLines(moduleSetupSurface, height)
|
|
if (flowLines.bottom.type === 'curve') {
|
|
flowLines = getFlowLines(moduleSetupSurface, width)
|
|
}
|
|
}
|
|
|
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
|
const moduleArray = []
|
|
|
|
let calcAreaWidth = flowLines.right.x1 - flowLines.left.x1 //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
|
|
let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
let calcMaxModuleWidthCount = calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcAreaheight = flowLines.bottom.y1 - flowLines.top.y1 //오른쪽 y에서 왼쪽 y를 뺀 가운데를 찾는 로직
|
|
let calcModuleHeightCount = calcAreaheight / (height + intvVer) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
|
let calcMaxModuleHeightCount = calcModuleHeightCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
|
let totalModuleHeightCount = Math.floor(calcMaxModuleHeightCount) //치조배치일경우는 한개 더 넣는다
|
|
|
|
let calcStartPoint = flowLines.right.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * width) / 2 : 0 //반씩 나눠서 중앙에 맞춤 bottom 기준으로 양변이 직선일때만 가운데 정렬
|
|
let startPointX = flowLines.left.x1 + calcStartPoint //시작점을 만든다
|
|
|
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
|
if (flowLines.left.type === 'curve' && flowLines.right.type === 'curve') {
|
|
startPointX = flowLines.left.x1 + (calcAreaWidth - totalModuleWidthCount * width) / 2
|
|
|
|
if (flowLines.left.x1 < flowLines.bottom.x1) {
|
|
startPointX = flowLines.left.x1
|
|
}
|
|
}
|
|
|
|
let heightMargin = 0
|
|
let widthMargin = 0
|
|
let chidoriLength = 0
|
|
let moduleMaxRows = totalModuleHeightCount
|
|
|
|
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
|
if (moduleIndex > 0) {
|
|
moduleMaxRows = moduleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
|
}
|
|
|
|
let isInstall = false
|
|
|
|
for (let i = 0; i < moduleMaxRows; i++) {
|
|
let moduleY = flowLines.bottom.y1 - height * i - 1 //살짝 여유를 준다
|
|
|
|
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
|
|
if (moduleIndex > 0) {
|
|
moduleY = installedLastHeightCoord - intvVer
|
|
}
|
|
|
|
//첫번째는 붙여서 두번째는 마진을 주고 설치
|
|
heightMargin = i === 0 ? 0 : intvVer * i
|
|
|
|
for (let j = 0; j < totalModuleWidthCount; j++) {
|
|
let moduleX = startPointX + width * j + 1 //5정도 마진을 준다
|
|
widthMargin = j === 0 ? 0 : intvHor * j // 가로 마진값
|
|
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
|
|
|
let square = [
|
|
[moduleX + widthMargin, moduleY - height - heightMargin],
|
|
[moduleX + widthMargin, moduleY - heightMargin],
|
|
[moduleX + width + widthMargin, moduleY - heightMargin],
|
|
[moduleX + width + widthMargin, moduleY - height - heightMargin],
|
|
[moduleX + widthMargin, moduleY - height - heightMargin],
|
|
]
|
|
|
|
let squarePolygon = turf.polygon([square])
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
|
|
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
|
|
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
|
|
|
|
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
|
|
let isDisjoint = checkModuleDisjointObjects(squarePolygon, containsBatchObjects)
|
|
|
|
if (disjointFromTrestle && isDisjoint) {
|
|
//최초 한번은 그냥 그린다
|
|
//겹치는지 확인해서 포함된 모듈만 그린다
|
|
canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
moduleArray.push(tempModule)
|
|
canvas.renderAll()
|
|
|
|
// ++installedModuleHeightCount
|
|
|
|
isInstall = true
|
|
//마지막에 설치된 모듈의 Y 좌표
|
|
installedLastHeightCoord = moduleY - height - heightMargin
|
|
} else {
|
|
//디버깅용
|
|
// tempModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
|
|
// canvas?.add(tempModule)
|
|
// canvas.renderAll()
|
|
}
|
|
}
|
|
if (isInstall) {
|
|
++installedModuleHeightCount
|
|
}
|
|
}
|
|
setupModule.push(moduleArray)
|
|
})
|
|
}
|
|
|
|
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
|
moduleSetupSurface.fire('mousedown')
|
|
const moduleSetupArray = []
|
|
|
|
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
|
|
return acc.length > cur.length ? acc : cur
|
|
})
|
|
|
|
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface, true) //폴리곤을 turf 객체로 변환
|
|
const containsBatchObjects = objectsIncludeSurface(turfModuleSetupSurface) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직
|
|
|
|
const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
|
|
const intvHor = 30
|
|
const intvVer = 10
|
|
|
|
canvas.renderAll()
|
|
|
|
//육지붕은 왼쪽 기준으로 그려진다
|
|
flatRoofDownFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, intvHor, intvVer, containsBatchObjects)
|
|
|
|
const setupedModules = moduleSetupArray.filter((module, index) => {
|
|
let disjointFromTrestle = checkModuleDisjointSurface(module.turfPoints, turfModuleSetupSurface)
|
|
let isDisjoint = checkModuleDisjointObjects(module.turfPoints, containsBatchObjects)
|
|
|
|
if (!(disjointFromTrestle && isDisjoint)) {
|
|
canvas?.remove(module)
|
|
// module.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 })
|
|
return false
|
|
} else {
|
|
return module
|
|
}
|
|
})
|
|
|
|
canvas?.renderAll()
|
|
|
|
// 나간애들 제외하고 설치된 애들로 겹친애들 삭제 하기
|
|
setupedModules.forEach((module, index) => {
|
|
if (index > 0) {
|
|
const isOverlap = turf.booleanOverlap(polygonToTurfPolygon(setupedModules[index - 1]), polygonToTurfPolygon(module))
|
|
//겹치는지 확인
|
|
if (isOverlap) {
|
|
//겹쳐있으면 삭제
|
|
canvas?.remove(module)
|
|
// module.set({ fill: 'rgba(72, 161, 250, 0.4)', stroke: 'black', strokeWidth: 0.1 })
|
|
canvas.renderAll()
|
|
setupedModules.splice(index, 1)
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
|
|
moduleSetupSurface.set({ modules: setupedModules })
|
|
setModuleStatisticsData()
|
|
// getModuleStatistics()
|
|
// console.log('moduleSetupSurface', moduleSetupSurface)
|
|
// console.log('setupedModules', setupedModules)
|
|
|
|
// const groupTest = new fabric.Group([moduleSetupSurface, ...setupedModules], {
|
|
// angle: compasDeg,
|
|
// })
|
|
|
|
// canvas.add(groupTest)
|
|
})
|
|
// console.log(calculateForApi())
|
|
|
|
//드래그 하기위해 기능 활성화
|
|
}
|
|
|
|
/**
|
|
* 도머나 개구가 모듈에 걸치는지 확인하는 로직
|
|
* @param {*} squarePolygon
|
|
* @param {*} containsBatchObjects
|
|
* @returns
|
|
*/
|
|
const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => {
|
|
let isDisjoint = false
|
|
|
|
if (containsBatchObjects.length > 0) {
|
|
let convertBatchObject
|
|
//도머가 있으면 적용되는 로직
|
|
isDisjoint = containsBatchObjects.every((batchObject) => {
|
|
if (batchObject.type === 'group') {
|
|
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
|
|
} else {
|
|
if (!batchObject.points) {
|
|
batchObject.set({ points: rectToPolygon(batchObject) })
|
|
}
|
|
convertBatchObject = polygonToTurfPolygon(batchObject)
|
|
}
|
|
/**
|
|
* 도머가 여러개일수있으므로 겹치는게 있다면...
|
|
* 안겹치는지 확인하는 로직이라 안겹치면 true를 반환
|
|
*/
|
|
return turf.booleanDisjoint(squarePolygon, convertBatchObject)
|
|
})
|
|
} else {
|
|
isDisjoint = true
|
|
}
|
|
return isDisjoint
|
|
}
|
|
|
|
/**
|
|
* 모듈의 너비와 높이를 계산하는 함수
|
|
* @param {object} maxLengthLine 최대 길이 라인
|
|
* @param {object} moduleSetupSurface 모듈 설치면
|
|
* @param {object} module 모듈
|
|
* @returns {object} 모듈의 너비와 높이
|
|
*/
|
|
const getModuleWidthHeight = (maxLengthLine, moduleSetupSurface, module) => {
|
|
let tmpWidth =
|
|
(maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? Number(module.longAxis) : Number(module.shortAxis)) / 10
|
|
let tmpHeight =
|
|
(maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? Number(module.shortAxis) : Number(module.longAxis)) / 10
|
|
|
|
//배치면때는 방향쪽으로 패널이 넓게 누워져야함
|
|
if (moduleSetupSurface.direction !== undefined) {
|
|
tmpWidth =
|
|
(moduleSetupSurface.direction === 'south' || moduleSetupSurface.direction === 'north' ? Number(module.longAxis) : Number(module.shortAxis)) /
|
|
10
|
|
tmpHeight =
|
|
(moduleSetupSurface.direction === 'south' || moduleSetupSurface.direction === 'north' ? Number(module.shortAxis) : Number(module.longAxis)) /
|
|
10
|
|
}
|
|
|
|
return canvasSetting.roofSizeSet == '1'
|
|
? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurface.roofMaterial.pitch), moduleSetupSurface.direction)
|
|
: { width: tmpWidth, height: tmpHeight }
|
|
}
|
|
|
|
const getFlowLines = (moduleSetupSurface, length) => {
|
|
let flowLines = {}
|
|
|
|
flowLines = {
|
|
bottom: bottomTopFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'bottom'),
|
|
top: bottomTopFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'top'),
|
|
left: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'left'),
|
|
right: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'right'),
|
|
}
|
|
return flowLines
|
|
}
|
|
|
|
return {
|
|
selectedModules,
|
|
makeModuleInstArea,
|
|
manualModuleSetup,
|
|
autoModuleSetup,
|
|
restoreModuleInstArea,
|
|
manualFlatroofModuleSetup,
|
|
autoFlatroofModuleSetup,
|
|
checkModuleDisjointObjects,
|
|
makeModuleInitArea,
|
|
}
|
|
}
|