qcast-front/src/hooks/surface/usePlacementShapeDrawing.js
2026-04-29 18:07:53 +09:00

1142 lines
35 KiB
JavaScript

import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { adsorptionPointModeState, adsorptionRangeState, canvasState, globalPitchState, verticalHorizontalModeState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine'
import { useEffect, useRef } from 'react'
import { fabric } from 'fabric'
import { calculateAngle } from '@/util/qpolygon-utils'
import {
OUTER_LINE_TYPE,
placementShapeDrawingAngle1State,
placementShapeDrawingAngle2State,
placementShapeDrawingArrow1State,
placementShapeDrawingArrow2State,
placementShapeDrawingDiagonalState,
placementShapeDrawingFixState,
placementShapeDrawingLength1State,
placementShapeDrawingLength2State,
placementShapeDrawingPointsState,
placementShapeDrawingTypeState,
} from '@/store/placementShapeDrawingAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup'
import { useSwal } from '@/hooks/useSwal'
import { useMessage } from '@/hooks/useMessage'
import { useSurfaceShapeBatch } from './useSurfaceShapeBatch'
import { roofDisplaySelector } from '@/store/settingAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { useObject } from '@/hooks/useObject'
import { useUndoRedo } from '@/hooks/useUndoRedo'
// 배치면 그리기
export function usePlacementShapeDrawing(id) {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
const {
initEvent,
addCanvasMouseEventListener,
addDocumentEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeMouseLine,
} = useEvent()
// const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
// useContext(EventContext)
const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine()
const { addPolygonByLines, drawDirectionArrow, addLengthText } = usePolygon()
const { setSurfaceShapePattern } = useRoofFn()
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
const { handleSelectableObjects } = useObject()
const { saveSnapshot, popLastSnapshot } = useUndoRedo()
const { swalFire } = useSwal()
const { getMessage } = useMessage()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState)
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const length1Ref = useRef(null)
const length2Ref = useRef(null)
const angle1Ref = useRef(null)
const angle2Ref = useRef(null)
const [length1, setLength1] = useRecoilState(placementShapeDrawingLength1State)
const [length2, setLength2] = useRecoilState(placementShapeDrawingLength2State)
const [arrow1, setArrow1] = useRecoilState(placementShapeDrawingArrow1State)
const [arrow2, setArrow2] = useRecoilState(placementShapeDrawingArrow2State)
const [points, setPoints] = useRecoilState(placementShapeDrawingPointsState)
const [type, setType] = useRecoilState(placementShapeDrawingTypeState)
const [angle1, setAngle1] = useRecoilState(placementShapeDrawingAngle1State)
const [angle2, setAngle2] = useRecoilState(placementShapeDrawingAngle2State)
const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(placementShapeDrawingDiagonalState)
const setOuterLineFix = useSetRecoilState(placementShapeDrawingFixState)
const arrow1Ref = useRef(arrow1)
const arrow2Ref = useRef(arrow2)
const outerLineDiagonalLengthRef = useRef(null)
const isFix = useRef(false)
const { closePopup, addPopup } = usePopup()
const globalPitch = useRecoilValue(globalPitchState)
useEffect(() => {
addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, (e) => {
handleRollback()
})
handleSelectableObjects(['lineGrid', 'tempGrid', 'adsorptionPoint'], false)
clear()
return () => {
initEvent()
handleSelectableObjects(['lineGrid', 'tempGrid', 'adsorptionPoint'], true)
}
}, [verticalHorizontalMode, points, adsorptionRange, adsorptionPointMode])
useEffect(() => {
// 배치면 그리기 시작 시 스냅샷 저장 (undo 하면 그리기 전 상태로 복원)
saveSnapshot()
setPoints([])
setType(OUTER_LINE_TYPE.OUTER_LINE)
return () => {
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
canvas.remove(placementShapeDrawingStartPoint)
// 확정하지 않고 닫은 경우 스냅샷 제거
if (!isFix.current) {
popLastSnapshot()
}
}
}, [])
useEffect(() => {
arrow1Ref.current = arrow1
}, [arrow1])
useEffect(() => {
arrow2Ref.current = arrow2
}, [arrow2])
useEffect(() => {
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type])
const clear = () => {
setLength1(0)
setLength2(0)
setArrow1('')
setArrow2('')
setAngle1(0)
setAngle2(0)
setOuterLineDiagonalLength(0)
}
const mouseDown = (e) => {
let pointer = getIntersectMousePoint(e)
if (points.length === 0) {
setPoints((prev) => [...prev, pointer])
} else {
const lastPoint = points[points.length - 1]
let newPoint = { x: pointer.x, y: pointer.y }
if (verticalHorizontalMode) {
const vector = {
x: pointer.x - lastPoint.x,
y: pointer.y - lastPoint.y,
}
const slope = Math.abs(vector.y / vector.x)
if (slope >= 1) {
// 기울기가 1 이상이면 수직으로 제한
newPoint = {
x: lastPoint.x,
y: pointer.y,
}
} else {
// 기울기가 1 미만이면 수평으로 제한
newPoint = {
x: pointer.x,
y: lastPoint.y,
}
}
}
setPoints((prev) => [...prev, newPoint])
}
}
useEffect(() => {
canvas
?.getObjects()
.filter((obj) => obj.name === 'placementShapeDrawingLine' || obj.name === 'helpGuideLine')
.forEach((obj) => {
removeLine(obj)
})
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint'))
if (points.length === 0) {
setOuterLineFix(true)
removeAllDocumentEventListeners()
return
}
addDocumentEventListener('keydown', document, keydown[type])
if (points.length === 1) {
const point = new fabric.Circle({
radius: 5,
fill: 'transparent',
stroke: 'red',
left: points[0].x - 5,
top: points[0].y - 5,
selectable: false,
name: 'placementShapeDrawingStartPoint',
})
canvas?.add(point)
} else {
setOuterLineFix(false)
canvas
.getObjects()
.filter((obj) => obj.name === 'placementShapeDrawingPoint')
.forEach((obj) => {
canvas.remove(obj)
})
points.forEach((point, idx) => {
const circle = new fabric.Circle({
left: point.x,
top: point.y,
visible: false,
name: 'placementShapeDrawingPoint',
})
canvas.add(circle)
})
points.forEach((point, idx) => {
if (idx === 0) {
return
}
drawLine(points[idx - 1], point, idx)
})
const lastPoint = points[points.length - 1]
const firstPoint = points[0]
if (isFix.current) {
removeAllMouseEventListeners()
removeAllDocumentEventListeners()
const lines = canvas?.getObjects().filter((obj) => obj.name === 'placementShapeDrawingLine')
const roof = addPolygonByLines(lines, {
stroke: 'black',
strokeWidth: 3,
selectable: true,
name: POLYGON_TYPE.ROOF,
originX: 'center',
originY: 'center',
direction: 'south',
pitch: globalPitch,
from: 'surface',
})
// 기존 도형의 흡착점을 이용해 그린 경우, 기존 도형의 planeSize를 상속
inheritPlaneSizeFromExistingShapes(roof)
setSurfaceShapePattern(roof, roofDisplay.column)
drawDirectionArrow(roof)
lines.forEach((line) => {
removeLine(line)
})
setPoints([])
canvas?.renderAll()
// if (+canvasSetting?.roofSizeSet === 3) {
// closePopup(id)
// return
// }
// addPopup(id, 1, <PlacementSurfaceLineProperty id={id} roof={roof} />, false)
changeSurfaceLineType(roof)
closePopup(id)
}
if (points.length < 3) {
return
}
if (Math.abs(lastPoint.x - firstPoint.x) < 1 && Math.abs(lastPoint.y - firstPoint.y) < 1) {
return
}
if (Math.abs(lastPoint.x - firstPoint.x) < 1 || Math.abs(lastPoint.y - firstPoint.y) < 1) {
let isAllRightAngle = true
const firstPoint = points[0]
points.forEach((point, idx) => {
if (idx === 0 || !isAllRightAngle) {
return
}
const angle = calculateAngle(point, firstPoint)
if (angle % 90 !== 0) {
isAllRightAngle = false
}
})
if (isAllRightAngle) {
return
}
const line = addLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
selectable: false,
name: 'helpGuideLine',
})
} else {
const guideLine1 = addLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
const guideLine2 = addLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
}
}
}, [points])
// 기존 도형의 변과 일치하는 경우 planeSize를 상속하는 함수
const inheritPlaneSizeFromExistingShapes = (newPolygon) => {
const tolerance = 2
const existingPolygons = canvas
.getObjects()
.filter((obj) => (obj.name === POLYGON_TYPE.ROOF || obj.name === POLYGON_TYPE.WALL) && obj.id !== newPolygon.id && obj.lines)
if (existingPolygons.length === 0) return
const inheritedSet = new Set()
// 1단계: 기존 도형의 변과 매칭하여 planeSize 상속
newPolygon.lines.forEach((line, lineIdx) => {
const x1 = line.x1,
y1 = line.y1,
x2 = line.x2,
y2 = line.y2
const isHorizontal = Math.abs(y1 - y2) < tolerance
const isVertical = Math.abs(x1 - x2) < tolerance
for (const polygon of existingPolygons) {
for (const edge of polygon.lines) {
if (!edge.attributes?.planeSize) continue
const ex1 = edge.x1,
ey1 = edge.y1,
ex2 = edge.x2,
ey2 = edge.y2
// 1순위: 양 끝점이 정확히 일치
const forwardMatch =
Math.abs(x1 - ex1) < tolerance && Math.abs(y1 - ey1) < tolerance && Math.abs(x2 - ex2) < tolerance && Math.abs(y2 - ey2) < tolerance
const reverseMatch =
Math.abs(x1 - ex2) < tolerance && Math.abs(y1 - ey2) < tolerance && Math.abs(x2 - ex1) < tolerance && Math.abs(y2 - ey1) < tolerance
if (forwardMatch || reverseMatch) {
line.attributes = { ...line.attributes, planeSize: edge.attributes.planeSize }
if (edge.attributes.actualSize) {
line.attributes.actualSize = edge.attributes.actualSize
}
inheritedSet.add(lineIdx)
return
}
// 2순위: 같은 방향 + 같은 좌표 차이 (끝점 공유 불필요)
if (isHorizontal && Math.abs(ey1 - ey2) < tolerance && Math.abs(Math.abs(x2 - x1) - Math.abs(ex2 - ex1)) < tolerance) {
line.attributes = { ...line.attributes, planeSize: edge.attributes.planeSize }
if (edge.attributes.actualSize) {
line.attributes.actualSize = edge.attributes.actualSize
}
inheritedSet.add(lineIdx)
return
}
if (isVertical && Math.abs(ex1 - ex2) < tolerance && Math.abs(Math.abs(y2 - y1) - Math.abs(ey2 - ey1)) < tolerance) {
line.attributes = { ...line.attributes, planeSize: edge.attributes.planeSize }
if (edge.attributes.actualSize) {
line.attributes.actualSize = edge.attributes.actualSize
}
inheritedSet.add(lineIdx)
return
}
}
}
})
// 2단계: 상속받은 변과 평행하고 좌표 차이가 같은 변에 planeSize 전파
if (inheritedSet.size > 0) {
newPolygon.lines.forEach((line, lineIdx) => {
if (inheritedSet.has(lineIdx)) return
const x1 = line.x1,
y1 = line.y1,
x2 = line.x2,
y2 = line.y2
const isHorizontal = Math.abs(y1 - y2) < tolerance
const isVertical = Math.abs(x1 - x2) < tolerance
for (const idx of inheritedSet) {
const inherited = newPolygon.lines[idx]
const ix1 = inherited.x1,
iy1 = inherited.y1,
ix2 = inherited.x2,
iy2 = inherited.y2
const iIsHorizontal = Math.abs(iy1 - iy2) < tolerance
const iIsVertical = Math.abs(ix1 - ix2) < tolerance
if (isHorizontal && iIsHorizontal) {
if (Math.abs(Math.abs(x2 - x1) - Math.abs(ix2 - ix1)) < tolerance) {
line.attributes = { ...line.attributes, planeSize: inherited.attributes.planeSize }
if (inherited.attributes.actualSize) {
line.attributes.actualSize = inherited.attributes.actualSize
}
inheritedSet.add(lineIdx)
return
}
} else if (isVertical && iIsVertical) {
if (Math.abs(Math.abs(y2 - y1) - Math.abs(iy2 - iy1)) < tolerance) {
line.attributes = { ...line.attributes, planeSize: inherited.attributes.planeSize }
if (inherited.attributes.actualSize) {
line.attributes.actualSize = inherited.attributes.actualSize
}
inheritedSet.add(lineIdx)
return
}
}
}
})
}
// planeSize가 상속된 경우 길이 텍스트를 다시 렌더링
if (inheritedSet.size > 0) {
addLengthText(newPolygon)
}
}
// 기존 도형에서 매칭되는 변의 planeSize를 찾는 헬퍼 함수
const findMatchingEdgePlaneSize = (x1, y1, x2, y2) => {
const tolerance = 2
const existingPolygons = canvas.getObjects().filter((obj) => (obj.name === POLYGON_TYPE.ROOF || obj.name === POLYGON_TYPE.WALL) && obj.lines)
const isHorizontal = Math.abs(y1 - y2) < tolerance
const isVertical = Math.abs(x1 - x2) < tolerance
for (const polygon of existingPolygons) {
for (const edge of polygon.lines) {
if (!edge.attributes?.planeSize) continue
const ex1 = edge.x1,
ey1 = edge.y1,
ex2 = edge.x2,
ey2 = edge.y2
// 1순위: 양 끝점 일치 (정방향/역방향)
const forwardMatch =
Math.abs(x1 - ex1) < tolerance && Math.abs(y1 - ey1) < tolerance && Math.abs(x2 - ex2) < tolerance && Math.abs(y2 - ey2) < tolerance
const reverseMatch =
Math.abs(x1 - ex2) < tolerance && Math.abs(y1 - ey2) < tolerance && Math.abs(x2 - ex1) < tolerance && Math.abs(y2 - ey1) < tolerance
if (forwardMatch || reverseMatch) {
return edge.attributes.planeSize
}
// 2순위: 같은 방향 + 같은 좌표 차이 (끝점 공유 불필요)
if (isHorizontal && Math.abs(ey1 - ey2) < tolerance && Math.abs(Math.abs(x2 - x1) - Math.abs(ex2 - ex1)) < tolerance) {
return edge.attributes.planeSize
}
if (isVertical && Math.abs(ex1 - ex2) < tolerance && Math.abs(Math.abs(y2 - y1) - Math.abs(ey2 - ey1)) < tolerance) {
return edge.attributes.planeSize
}
}
}
return null
}
const drawLine = (point1, point2, idx) => {
const line = addLine([point1.x, point1.y, point2.x, point2.y], {
stroke: 'black',
strokeWidth: 3,
idx: idx,
selectable: true,
name: 'placementShapeDrawingLine',
x1: point1.x,
y1: point1.y,
x2: point2.x,
y2: point2.y,
})
// 기존 도형의 변과 일치하는 경우 planeSize 상속
if (line) {
const matchedPlaneSize = findMatchingEdgePlaneSize(point1.x, point1.y, point2.x, point2.y)
if (matchedPlaneSize !== null) {
line.attributes = { ...line.attributes, planeSize: matchedPlaneSize }
}
}
}
// 직각 완료될 경우 확인
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 (points.length === 0) {
return
}
if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') {
return
}
if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }]
})
} else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }]
})
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }]
})
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }]
})
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }]
})
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }]
})
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }]
})
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }]
})
}
}
//이구배 완료될 경우 확인 ↓, ↑, ←, →
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 = angle1Ref.current.value
const angle2Value = angle2Ref.current.value
const length1Value = length1Ref.current.value
const length2Value = length2Ref.current.value
const arrow1Value = arrow1Ref.current
const arrow2Value = arrow2Ref.current
if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
if (arrow1Value === '↓' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length1Value / 10,
y: prev[prev.length - 1].y + length2Value / 10,
},
]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length1Value / 10,
y: prev[prev.length - 1].y + length2Value / 10,
},
]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length1Value / 10,
y: prev[prev.length - 1].y - length2Value / 10,
},
]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length1Value / 10,
y: prev[prev.length - 1].y - length2Value / 10,
},
]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length2Value / 10,
y: prev[prev.length - 1].y + length1Value / 10,
},
]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length2Value / 10,
y: prev[prev.length - 1].y - length1Value / 10,
},
]
})
} else if (arrow1Value === '←' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length2Value / 10,
y: prev[prev.length - 1].y + length1Value / 10,
},
]
})
} else if (arrow1Value === '←' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length2Value / 10,
y: prev[prev.length - 1].y - length1Value / 10,
},
]
})
}
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()
}
if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
if (arrow1Value === '↓' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length2Value / 10,
y: prev[prev.length - 1].y + length1Value / 10,
},
]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length2Value / 10,
y: prev[prev.length - 1].y + length1Value / 10,
},
]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length2Value / 10,
y: prev[prev.length - 1].y - length1Value / 10,
},
]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length2Value / 10,
y: prev[prev.length - 1].y - length1Value / 10,
},
]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length1Value / 10,
y: prev[prev.length - 1].y + length2Value / 10,
},
]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length1Value / 10,
y: prev[prev.length - 1].y - length2Value / 10,
},
]
})
} else if (arrow1Value === '←' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length1Value / 10,
y: prev[prev.length - 1].y + length2Value / 10,
},
]
})
} else if (arrow1Value === '←' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x - length1Value / 10,
y: prev[prev.length - 1].y - length2Value / 10,
},
]
})
}
}
}
const keydown = {
outerLine: (e) => {
if (points.length === 0) {
return
}
enterCheck(e)
// 포커스가 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
}
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
case 'ArrowDown': {
setArrow1('↓')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }]
})
break
}
case 'Up': // IE/Edge에서 사용되는 값
case 'ArrowUp':
setArrow1('↑')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }]
})
break
case 'Left': // IE/Edge에서 사용되는 값
case 'ArrowLeft':
setArrow1('←')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }]
})
break
case 'Right': // IE/Edge에서 사용되는 값
case 'ArrowRight':
setArrow1('→')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }]
})
break
}
},
rightAngle: (e) => {
if (points.length === 0) {
return
}
enterCheck(e)
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
case 'Enter':
break
}
},
doublePitch: (e) => {
if (points.length === 0) {
return
}
enterCheck(e)
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 (points.length === 0) {
return
}
// enterCheck(e)
const key = e.key
switch (key) {
case 'Enter': {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
const lastPoint = prev[prev.length - 1]
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)
return [...prev, { x, y }]
})
}
}
},
diagonalLine: (e) => {
if (points.length === 0) {
return
}
enterCheck(e)
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 handleRollback = () => {
//points의 마지막 요소를 제거
setPoints((prev) => prev.slice(0, prev.length - 1))
}
const handleFix = () => {
if (points.length < 3) {
return
}
let isAllRightAngle = true
const firstPoint = points[0]
/*points.forEach((point, idx) => {
if (idx === 0 || !isAllRightAngle) {
return
}
const angle = calculateAngle(point, firstPoint)
if (angle % 90 !== 0) {
isAllRightAngle = false
}
})
if (isAllRightAngle) {
// alert('부정확한 다각형입니다.')
return
}*/
setPoints((prev) => {
return [...prev, { x: prev[0].x, y: prev[0].y }]
})
isFix.current = true
}
const enterCheck = (e) => {
if (e.key === 'Enter') {
swalFire({
type: 'confirm',
text: getMessage('modal.cover.outline.fix.confirm'),
confirmFn: handleFix,
})
}
}
return {
points,
setPoints,
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,
}
}