909 lines
30 KiB
JavaScript
909 lines
30 KiB
JavaScript
import { useEffect, useRef, useState } from 'react'
|
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
import { adsorptionRangeState, canvasState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
|
import { useEvent } from '@/hooks/useEvent'
|
|
import { useMouse } from '@/hooks/useMouse'
|
|
import { useLine } from '@/hooks/useLine'
|
|
import { useTempGrid } from '@/hooks/useTempGrid'
|
|
import {
|
|
outerLineAngle1State,
|
|
outerLineAngle2State,
|
|
outerLineArrow1State,
|
|
outerLineArrow2State,
|
|
outerLineDiagonalState,
|
|
outerLineLength1State,
|
|
outerLineLength2State,
|
|
outerLineTypeState,
|
|
} from '@/store/outerLineAtom'
|
|
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
|
|
import { fabric } from 'fabric'
|
|
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
|
import { useSwal } from '@/hooks/useSwal'
|
|
import { usePopup } from '@/hooks/usePopup'
|
|
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
|
|
import { POLYGON_TYPE } from '@/common/common'
|
|
|
|
// 보조선 작성
|
|
export function useAuxiliaryDrawing(id) {
|
|
const canvas = useRecoilValue(canvasState)
|
|
const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
|
|
const { getIntersectMousePoint } = useMouse()
|
|
const { addLine, removeLine } = useLine()
|
|
const { tempGridMode } = useTempGrid()
|
|
const { swalFire } = useSwal()
|
|
const { getAdsorptionPoints } = useAdsorptionPoint()
|
|
const { closePopup } = usePopup()
|
|
|
|
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
|
|
|
const [buttonAct, setButtonAct] = useState(1)
|
|
|
|
const mousePointerArr = useRef([])
|
|
const roofAdsorptionPoints = useRef([])
|
|
const lineHistory = useRef([])
|
|
|
|
const length1Ref = useRef(0)
|
|
const length2Ref = useRef(0)
|
|
const angle1Ref = useRef('')
|
|
const angle2Ref = useRef('')
|
|
const [length1, setLength1] = useRecoilState(outerLineLength1State)
|
|
const [length2, setLength2] = useRecoilState(outerLineLength2State)
|
|
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
|
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
|
const [type, setType] = useRecoilState(outerLineTypeState)
|
|
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
|
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
|
|
const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState)
|
|
const arrow1Ref = useRef(arrow1)
|
|
const arrow2Ref = useRef(arrow2)
|
|
|
|
const typeRef = useRef(type)
|
|
|
|
const outerLineDiagonalLengthRef = useRef(0)
|
|
const intersectionPoints = useRef([])
|
|
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
|
|
|
useEffect(() => {
|
|
arrow1Ref.current = arrow1
|
|
}, [arrow1])
|
|
|
|
useEffect(() => {
|
|
arrow2Ref.current = arrow2
|
|
}, [arrow2])
|
|
|
|
useEffect(() => {
|
|
typeRef.current = type
|
|
clear()
|
|
addDocumentEventListener('keydown', document, keydown[type])
|
|
}, [type])
|
|
|
|
useEffect(() => {
|
|
// innerLines가 있을경우 삭제
|
|
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
if (roofs.length === 0) {
|
|
swalFire({ text: '지붕형상이 없습니다.' })
|
|
closePopup(id)
|
|
return
|
|
}
|
|
|
|
// 지붕의 각 꼭지점을 흡착점으로 설정
|
|
const roofsPoints = roofs.map((roof) => roof.points).flat()
|
|
roofAdsorptionPoints.current = [...roofsPoints]
|
|
|
|
addCanvasMouseEventListener('mouse:move', mouseMove)
|
|
addCanvasMouseEventListener('mouse:down', mouseDown)
|
|
addDocumentEventListener('contextmenu', document, cutAuxiliary)
|
|
addDocumentEventListener('keydown', document, keydown[type])
|
|
|
|
return () => {
|
|
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint'))
|
|
initEvent()
|
|
canvas.renderAll()
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
addCanvasMouseEventListener('mouse:down', mouseDown)
|
|
}, [verticalHorizontalMode])
|
|
|
|
const clear = () => {
|
|
addCanvasMouseEventListener('mouse:move', mouseMove)
|
|
setLength1(0)
|
|
setLength2(0)
|
|
|
|
setArrow1('')
|
|
setArrow2('')
|
|
|
|
setAngle1(0)
|
|
setAngle2(0)
|
|
|
|
setOuterLineDiagonalLength(0)
|
|
}
|
|
|
|
const move = (object, x, y) => {
|
|
const line = copy(object, x, y)
|
|
canvas.remove(object)
|
|
canvas.setActiveObject(line)
|
|
}
|
|
|
|
const copy = (object, x, y) => {
|
|
return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
selectable: true,
|
|
name: 'auxiliaryLine',
|
|
})
|
|
}
|
|
|
|
const addBisectorLine = (target) => {
|
|
const slope = (target.y2 - target.y1) / (target.x2 - target.x1)
|
|
const bisectorSlope = -1 / slope
|
|
const length = target.length
|
|
const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope)
|
|
const dy = bisectorSlope * dx
|
|
const endX = (target.x1 + target.x2) / 2
|
|
const endY = (target.y1 + target.y2) / 2
|
|
|
|
addLine([dx, dy, endX, endY], {
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
selectable: true,
|
|
name: 'auxiliaryLine',
|
|
})
|
|
}
|
|
|
|
const keydown = {
|
|
outerLine: (e) => {
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
|
|
const activeElem = document.activeElement
|
|
if (activeElem !== length1Ref.current) {
|
|
length1Ref?.current?.focus()
|
|
}
|
|
|
|
const key = e.key
|
|
|
|
if (!length1Ref.current) {
|
|
return
|
|
}
|
|
|
|
const lengthNum = Number(length1Ref.current.value) / 10
|
|
if (lengthNum === 0) {
|
|
return
|
|
}
|
|
const lastPoint = mousePointerArr.current[0]
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
setArrow1('↓')
|
|
mousePointerArr.current.push({ x: lastPoint.x, y: lastPoint.y + lengthNum })
|
|
drawLine()
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
setArrow1('↑')
|
|
mousePointerArr.current.push({ x: lastPoint.x, y: lastPoint.y - lengthNum })
|
|
drawLine()
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
setArrow1('←')
|
|
mousePointerArr.current.push({ x: lastPoint.x - lengthNum, y: lastPoint.y })
|
|
drawLine()
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
setArrow1('→')
|
|
mousePointerArr.current.push({ x: lastPoint.x + lengthNum, y: lastPoint.y })
|
|
drawLine()
|
|
break
|
|
}
|
|
},
|
|
rightAngle: (e) => {
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
const key = e.key
|
|
|
|
const activeElem = document.activeElement
|
|
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
|
|
length1Ref?.current?.focus()
|
|
}
|
|
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
checkRightAngle('↓')
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
checkRightAngle('↑')
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
checkRightAngle('←')
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
checkRightAngle('→')
|
|
break
|
|
}
|
|
},
|
|
doublePitch: (e) => {
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
const key = e.key
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
checkDoublePitch('↓')
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
checkDoublePitch('↑')
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
checkDoublePitch('←')
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
checkDoublePitch('→')
|
|
break
|
|
}
|
|
},
|
|
angle: (e) => {
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
const key = e.key
|
|
switch (key) {
|
|
case 'Enter': {
|
|
const lastPoint = mousePointerArr.current[0]
|
|
const length = length1Ref.current.value / 10
|
|
const angle = angle1Ref.current.value
|
|
//lastPoint로부터 angle1만큼의 각도로 length1만큼의 길이를 가지는 선을 그림
|
|
const radian = (angle * Math.PI) / 180
|
|
|
|
const x = lastPoint.x + length * Math.cos(radian)
|
|
const y = lastPoint.y - length * Math.sin(radian)
|
|
mousePointerArr.current.push({ x, y })
|
|
drawLine()
|
|
}
|
|
}
|
|
},
|
|
diagonalLine: (e) => {
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
|
|
const key = e.key
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
checkDiagonal('↓')
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
checkDiagonal('↑')
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
checkDiagonal('←')
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
checkDiagonal('→')
|
|
break
|
|
}
|
|
},
|
|
}
|
|
|
|
// 직각 완료될 경우 확인
|
|
const checkRightAngle = (direction) => {
|
|
const activeElem = document.activeElement
|
|
|
|
const canDirection =
|
|
direction === '↓' || direction === '↑'
|
|
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
|
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
|
|
|
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
|
|
setArrow1(direction)
|
|
arrow1Ref.current = direction
|
|
length2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
|
if (!canDirection) {
|
|
return
|
|
}
|
|
setArrow2(direction)
|
|
arrow2Ref.current = direction
|
|
}
|
|
|
|
const length1Num = Number(length1Ref.current.value) / 10
|
|
const length2Num = Number(length2Ref.current.value) / 10
|
|
|
|
if (mousePointerArr.current.length === 0) {
|
|
return
|
|
}
|
|
|
|
const lastPoint = mousePointerArr.current[0]
|
|
|
|
if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') {
|
|
return
|
|
}
|
|
|
|
if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Num, y: lastPoint.y + length1Num })
|
|
} else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Num, y: lastPoint.y + length1Num })
|
|
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Num, y: lastPoint.y - length1Num })
|
|
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Num, y: lastPoint.y - length1Num })
|
|
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Num, y: lastPoint.y + length2Num })
|
|
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Num, y: lastPoint.y - length2Num })
|
|
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length1Num, y: lastPoint.y + length2Num })
|
|
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length1Num, y: lastPoint.y - length2Num })
|
|
}
|
|
drawLine()
|
|
}
|
|
|
|
//이구배 완료될 경우 확인 ↓, ↑, ←, →
|
|
const checkDoublePitch = (direction) => {
|
|
const activeElem = document.activeElement
|
|
|
|
const canDirection =
|
|
direction === '↓' || direction === '↑'
|
|
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
|
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
|
|
|
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
|
|
setArrow1(direction)
|
|
arrow1Ref.current = direction
|
|
angle2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
|
if (!canDirection) {
|
|
return
|
|
}
|
|
setArrow2(direction)
|
|
arrow2Ref.current = direction
|
|
}
|
|
|
|
const angle1Value = Number(angle1Ref.current.value)
|
|
const angle2Value = Number(angle2Ref.current.value)
|
|
const length1Value = Number(length1Ref.current.value)
|
|
const length2Value = Number(length2Ref.current.value)
|
|
|
|
const arrow1Value = arrow1Ref.current
|
|
const arrow2Value = arrow2Ref.current
|
|
const lastPoint = mousePointerArr.current[0]
|
|
|
|
if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
|
|
if (arrow1Value === '↓' && arrow2Value === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
|
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
|
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
|
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
|
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Value / 10, y: lastPoint.y + length1Value / 10 })
|
|
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Value / 10, y: lastPoint.y - length1Value / 10 })
|
|
} else if (arrow1Value === '←' && arrow2Value === '↓') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Value / 10, y: lastPoint.y + length1Value / 10 })
|
|
} else if (arrow1Value === '←' && arrow2Value === '↑') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Value / 10, y: lastPoint.y - length1Value / 10 })
|
|
}
|
|
drawLine()
|
|
|
|
angle1Ref.current.focus()
|
|
}
|
|
}
|
|
|
|
//대각선 완료될 경우 확인
|
|
const checkDiagonal = (direction) => {
|
|
const activeElem = document.activeElement
|
|
const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이
|
|
|
|
const length1Value = length1Ref.current.value
|
|
|
|
if (diagonalLength <= length1Value) {
|
|
alert('대각선 길이는 직선 길이보다 길어야 합니다.')
|
|
return
|
|
}
|
|
|
|
const canDirection =
|
|
direction === '↓' || direction === '↑'
|
|
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
|
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
|
|
|
if (activeElem === length1Ref.current) {
|
|
setArrow1(direction)
|
|
arrow1Ref.current = direction
|
|
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
|
if (!canDirection) {
|
|
return
|
|
}
|
|
setArrow2(direction)
|
|
arrow2Ref.current = direction
|
|
}
|
|
|
|
const arrow1Value = arrow1Ref.current
|
|
const arrow2Value = arrow2Ref.current
|
|
|
|
const getLength2 = () => {
|
|
return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2))
|
|
}
|
|
|
|
const length2Value = getLength2()
|
|
|
|
if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') {
|
|
setLength2(getLength2())
|
|
length2Ref.current.focus()
|
|
}
|
|
|
|
const lastPoint = mousePointerArr.current[0]
|
|
|
|
if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
|
|
if (arrow1Value === '↓' && arrow2Value === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Value / 10, y: lastPoint.y + length1Value / 10 })
|
|
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Value / 10, y: lastPoint.y + length1Value / 10 })
|
|
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length2Value / 10, y: lastPoint.y - length1Value / 10 })
|
|
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
|
mousePointerArr.current.push({ x: lastPoint.x - length2Value / 10, y: lastPoint.y - length1Value / 10 })
|
|
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
|
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
|
}
|
|
drawLine()
|
|
}
|
|
}
|
|
|
|
const drawLine = () => {
|
|
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint'))
|
|
const line = addLine([mousePointerArr.current[0].x, mousePointerArr.current[0].y, mousePointerArr.current[1].x, mousePointerArr.current[1].y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
})
|
|
|
|
const historyLines = [...lineHistory.current]
|
|
|
|
const hasSameLine = historyLines.some((history) => {
|
|
return (
|
|
(isSamePoint(history.startPoint, line.startPoint) && isSamePoint(history.endPoint, line.endPoint)) ||
|
|
(isSamePoint(history.startPoint, line.endPoint) && isSamePoint(history.endPoint, line.startPoint))
|
|
)
|
|
})
|
|
|
|
mousePointerArr.current = []
|
|
clear()
|
|
|
|
if (hasSameLine) {
|
|
canvas.remove(line)
|
|
return
|
|
}
|
|
|
|
lineHistory.current.push(line)
|
|
}
|
|
|
|
const mouseDown = (e) => {
|
|
canvas.renderAll()
|
|
let pointer = getIntersectMousePoint(e)
|
|
|
|
if (mousePointerArr.current.length === 1) {
|
|
const currentPoint = canvas.getPointer(e.e)
|
|
const prevPoint = mousePointerArr.current[0]
|
|
|
|
const degreeByTwoPoints = calculateAngle(prevPoint, currentPoint)
|
|
|
|
const degree = Math.round(degreeByTwoPoints / 45) * 45
|
|
|
|
if (verticalHorizontalMode) {
|
|
pointer = {
|
|
x: prevPoint.x + distanceBetweenPoints(currentPoint, prevPoint) * Math.cos((degree * Math.PI) / 180),
|
|
y: prevPoint.y + distanceBetweenPoints(currentPoint, prevPoint) * Math.sin((degree * Math.PI) / 180),
|
|
}
|
|
}
|
|
|
|
mousePointerArr.current.push(pointer)
|
|
|
|
drawLine()
|
|
} else {
|
|
const circle = new fabric.Circle({
|
|
radius: 3,
|
|
fill: 'red',
|
|
left: pointer.x - 3,
|
|
top: pointer.y - 3,
|
|
x: pointer.x,
|
|
y: pointer.y,
|
|
name: 'innerPoint',
|
|
selectable: true,
|
|
})
|
|
canvas.add(circle)
|
|
canvas.renderAll()
|
|
mousePointerArr.current.push(pointer)
|
|
}
|
|
}
|
|
|
|
const mouseMove = (e) => {
|
|
removeMouseLine()
|
|
// 가로선
|
|
const pointer = canvas.getPointer(e.e)
|
|
const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed)
|
|
|
|
const otherAdsorptionPoints = []
|
|
|
|
auxiliaryLines.forEach((line1) => {
|
|
auxiliaryLines.forEach((line2) => {
|
|
if (line1 === line2) {
|
|
return
|
|
}
|
|
|
|
const intersectionPoint = calculateIntersection(line1, line2)
|
|
if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) {
|
|
return
|
|
}
|
|
otherAdsorptionPoints.push(intersectionPoint)
|
|
})
|
|
})
|
|
|
|
const adsorptionPoints = [...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, ...intersectionPoints.current]
|
|
|
|
let arrivalPoint = { x: pointer.x, y: pointer.y }
|
|
|
|
// pointer와 adsorptionPoints의 거리를 계산하여 가장 가까운 점을 찾는다.
|
|
let adsorptionPoint = findClosestPoint(pointer, adsorptionPoints)
|
|
|
|
if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) <= adsorptionRange) {
|
|
arrivalPoint = { ...adsorptionPoint }
|
|
}
|
|
|
|
const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], {
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'mouseLine',
|
|
})
|
|
|
|
// 세로선
|
|
const verticalLine = new fabric.Line([arrivalPoint.x, -1 * canvas.height, arrivalPoint.x, 2 * canvas.height], {
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'mouseLine',
|
|
})
|
|
|
|
// 선들을 캔버스에 추가합니다.
|
|
canvas?.add(horizontalLine, verticalLine)
|
|
|
|
// 캔버스를 다시 그립니다.
|
|
canvas?.renderAll()
|
|
}
|
|
|
|
// 보조선 절삭
|
|
const cutAuxiliary = (e) => {
|
|
const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine')
|
|
|
|
if (auxiliaryLines.length === 0) {
|
|
return
|
|
}
|
|
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
/*const allLines = [...auxiliaryLines]
|
|
|
|
roofBases.forEach((roofBase) => {
|
|
allLines.push(...roofBase.lines)
|
|
})*/
|
|
|
|
const innerLines = [...auxiliaryLines]
|
|
const roofLines = []
|
|
|
|
roofBases.forEach((roofBase) => {
|
|
innerLines.push(...roofBase.innerLines)
|
|
roofLines.push(...roofBase.lines)
|
|
})
|
|
|
|
auxiliaryLines.forEach((line1) => {
|
|
let interSectionPointsWithRoofLines = []
|
|
|
|
//지붕선과 만나는 점을 찾는다.
|
|
roofLines.forEach((line2) => {
|
|
const intersectionPoint = calculateIntersection(line1, line2)
|
|
if (!intersectionPoint) {
|
|
return
|
|
}
|
|
// 기존 점과 겹치는지 확인
|
|
if (interSectionPointsWithRoofLines.some((point) => isSamePoint(point, intersectionPoint))) {
|
|
return
|
|
}
|
|
|
|
interSectionPointsWithRoofLines.push(intersectionPoint)
|
|
})
|
|
|
|
//지붕선과 만나는 점이 두개일 경우 넘친 보조선을 잘라준다 (케라바로 만든 마루)
|
|
if (interSectionPointsWithRoofLines.length === 2) {
|
|
const newLine = addLine(
|
|
[
|
|
interSectionPointsWithRoofLines[0].x,
|
|
interSectionPointsWithRoofLines[0].y,
|
|
interSectionPointsWithRoofLines[1].x,
|
|
interSectionPointsWithRoofLines[1].y,
|
|
],
|
|
{
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
},
|
|
)
|
|
lineHistory.current.push(newLine)
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
removeLine(line1)
|
|
intersectionPoints.current.push(...interSectionPointsWithRoofLines)
|
|
return
|
|
} else if (interSectionPointsWithRoofLines.length === 1) {
|
|
//지붕선과 만나는 점이 하나일 경우
|
|
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, interSectionPointsWithRoofLines[0])
|
|
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, interSectionPointsWithRoofLines[0])
|
|
|
|
if (!(distance1 === 0 || distance2 === 0)) {
|
|
if (distance1 >= distance2) {
|
|
const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
})
|
|
lineHistory.current.push(newLine)
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
removeLine(line1)
|
|
} else {
|
|
const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
})
|
|
lineHistory.current.push(newLine)
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
removeLine(line1)
|
|
}
|
|
intersectionPoints.current.push(interSectionPointsWithRoofLines[0])
|
|
}
|
|
}
|
|
|
|
//보조선과 만나는 점을 찾는다.
|
|
innerLines.forEach((line2) => {
|
|
if (line1 === line2) {
|
|
return
|
|
}
|
|
const intersectionPoint = calculateIntersection(line1, line2)
|
|
if (!intersectionPoint) {
|
|
return
|
|
}
|
|
roofAdsorptionPoints.current.push(intersectionPoint)
|
|
intersectionPoints.current.push(intersectionPoint)
|
|
|
|
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
|
|
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
|
|
|
|
if (distance1 === 0 || distance2 === 0) {
|
|
return
|
|
}
|
|
//historyLine에서 기존 line을 제거한다.
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
|
|
let newLine
|
|
|
|
if (distance1 >= distance2) {
|
|
newLine = addLine([line1.x1, line1.y1, intersectionPoint.x, intersectionPoint.y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
intersectionPoint,
|
|
})
|
|
} else {
|
|
newLine = addLine([line1.x2, line1.y2, intersectionPoint.x, intersectionPoint.y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
intersectionPoint,
|
|
})
|
|
}
|
|
lineHistory.current.push(newLine)
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
removeLine(line1)
|
|
})
|
|
|
|
/*auxiliaryLines.forEach((line1) => {
|
|
allLines.forEach((line2) => {
|
|
if (line1 === line2) {
|
|
return
|
|
}
|
|
const intersectionPoint = calculateIntersection(line1, line2)
|
|
if (!intersectionPoint) {
|
|
return
|
|
}
|
|
roofAdsorptionPoints.current.push(intersectionPoint)
|
|
intersectionPoints.current.push(intersectionPoint)
|
|
|
|
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
|
|
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
|
|
|
|
if (distance1 === 0 || distance2 === 0) {
|
|
return
|
|
}
|
|
//historyLine에서 기존 line을 제거한다.
|
|
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
|
|
|
let newLine
|
|
|
|
if (distance1 >= distance2) {
|
|
newLine = addLine([line1.x1, line1.y1, intersectionPoint.x, intersectionPoint.y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
intersectionPoint,
|
|
})
|
|
} else {
|
|
newLine = addLine([line1.x2, line1.y2, intersectionPoint.x, intersectionPoint.y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'auxiliaryLine',
|
|
isFixed: true,
|
|
intersectionPoint,
|
|
})
|
|
}
|
|
lineHistory.current.push(newLine)
|
|
removeLine(line1)
|
|
})
|
|
})*/
|
|
})
|
|
addCanvasMouseEventListener('mouse:move', mouseMove)
|
|
}
|
|
|
|
/**
|
|
* 일변전으로 돌아가기
|
|
*/
|
|
const handleRollback = () => {
|
|
const innerPoint = canvas.getObjects().find((obj) => obj.name === 'innerPoint')
|
|
if (innerPoint) {
|
|
mousePointerArr.current = []
|
|
canvas.remove(innerPoint)
|
|
canvas.renderAll()
|
|
return
|
|
}
|
|
|
|
const lastLine = lineHistory.current.pop()
|
|
if (lastLine) {
|
|
roofAdsorptionPoints.current = roofAdsorptionPoints.current.filter(
|
|
(point) => point.x !== lastLine.intersectionPoint?.x && point.y !== lastLine.intersectionPoint?.y,
|
|
)
|
|
|
|
canvas.remove(lastLine)
|
|
canvas.renderAll()
|
|
}
|
|
|
|
addCanvasMouseEventListener('mouse:move', mouseMove)
|
|
}
|
|
|
|
const handleFix = () => {
|
|
if (!confirm('지붕선 완료하시겠습니까?')) {
|
|
return
|
|
}
|
|
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
|
|
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
|
|
// 겹치는 선 하나는 canvas에서 제거한다.
|
|
|
|
const tempLines = [...lineHistory.current]
|
|
lineHistory.current = []
|
|
tempLines.forEach((line) => {
|
|
if (
|
|
lineHistory.current.some(
|
|
(history) =>
|
|
JSON.stringify(history.startPoint) === JSON.stringify(line.startPoint) &&
|
|
JSON.stringify(history.endPoint) === JSON.stringify(line.endPoint),
|
|
)
|
|
) {
|
|
canvas.remove(line)
|
|
return
|
|
}
|
|
|
|
lineHistory.current.push(line)
|
|
})
|
|
|
|
const innerLines = lineHistory.current
|
|
|
|
roofBases.forEach((roofBase) => {
|
|
const tempPolygonPoints = [...roofBase.points].map((obj) => {
|
|
return { x: Math.round(obj.x), y: Math.round(obj.y) }
|
|
})
|
|
const roofInnerLines = innerLines.filter((line) => {
|
|
const inPolygon1 =
|
|
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
|
|
roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
|
|
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
|
|
const inPolygon2 =
|
|
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
|
|
roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
|
|
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
|
|
|
if (inPolygon1 && inPolygon2) {
|
|
line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() }
|
|
return true
|
|
}
|
|
})
|
|
|
|
roofBase.innerLines = [...roofInnerLines]
|
|
|
|
canvas.renderAll()
|
|
})
|
|
|
|
closePopup(id)
|
|
}
|
|
|
|
return {
|
|
length1,
|
|
setLength1,
|
|
length2,
|
|
setLength2,
|
|
length1Ref,
|
|
length2Ref,
|
|
arrow1,
|
|
setArrow1,
|
|
arrow2,
|
|
setArrow2,
|
|
arrow1Ref,
|
|
arrow2Ref,
|
|
angle1,
|
|
setAngle1,
|
|
angle1Ref,
|
|
angle2,
|
|
setAngle2,
|
|
angle2Ref,
|
|
outerLineDiagonalLength,
|
|
setOuterLineDiagonalLength,
|
|
outerLineDiagonalLengthRef,
|
|
type,
|
|
setType,
|
|
handleFix,
|
|
handleRollback,
|
|
buttonAct,
|
|
setButtonAct,
|
|
move,
|
|
copy,
|
|
addBisectorLine,
|
|
}
|
|
}
|