1194 lines
44 KiB
JavaScript
1194 lines
44 KiB
JavaScript
import { useContext, useEffect, useState } from 'react'
|
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
|
import { canvasState } from '@/store/canvasAtom'
|
|
import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
|
|
import { roofDisplaySelector } from '@/store/settingAtom'
|
|
import offsetPolygon from '@/util/qpolygon-utils'
|
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
|
|
import { useEvent } from '@/hooks/useEvent'
|
|
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
|
|
|
|
import * as turf from '@turf/turf'
|
|
import { EventContext } from '@/app/floor-plan/EventProvider'
|
|
|
|
export function useModuleBasicSetting() {
|
|
const canvas = useRecoilValue(canvasState)
|
|
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
|
const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
|
|
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
|
|
// const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent()
|
|
const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
|
let selectedModuleInstSurfaceArray = []
|
|
|
|
const makeModuleInstArea = () => {
|
|
//지붕 객체 반환
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
|
|
|
if (!roofs) {
|
|
return
|
|
}
|
|
|
|
roofs.forEach((roof) => {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경
|
|
const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset
|
|
//모듈설치영역?? 생성
|
|
let setupSurface = new QPolygon(offsetPoints, {
|
|
stroke: 'red',
|
|
fill: 'transparent',
|
|
strokeDashArray: [10, 4],
|
|
strokeWidth: 1,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
selectable: true,
|
|
parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌
|
|
name: POLYGON_TYPE.MODULE_SETUP_SURFACE,
|
|
flowDirection: roof.direction,
|
|
flipX: roof.flipX,
|
|
flipY: roof.flipY,
|
|
})
|
|
|
|
setupSurface.setViewLengthText(false)
|
|
|
|
canvas.add(setupSurface)
|
|
bottomModuleLine(setupSurface)
|
|
topModuleLine(setupSurface)
|
|
leftModuleLine(setupSurface)
|
|
// rightModuleLine(setupSurface)
|
|
|
|
//지붕면 선택 금지
|
|
roof.set({
|
|
selectable: false,
|
|
})
|
|
|
|
//모듈설치면 클릭이벤트
|
|
addTargetMouseEventListener('mousedown', setupSurface, function () {
|
|
toggleSelection(setupSurface)
|
|
})
|
|
})
|
|
}
|
|
|
|
//설치 범위 지정 클릭 이벤트
|
|
const toggleSelection = (setupSurface) => {
|
|
console.log('setupSurface', setupSurface)
|
|
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
|
|
//최초 선택일때
|
|
if (!isExist) {
|
|
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
|
|
setupSurface.set({
|
|
...setupSurface,
|
|
strokeWidth: 3,
|
|
strokeDashArray: [0],
|
|
fill: 'transparent',
|
|
})
|
|
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
|
//중복으로 들어가는걸 방지하기 위한 코드
|
|
|
|
canvas?.renderAll()
|
|
selectedModuleInstSurfaceArray.push(setupSurface)
|
|
} else {
|
|
//선택후 재선택하면 선택안됨으로 변경
|
|
setupSurface.set({
|
|
...setupSurface,
|
|
fill: 'transparent',
|
|
strokeDashArray: [10, 4],
|
|
strokeWidth: 1,
|
|
})
|
|
canvas.discardActiveObject() // 객체의 활성 상태 해제
|
|
|
|
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
|
|
const removeIndex = setupSurface.parentId
|
|
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
|
|
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
|
|
}
|
|
|
|
canvas?.renderAll()
|
|
setModuleSetupSurface([...selectedModuleInstSurfaceArray])
|
|
}
|
|
|
|
/**
|
|
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
|
|
* 확인 후 셀을 이동시킴
|
|
*/
|
|
const manualModuleSetup = () => {
|
|
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
|
|
const batchObjects = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(obj) =>
|
|
obj.name === BATCH_TYPE.OPENING ||
|
|
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
|
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
|
obj.name === BATCH_TYPE.SHADOW,
|
|
) //도머s 객체
|
|
|
|
if (moduleSetupSurfaces.length !== 0) {
|
|
let tempModule
|
|
let manualDrawModules = moduleIsSetup // 앞에서 자동으로 했을때 추가됨
|
|
let inside = false
|
|
let turfPolygon
|
|
let flowDirection
|
|
let trestlePolygon
|
|
addCanvasMouseEventListener('mouse:move', (e) => {
|
|
//마우스 이벤트 삭제 후 재추가
|
|
const mousePoint = canvas.getPointer(e.e)
|
|
|
|
for (let i = 0; i < moduleSetupSurfaces.length; i++) {
|
|
turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i])
|
|
trestlePolygon = moduleSetupSurfaces[i]
|
|
flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향
|
|
let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113
|
|
let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172
|
|
|
|
const points = [
|
|
{ x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
|
|
{ x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
|
|
{ x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
|
|
{ x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
|
|
]
|
|
|
|
const turfPoints = coordToTurfPolygon(points)
|
|
|
|
if (turf.booleanWithin(turfPoints, turfPolygon)) {
|
|
let isDrawing = false
|
|
|
|
if (isDrawing) return
|
|
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
|
|
|
|
tempModule = new fabric.Rect({
|
|
fill: 'white',
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
width: width,
|
|
height: height,
|
|
left: mousePoint.x - width / 2,
|
|
top: mousePoint.y - height / 2,
|
|
selectable: false,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
opacity: 0.8,
|
|
name: 'tempModule',
|
|
parentId: moduleSetupSurfaces[i].parentId,
|
|
})
|
|
|
|
canvas?.add(tempModule) //움직여가면서 추가됨
|
|
|
|
/**
|
|
* 스냅기능
|
|
*/
|
|
let snapDistance = 10
|
|
let cellSnapDistance = 20
|
|
|
|
const trestleLeft = moduleSetupSurfaces[i].left
|
|
const trestleTop = moduleSetupSurfaces[i].top
|
|
const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX
|
|
const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY
|
|
const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
|
|
|
|
// 작은 폴리곤의 경계 좌표 계산
|
|
const smallLeft = tempModule.left
|
|
const smallTop = tempModule.top
|
|
const smallRight = smallLeft + tempModule.width * tempModule.scaleX
|
|
const smallBottom = smallTop + tempModule.height * tempModule.scaleY
|
|
const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2
|
|
const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2
|
|
|
|
/**
|
|
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
|
|
*/
|
|
if (manualDrawModules) {
|
|
manualDrawModules.forEach((cell) => {
|
|
const holdCellLeft = cell.left
|
|
const holdCellTop = cell.top
|
|
const holdCellRight = holdCellLeft + cell.width * cell.scaleX
|
|
const holdCellBottom = holdCellTop + cell.height * cell.scaleY
|
|
const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2
|
|
const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2
|
|
|
|
//설치된 셀에 좌측에 스냅
|
|
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
|
|
tempModule.left = holdCellLeft - width - 0.5
|
|
}
|
|
|
|
//설치된 셀에 우측에 스냅
|
|
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
|
tempModule.left = holdCellRight + 0.5
|
|
}
|
|
|
|
//설치된 셀에 위쪽에 스냅
|
|
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
|
|
tempModule.top = holdCellTop - height - 0.5
|
|
}
|
|
|
|
//설치된 셀에 밑쪽에 스냅
|
|
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
|
|
tempModule.top = holdCellBottom + 0.5
|
|
}
|
|
//가운데 -> 가운데
|
|
if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
|
|
tempModule.left = holdCellCenterX - width / 2
|
|
}
|
|
//왼쪽 -> 가운데
|
|
if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
|
|
tempModule.left = holdCellCenterX
|
|
}
|
|
// 오른쪽 -> 가운데
|
|
if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
|
|
tempModule.left = holdCellCenterX - width
|
|
}
|
|
//세로 가운데 -> 가운데
|
|
if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
|
|
tempModule.top = holdCellCenterY - height / 2
|
|
}
|
|
//위쪽 -> 가운데
|
|
if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
|
|
tempModule.top = holdCellCenterY
|
|
}
|
|
//아랫쪽 -> 가운데
|
|
if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
|
|
tempModule.top = holdCellCenterY - height
|
|
}
|
|
})
|
|
}
|
|
|
|
// 위쪽 변에 스냅
|
|
if (Math.abs(smallTop - trestleTop) < snapDistance) {
|
|
tempModule.top = trestleTop
|
|
}
|
|
|
|
// 아래쪽 변에 스냅
|
|
if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) {
|
|
tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY
|
|
}
|
|
|
|
// 왼쪽변에 스냅
|
|
if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
|
|
tempModule.left = trestleLeft
|
|
}
|
|
//오른쪽 변에 스냅
|
|
if (Math.abs(smallRight - trestleRight) < snapDistance) {
|
|
tempModule.left = trestleRight - tempModule.width * tempModule.scaleX
|
|
}
|
|
|
|
if (flowDirection === 'south' || flowDirection === 'north') {
|
|
// 모듈왼쪽이 세로중앙선에 붙게 스냅
|
|
if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
|
|
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2
|
|
}
|
|
|
|
// 모듈이 가운데가 세로중앙선에 붙게 스냅
|
|
if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
|
|
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2
|
|
}
|
|
|
|
// 모듈오른쪽이 세로중앙선에 붙게 스냅
|
|
if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
|
|
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
|
|
}
|
|
} else {
|
|
// 모듈이 가로중앙선에 스냅
|
|
if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) {
|
|
tempModule.top = bigCenterY - tempModule.height / 2
|
|
}
|
|
|
|
if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
|
|
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
|
|
}
|
|
// 모듈 밑면이 가로중앙선에 스냅
|
|
if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
|
|
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY
|
|
}
|
|
}
|
|
|
|
tempModule.setCoords()
|
|
canvas?.renderAll()
|
|
inside = true
|
|
break
|
|
} else {
|
|
inside = false
|
|
}
|
|
}
|
|
|
|
if (!inside) {
|
|
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule'))
|
|
canvas?.renderAll()
|
|
}
|
|
})
|
|
|
|
addCanvasMouseEventListener('mouse:up', (e) => {
|
|
let isIntersection = true
|
|
if (!inside) return
|
|
if (tempModule) {
|
|
const rectPoints = [
|
|
{ x: tempModule.left + 0.5, y: tempModule.top + 0.5 },
|
|
{ x: tempModule.left + 0.5 + tempModule.width * tempModule.scaleX, y: tempModule.top + 0.5 },
|
|
{
|
|
x: tempModule.left + tempModule.width * tempModule.scaleX + 0.5,
|
|
y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5,
|
|
},
|
|
{ x: tempModule.left + 0.5, y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5 },
|
|
]
|
|
|
|
tempModule.set({ points: rectPoints })
|
|
const tempTurfModule = polygonToTurfPolygon(tempModule)
|
|
|
|
//도머 객체를 가져옴
|
|
if (batchObjects) {
|
|
batchObjects.forEach((object) => {
|
|
let dormerTurfPolygon
|
|
|
|
if (object.type === 'group') {
|
|
//도머는 그룹형태임
|
|
dormerTurfPolygon = batchObjectGroupToTurfPolygon(object)
|
|
} else {
|
|
//개구, 그림자
|
|
dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object))
|
|
}
|
|
|
|
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
|
|
//겹치면 안됨
|
|
if (intersection) {
|
|
alert('도머위에 모듈을 올릴 수 없습니다.')
|
|
isIntersection = false
|
|
}
|
|
})
|
|
}
|
|
|
|
if (!isIntersection) return
|
|
|
|
tempModule.setCoords() //좌표 재정렬
|
|
|
|
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
|
|
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
|
|
const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
|
|
if (!isOverlap) {
|
|
//안겹치면 넣는다
|
|
tempModule.setCoords()
|
|
tempModule.set({ name: 'module', fill: '#BFFD9F' })
|
|
manualDrawModules.push(tempModule) //모듈배열에 추가
|
|
//해당 모듈에 프로퍼티로 넣는다
|
|
trestlePolygon.set({
|
|
modules: manualDrawModules,
|
|
})
|
|
} else {
|
|
alert('셀끼리 겹치면 안되죠?')
|
|
}
|
|
} else {
|
|
alert('나갔죠?!!')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
//자동 모듈 설치(그리드 방식)
|
|
const autoModuleSetup = (placementRef) => {
|
|
const isChidori = placementRef.isChidori.current
|
|
|
|
initEvent()
|
|
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
|
|
|
|
const notSelectedTrestlePolygons = canvas
|
|
?.getObjects()
|
|
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것
|
|
|
|
const batchObjects = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(obj) =>
|
|
obj.name === BATCH_TYPE.OPENING ||
|
|
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
|
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
|
obj.name === BATCH_TYPE.SHADOW,
|
|
) //도머s 객체
|
|
|
|
if (moduleSetupSurfaces.length === 0) {
|
|
alert('선택된 모듈 설치면이 없습니다.')
|
|
return
|
|
}
|
|
|
|
if (moduleIsSetup.length > 0) {
|
|
alert('기존 모듈은 제거됩니다.')
|
|
moduleIsSetup.forEach((module) => {
|
|
canvas?.remove(module)
|
|
})
|
|
}
|
|
|
|
notSelectedTrestlePolygons.forEach((obj) => {
|
|
if (obj.modules) {
|
|
obj.modules.forEach((module) => {
|
|
canvas?.remove(module)
|
|
})
|
|
obj.modules = []
|
|
}
|
|
})
|
|
|
|
const moduleSetupArray = []
|
|
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
|
moduleSetupSurface.fire('mousedown')
|
|
|
|
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
|
|
return acc.length > cur.length ? acc : cur
|
|
})
|
|
|
|
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
|
|
|
|
const containsBatchObjects = batchObjects.filter((batchObject) => {
|
|
let convertBatchObject
|
|
|
|
if (batchObject.type === 'group') {
|
|
//도머는 그룹형태임
|
|
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
|
|
} else {
|
|
//개구, 그림자
|
|
batchObject.set({
|
|
points: rectToPolygon(batchObject),
|
|
})
|
|
canvas?.renderAll() // set된걸 바로 적용하기 위해
|
|
convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환
|
|
}
|
|
|
|
// 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
|
|
return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface)
|
|
})
|
|
|
|
let difference = turfModuleSetupSurface //기본 객체(면형상)
|
|
|
|
if (containsBatchObjects.length > 0) {
|
|
//turf로 도머를 제외시키는 로직
|
|
for (let i = 0; i < containsBatchObjects.length; i++) {
|
|
let convertBatchObject
|
|
if (containsBatchObjects[i].type === 'group') {
|
|
convertBatchObject = batchObjectGroupToTurfPolygon(containsBatchObjects[i])
|
|
} else {
|
|
convertBatchObject = polygonToTurfPolygon(containsBatchObjects[i])
|
|
}
|
|
|
|
if (i === 0) {
|
|
difference = turf.difference(turf.featureCollection([turfModuleSetupSurface, convertBatchObject])) //한 면에 도머가 1개일때
|
|
} else {
|
|
if (difference) {
|
|
difference = turf.difference(turf.featureCollection([difference, convertBatchObject])) //한면에 도머가 여러개일때 계속 제외시킴
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bbox = turf.bbox(difference)
|
|
|
|
let width = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 172.2 : 113.4
|
|
let height = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 113.4 : 172.2
|
|
|
|
//배치면때는 방향쪽으로 패널이 넓게 누워져야함
|
|
if (moduleSetupSurface.flowDirection !== undefined) {
|
|
width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4
|
|
height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2
|
|
}
|
|
|
|
let cols = Math.floor((bbox[2] - bbox[0]) / width)
|
|
let rows = Math.floor((bbox[3] - bbox[1]) / height)
|
|
|
|
// cols = cols * 2
|
|
|
|
for (let col = 0; col <= cols; col++) {
|
|
for (let row = 0; row <= rows; row++) {
|
|
let x = 0,
|
|
y = 0,
|
|
square = [],
|
|
margin = 0
|
|
if (moduleSetupSurface.flowDirection !== undefined) {
|
|
//배치면 처림 방향이 정해져있는 경우
|
|
if (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north') {
|
|
//남,북
|
|
margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨
|
|
if (moduleSetupSurface.flowDirection === 'south') {
|
|
//남쪽
|
|
x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin //상하 위치 기준이면 좌우 가운데 정렬한다
|
|
y = bbox[3] - row * height
|
|
} else {
|
|
//북쪽
|
|
x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin
|
|
y = bbox[1] + row * height
|
|
}
|
|
} else if (moduleSetupSurface.flowDirection === 'east' || moduleSetupSurface.flowDirection === 'west') {
|
|
//동쪽
|
|
margin = (bbox[3] - bbox[1] - rows * height) / 2
|
|
if (moduleSetupSurface.flowDirection === 'east') {
|
|
x = bbox[2] - col * width
|
|
y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin //좌우 위치 기준이면 상하 가운데 정렬한다
|
|
} else {
|
|
x = bbox[0] + col * width
|
|
y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin
|
|
}
|
|
}
|
|
} else {
|
|
//방향이 없는 경우 ex) 템플릿
|
|
x = bbox[0] + col * width
|
|
y = bbox[1] + row * height
|
|
}
|
|
|
|
if (isChidori === 'true') {
|
|
if (row % 2 !== 0) {
|
|
square = [
|
|
[x, y],
|
|
[x + width, y],
|
|
[x + width, y + height],
|
|
[x, y + height],
|
|
[x, y],
|
|
]
|
|
} else {
|
|
square = [
|
|
[x - width / 2, y],
|
|
[x - width / 2 + width, y],
|
|
[x - width / 2 + width, y + height],
|
|
[x - width / 2, y + height],
|
|
[x - width / 2, y],
|
|
]
|
|
}
|
|
} else {
|
|
square = [
|
|
[x, y],
|
|
[x + width, y],
|
|
[x + width, y + height],
|
|
[x, y + height],
|
|
[x, y],
|
|
]
|
|
}
|
|
|
|
// square = [
|
|
// [x - width / 2, y],
|
|
// [x - width / 2 + width, y],
|
|
// [x - width / 2 + width, y + height],
|
|
// [x - width / 2, y + height],
|
|
// [x - width / 2, y],
|
|
// ]
|
|
|
|
const squarePolygon = turf.polygon([square])
|
|
const disjointFromTrestle =
|
|
turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
|
|
if (disjointFromTrestle) {
|
|
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
|
|
const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
|
|
if (containsBatchObjects.length > 0) {
|
|
let convertBatchObject
|
|
//도머가 있으면 적용되는 로직
|
|
const isDisjoint = containsBatchObjects.every((batchObject) => {
|
|
if (batchObject.type === 'group') {
|
|
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
|
|
} else {
|
|
convertBatchObject = polygonToTurfPolygon(batchObject)
|
|
}
|
|
return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면...
|
|
})
|
|
if (isDisjoint) {
|
|
const tempModule = new QPolygon(points, {
|
|
fill: '#BFFD9F',
|
|
stroke: 'black',
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: false, // X 축 이동 잠금
|
|
lockMovementY: false, // Y 축 이동 잠금
|
|
lockRotation: false, // 회전 잠금
|
|
lockScalingX: false, // X 축 크기 조정 잠금
|
|
lockScalingY: false, // Y 축 크기 조정 잠금
|
|
opacity: 0.8,
|
|
parentId: moduleSetupSurface.parentId,
|
|
lineCol: col,
|
|
lineRow: row,
|
|
name: 'module',
|
|
})
|
|
tempModule.setViewLengthText(false)
|
|
// canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
}
|
|
} else {
|
|
//도머가 없을땐 그냥 그림
|
|
const tempModule = new QPolygon(points, {
|
|
fill: '#BFFD9F',
|
|
stroke: 'black',
|
|
selectable: true, // 선택 가능하게 설정
|
|
lockMovementX: true, // X 축 이동 잠금
|
|
lockMovementY: true, // Y 축 이동 잠금
|
|
lockRotation: true, // 회전 잠금
|
|
lockScalingX: true, // X 축 크기 조정 잠금
|
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
|
opacity: 0.8,
|
|
parentId: moduleSetupSurface.parentId,
|
|
lineCol: col,
|
|
lineRow: row,
|
|
name: 'module',
|
|
})
|
|
// canvas?.add(tempModule)
|
|
moduleSetupArray.push(tempModule)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
moduleSetupSurface.set({ modules: moduleSetupArray })
|
|
})
|
|
|
|
setModuleIsSetup(moduleSetupArray)
|
|
|
|
console.log(calculateForApi(moduleSetupArray))
|
|
}
|
|
|
|
const calculateForApi = (moduleSetupArray) => {
|
|
const centerPoints = []
|
|
moduleSetupArray.forEach((module, index) => {
|
|
module.tempIndex = index
|
|
const { x, y } = module.getCenterPoint()
|
|
const { width, height } = module
|
|
centerPoints.push({ x, y, width, height, index })
|
|
const circle = new fabric.Circle({
|
|
radius: 5,
|
|
fill: 'red',
|
|
name: 'redCircle',
|
|
left: x - 5,
|
|
top: y - 5,
|
|
index: index,
|
|
selectable: false,
|
|
})
|
|
canvas.add(circle)
|
|
})
|
|
|
|
//완전 노출 하면
|
|
let exposedBottom = 0
|
|
// 반 노출 하면
|
|
let exposedHalfBottom = 0
|
|
// 완전 노출 상면
|
|
let exposedTop = 0
|
|
//반 노출 상면
|
|
let exposedHalfTop = 0
|
|
// 완전 접면
|
|
let touchDimension = 0
|
|
//반접면
|
|
let halfTouchDimension = 0
|
|
// 노출하면 체크
|
|
centerPoints.forEach((centerPoint, index) => {
|
|
const { x, y, width, height } = centerPoint
|
|
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
|
|
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
|
|
if (bottomCell.length === 1) {
|
|
touchDimension++
|
|
return
|
|
}
|
|
|
|
const bottomLeftPoint = { x: x - width / 2, y: y + height }
|
|
const bottomRightPoint = { x: x + width / 2, y: y + height }
|
|
|
|
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
|
|
const leftBottomCnt = centerPoints.filter(
|
|
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
|
|
).length
|
|
const rightBottomCnt = centerPoints.filter(
|
|
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
|
|
).length
|
|
if (leftBottomCnt + rightBottomCnt === 2) {
|
|
touchDimension++
|
|
return
|
|
}
|
|
if (leftBottomCnt + rightBottomCnt === 1) {
|
|
halfTouchDimension++
|
|
exposedHalfBottom++
|
|
return
|
|
}
|
|
if (leftBottomCnt + rightBottomCnt === 0) {
|
|
exposedBottom++
|
|
return
|
|
}
|
|
})
|
|
// 노출상면 체크
|
|
|
|
centerPoints.forEach((centerPoint, index) => {
|
|
const { x, y, width, height } = centerPoint
|
|
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
|
|
if (topCell.length === 1) {
|
|
return
|
|
}
|
|
|
|
const topLeftPoint = { x: x - width / 2, y: y - height }
|
|
const topRightPoint = { x: x + width / 2, y: y - height }
|
|
|
|
const leftTopCnt = centerPoints.filter(
|
|
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
|
|
).length
|
|
const rightTopCnt = centerPoints.filter(
|
|
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
|
|
).length
|
|
|
|
if (leftTopCnt + rightTopCnt === 1) {
|
|
exposedHalfTop++
|
|
return
|
|
}
|
|
if (leftTopCnt + rightTopCnt === 0) {
|
|
exposedTop++
|
|
return
|
|
}
|
|
})
|
|
return {
|
|
exposedBottom,
|
|
exposedHalfBottom,
|
|
exposedTop,
|
|
exposedHalfTop,
|
|
touchDimension,
|
|
halfTouchDimension,
|
|
}
|
|
}
|
|
|
|
const coordToTurfPolygon = (points) => {
|
|
const coordinates = points.map((point) => [point.x, point.y])
|
|
coordinates.push(coordinates[0])
|
|
return turf.polygon([coordinates])
|
|
}
|
|
|
|
const polygonToTurfPolygon = (object) => {
|
|
let coordinates
|
|
coordinates = object.points.map((point) => [point.x, point.y])
|
|
coordinates.push(coordinates[0])
|
|
return turf.polygon(
|
|
[coordinates],
|
|
{},
|
|
{
|
|
parentId: object.parentId,
|
|
},
|
|
)
|
|
}
|
|
|
|
const batchObjectGroupToTurfPolygon = (group) => {
|
|
const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon')
|
|
let allPoints = []
|
|
|
|
polygons.forEach((obj) => allPoints.push(...obj.get('points')))
|
|
|
|
const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y])))
|
|
const hull = turf.concave(points, { tolerance: 0.1 })
|
|
|
|
return hull
|
|
}
|
|
|
|
const bottomModuleLine = (nowSurface) => {
|
|
let selectedLine = null
|
|
|
|
const sortedLines = sortLinesByTopLeft(nowSurface.lines)
|
|
|
|
const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
|
|
|
|
// if (nowSurface.flowDirection === 'east') {
|
|
// const leftFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
// )
|
|
// selectedLine = leftFlow
|
|
// } else if (nowSurface.flowDirection === 'west') {
|
|
// const rightFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
// )
|
|
// selectedLine = rightFlow
|
|
// } else if (nowSurface.flowDirection === 'north') {
|
|
// const topFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
// )
|
|
// selectedLine = topFlow
|
|
// } else {
|
|
const bottomFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
)
|
|
selectedLine = bottomFlow
|
|
// }
|
|
|
|
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
|
|
let nextLines = nowSurface.lines[selectedLine.index]
|
|
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
|
|
|
|
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
|
|
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
|
|
|
|
const c1 = prevLines.y1 - m1 * prevLines.x1
|
|
const c2 = nextLines.y1 - m2 * nextLines.x1
|
|
|
|
// Step 2: Calculate intersection point
|
|
|
|
let xIntersectPrev = 0
|
|
let yIntersectPrev = 0
|
|
let xIntersectNext = 0
|
|
let yIntersectNext = 0
|
|
|
|
let endPoint = prevLines.y1 > nextLines.y2 ? prevLines.y1 : nextLines.y2
|
|
let biggerEndPoint = prevLines.y1 > nextLines.y2 ? 'left' : 'right'
|
|
|
|
//bottom일 경우
|
|
xIntersectPrev = (endPoint - c1) / m1
|
|
yIntersectPrev = m1 * xIntersectPrev + c1
|
|
xIntersectNext = (endPoint - c2) / m2
|
|
yIntersectNext = m2 * xIntersectNext + c2
|
|
|
|
let lineCoords
|
|
let polygonCoords
|
|
let ratio = 1
|
|
if (biggerEndPoint === 'left') {
|
|
//왼쪽이 더 밑이면 우측 라인에 절편으로 계산
|
|
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
|
|
polygonCoords = [
|
|
{ x: prevLines.x1, y: yIntersectNext },
|
|
{ x: xIntersectNext, y: yIntersectNext },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
|
|
} else {
|
|
lineCoords = [xIntersectPrev, yIntersectPrev, nextLines.x2, yIntersectPrev]
|
|
polygonCoords = [
|
|
{ x: xIntersectPrev, y: yIntersectPrev },
|
|
{ x: nextLines.x2, y: yIntersectPrev },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(nextLines.x2 - xIntersectPrev)
|
|
}
|
|
|
|
const tempTriangle = new QPolygon(polygonCoords, {
|
|
fill: 'transparent',
|
|
stroke: 'green',
|
|
strokeWidth: 2,
|
|
originY: 'bottom',
|
|
strokeDashArray: [5, 5],
|
|
// fontSize: 15,
|
|
})
|
|
|
|
// canvas.add(tempTriangle)
|
|
|
|
let cloneCoords = []
|
|
tempTriangle.clone((clone) => {
|
|
clone.scale(ratio)
|
|
cloneCoords = clone.getCurrentPoints()
|
|
})
|
|
|
|
//아래쪽에선 잴 작은
|
|
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['y'] > point['y'] ? acc : point))
|
|
|
|
const differenceDistance = overlapCoords.x - vertexPoints.x
|
|
|
|
const newTriangleCoords = cloneCoords.map((point) => {
|
|
return { x: point.x + differenceDistance, y: point.y }
|
|
})
|
|
|
|
const deleteBottomPoint = newTriangleCoords.reduce((acc, point) => (acc['y'] > point['y'] ? acc : point))
|
|
const deleteIndex = newTriangleCoords.indexOf(deleteBottomPoint)
|
|
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
|
|
|
|
const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
|
|
fill: 'transparent',
|
|
stroke: 'red',
|
|
strokeWidth: 2,
|
|
selectable: true,
|
|
fontSize: 14,
|
|
})
|
|
canvas.add(newLine)
|
|
return newLine
|
|
}
|
|
|
|
const topModuleLine = (nowSurface) => {
|
|
let selectedLine = null
|
|
const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
|
|
|
|
const topFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
)
|
|
selectedLine = topFlow
|
|
|
|
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
|
|
let nextLines = nowSurface.lines[selectedLine.index]
|
|
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
|
|
|
|
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
|
|
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
|
|
|
|
const c1 = prevLines.y1 - m1 * prevLines.x1
|
|
const c2 = nextLines.y1 - m2 * nextLines.x1
|
|
|
|
// Step 2: Calculate intersection point
|
|
|
|
let xIntersectPrev = 0
|
|
let yIntersectPrev = 0
|
|
let xIntersectNext = 0
|
|
let yIntersectNext = 0
|
|
|
|
let endPoint = prevLines.y1 > nextLines.y2 ? nextLines.y2 : prevLines.y1
|
|
let biggerEndPoint = prevLines.y1 < nextLines.y2 ? 'left' : 'right'
|
|
|
|
//bottom일 경우
|
|
xIntersectPrev = (endPoint - c1) / m1
|
|
yIntersectPrev = m1 * xIntersectPrev + c1
|
|
xIntersectNext = (endPoint - c2) / m2
|
|
yIntersectNext = m2 * xIntersectNext + c2
|
|
|
|
let lineCoords
|
|
let polygonCoords
|
|
let ratio = 1
|
|
|
|
if (biggerEndPoint === 'left') {
|
|
//왼쪽이 더 밑이면 우측 라인에 절편으로 계산
|
|
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
|
|
polygonCoords = [
|
|
{ x: prevLines.x1, y: yIntersectNext },
|
|
{ x: xIntersectNext, y: yIntersectNext },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
|
|
} else {
|
|
lineCoords = [xIntersectPrev, yIntersectPrev, nextLines.x2, yIntersectPrev]
|
|
polygonCoords = [
|
|
{ x: xIntersectPrev, y: yIntersectPrev },
|
|
{ x: nextLines.x2, y: yIntersectPrev },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(nextLines.x2 - xIntersectPrev)
|
|
}
|
|
|
|
const tempTriangle = new QPolygon(polygonCoords, {
|
|
fill: 'transparent',
|
|
stroke: 'green',
|
|
strokeWidth: 2,
|
|
originY: 'top',
|
|
strokeDashArray: [5, 5],
|
|
// fontSize: 15,
|
|
})
|
|
|
|
// canvas.add(tempTriangle)
|
|
|
|
let cloneCoords = []
|
|
tempTriangle.clone((clone) => {
|
|
clone.scale(ratio)
|
|
cloneCoords = clone.getCurrentPoints()
|
|
})
|
|
|
|
//아래쪽에선 잴 작은
|
|
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['y'] < point['y'] ? acc : point))
|
|
|
|
const differenceDistance = overlapCoords.x - vertexPoints.x
|
|
|
|
const newTriangleCoords = cloneCoords.map((point) => {
|
|
return { x: point.x + differenceDistance, y: point.y }
|
|
})
|
|
|
|
// const newTriangle1 = new QPolygon(newTriangleCoords, {
|
|
// fill: 'transparent',
|
|
// stroke: 'red',
|
|
// strokeWidth: 1,
|
|
// selectable: true,
|
|
// fontSize: 14,
|
|
// })
|
|
// canvas.add(newTriangle1)
|
|
|
|
const deleteBottomPoint = newTriangleCoords.reduce((acc, point) => (acc['y'] < point['y'] ? acc : point))
|
|
const deleteIndex = newTriangleCoords.indexOf(deleteBottomPoint)
|
|
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
|
|
|
|
const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
|
|
fill: 'transparent',
|
|
stroke: 'red',
|
|
strokeWidth: 2,
|
|
selectable: true,
|
|
fontSize: 14,
|
|
})
|
|
canvas.add(newLine)
|
|
return newLine
|
|
}
|
|
|
|
const leftModuleLine = (nowSurface) => {
|
|
let selectedLine = null
|
|
|
|
sortLinesByTopLeft(nowSurface)
|
|
|
|
console.log('nowSurface', nowSurface)
|
|
|
|
const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
|
|
|
|
const leftFlow = nowSurface.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 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
|
)
|
|
selectedLine = leftFlow
|
|
|
|
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
|
|
let nextLines = nowSurface.lines[selectedLine.index]
|
|
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
|
|
|
|
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
|
|
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
|
|
|
|
const c1 = prevLines.y1 - m1 * prevLines.x1
|
|
const c2 = nextLines.y1 - m2 * nextLines.x1
|
|
|
|
// Step 2: Calculate intersection point
|
|
|
|
let xIntersectPrev = 0
|
|
let yIntersectPrev = 0
|
|
let xIntersectNext = 0
|
|
let yIntersectNext = 0
|
|
|
|
let biggerEndPoint = prevLines.x1 > nextLines.x2 ? 'top' : 'bottom'
|
|
console.log('prevLines.x1', prevLines.x1)
|
|
console.log('nextLines.x2', nextLines.x2)
|
|
console.log('biggerEndPoint', biggerEndPoint)
|
|
|
|
//bottom일 경우
|
|
xIntersectPrev = prevLines.x1
|
|
yIntersectPrev = m1 * xIntersectPrev + c1
|
|
xIntersectNext = prevLines.x1
|
|
yIntersectNext = m2 * xIntersectNext + c2
|
|
|
|
let lineCoords
|
|
let polygonCoords
|
|
let ratio = 1
|
|
|
|
if (biggerEndPoint === 'top') {
|
|
//윗쪽이이 더 밑이면 아래 라인에 절편으로 계산
|
|
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
|
|
polygonCoords = [
|
|
{ x: prevLines.x1, y: yIntersectNext },
|
|
{ x: xIntersectNext, y: yIntersectNext },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
|
|
} else {
|
|
lineCoords = [xIntersectPrev, prevLines.y1, xIntersectPrev, yIntersectPrev]
|
|
polygonCoords = [
|
|
{ x: xIntersectNext, y: prevLines.y1 },
|
|
{ x: xIntersectNext, y: yIntersectNext },
|
|
{ x: overlapCoords.x, y: overlapCoords.y },
|
|
]
|
|
ratio = moduleWidthLength / Math.abs(prevLines.y1 - yIntersectNext)
|
|
}
|
|
|
|
const tempTriangle = new QPolygon(polygonCoords, {
|
|
fill: 'transparent',
|
|
stroke: 'green',
|
|
strokeWidth: 2,
|
|
originX: 'left',
|
|
strokeDashArray: [5, 5],
|
|
// fontSize: 15,
|
|
selectable: true,
|
|
})
|
|
|
|
// canvas.add(tempTriangle)
|
|
|
|
let cloneCoords = []
|
|
tempTriangle.clone((clone) => {
|
|
clone.scale(ratio)
|
|
cloneCoords = clone.getCurrentPoints()
|
|
// canvas.add(clone)
|
|
})
|
|
|
|
canvas.remove(tempTriangle)
|
|
|
|
//left에선 가장 왼쪽
|
|
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['x'] < point['x'] ? acc : point))
|
|
const differenceDistance = overlapCoords.y - vertexPoints.y
|
|
|
|
const newTriangleCoords = cloneCoords.map((point) => {
|
|
return { x: point.x, y: point.y + differenceDistance }
|
|
})
|
|
|
|
// const newTriangle1 = new QPolygon(newTriangleCoords, {
|
|
// fill: 'transparent',
|
|
// stroke: 'red',
|
|
// strokeWidth: 1,
|
|
// selectable: true,
|
|
// fontSize: 14,
|
|
// })
|
|
// canvas.add(newTriangle1)
|
|
|
|
const deleteLeftPoint = newTriangleCoords.reduce((acc, point) => (acc['x'] < point['x'] ? acc : point))
|
|
const deleteIndex = newTriangleCoords.indexOf(deleteLeftPoint)
|
|
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
|
|
|
|
const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
|
|
fill: 'transparent',
|
|
stroke: 'red',
|
|
strokeWidth: 2,
|
|
viewLengthText: false,
|
|
// selectable: true,
|
|
fontSize: 14,
|
|
})
|
|
canvas.add(newLine)
|
|
return newLine
|
|
}
|
|
|
|
function sortLinesByTopLeft(surface) {
|
|
// 좌측 상단 기준으로 정렬
|
|
const sortedLines = surface.lines.sort((a, b) => {
|
|
// x1, y1 값을 기준으로 정렬
|
|
if (a.x1 !== b.x1) {
|
|
return a.x1 - b.x1 // x1 기준 정렬
|
|
} else {
|
|
return a.y1 - b.y1 // x1이 같으면 y1 기준 정렬
|
|
}
|
|
})
|
|
|
|
// 정렬된 결과를 기반으로 좌표 재정렬
|
|
sortedLines.forEach((line) => {
|
|
// 좌측 상단이 (0,0) 기준이 되도록 좌표 이동
|
|
const minX = Math.min(line.x1, line.x2)
|
|
const minY = Math.min(line.y1, line.y2)
|
|
|
|
line.set({
|
|
x1: line.x1 - minX,
|
|
y1: line.y1 - minY,
|
|
x2: line.x2 - minX,
|
|
y2: line.y2 - minY,
|
|
})
|
|
})
|
|
|
|
surface.set({
|
|
sortedLines: sortedLines,
|
|
})
|
|
|
|
return sortedLines
|
|
}
|
|
|
|
return {
|
|
makeModuleInstArea,
|
|
manualModuleSetup,
|
|
autoModuleSetup,
|
|
}
|
|
}
|